Compare commits
10 Commits
961a31141f
...
7c3091d64c
Author | SHA1 | Date |
---|---|---|
pictuga | 7c3091d64c | |
pictuga | 37b4e144a9 | |
pictuga | bd4b7b5bb2 | |
pictuga | 68d920d4b5 | |
pictuga | 758ff404a8 | |
pictuga | 463530f02c | |
pictuga | ec0a28a91d | |
pictuga | 421acb439d | |
pictuga | 42c5d09ccb | |
pictuga | 056de12484 |
124
morss/morss.py
124
morss/morss.py
|
@ -18,6 +18,7 @@ from . import readabilite
|
|||
|
||||
import wsgiref.simple_server
|
||||
import wsgiref.handlers
|
||||
import cgitb
|
||||
|
||||
|
||||
try:
|
||||
|
@ -44,7 +45,7 @@ THREADS = 10 # number of threads (1 for single-threaded)
|
|||
DEBUG = False
|
||||
PORT = 8080
|
||||
|
||||
PROTOCOL = ['http', 'https', 'ftp']
|
||||
PROTOCOL = ['http', 'https']
|
||||
|
||||
|
||||
def filterOptions(options):
|
||||
|
@ -66,6 +67,7 @@ def log(txt, force=False):
|
|||
if DEBUG or force:
|
||||
if 'REQUEST_URI' in os.environ:
|
||||
open('morss.log', 'a').write("%s\n" % repr(txt))
|
||||
|
||||
else:
|
||||
print(repr(txt))
|
||||
|
||||
|
@ -73,6 +75,7 @@ def log(txt, force=False):
|
|||
def len_html(txt):
|
||||
if len(txt):
|
||||
return len(lxml.html.fromstring(txt).text_content())
|
||||
|
||||
else:
|
||||
return 0
|
||||
|
||||
|
@ -80,6 +83,7 @@ def len_html(txt):
|
|||
def count_words(txt):
|
||||
if len(txt):
|
||||
return len(lxml.html.fromstring(txt).text_content().split())
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
|
@ -88,12 +92,14 @@ class Options:
|
|||
if len(args):
|
||||
self.options = args
|
||||
self.options.update(options or {})
|
||||
|
||||
else:
|
||||
self.options = options or {}
|
||||
|
||||
def __getattr__(self, key):
|
||||
if key in self.options:
|
||||
return self.options[key]
|
||||
|
||||
else:
|
||||
return False
|
||||
|
||||
|
@ -107,17 +113,23 @@ class Options:
|
|||
def parseOptions(options):
|
||||
""" Turns ['md=True'] into {'md':True} """
|
||||
out = {}
|
||||
|
||||
for option in options:
|
||||
split = option.split('=', 1)
|
||||
|
||||
if len(split) > 1:
|
||||
if split[0].lower() == 'true':
|
||||
out[split[0]] = True
|
||||
|
||||
elif split[0].lower() == 'false':
|
||||
out[split[0]] = False
|
||||
|
||||
else:
|
||||
out[split[0]] = split[1]
|
||||
|
||||
else:
|
||||
out[split[0]] = True
|
||||
|
||||
return out
|
||||
|
||||
|
||||
|
@ -208,6 +220,7 @@ def ItemFill(item, options, feedurl='/', fast=False):
|
|||
if len(match):
|
||||
link = match[0]
|
||||
log(link)
|
||||
|
||||
else:
|
||||
link = None
|
||||
|
||||
|
@ -217,6 +230,7 @@ def ItemFill(item, options, feedurl='/', fast=False):
|
|||
if len(match) and urlparse(match[0]).netloc != 'www.facebook.com':
|
||||
link = match[0]
|
||||
log(link)
|
||||
|
||||
else:
|
||||
link = None
|
||||
|
||||
|
@ -299,6 +313,7 @@ def UrlFix(url):
|
|||
|
||||
return url
|
||||
|
||||
|
||||
def FeedFetch(url, options):
|
||||
# allow for code execution for feedify
|
||||
pre = feedify.pre_worker(url)
|
||||
|
@ -327,7 +342,6 @@ def FeedFetch(url, options):
|
|||
if options.items:
|
||||
# using custom rules
|
||||
rss = feeds.FeedHTML(xml)
|
||||
feed.rule
|
||||
|
||||
rss.rules['items'] = options.items
|
||||
|
||||
|
@ -340,6 +354,8 @@ def FeedFetch(url, options):
|
|||
if options.item_time:
|
||||
rss.rules['item_time'] = options.item_time
|
||||
|
||||
rss = rss.convert(feeds.FeedXML)
|
||||
|
||||
else:
|
||||
try:
|
||||
rss = feeds.parse(xml, url, contenttype)
|
||||
|
@ -377,6 +393,7 @@ def FeedGather(rss, url, options):
|
|||
value = queue.get()
|
||||
try:
|
||||
worker(*value)
|
||||
|
||||
except Exception as e:
|
||||
log('Thread Error: %s' % e.message)
|
||||
queue.task_done()
|
||||
|
@ -416,6 +433,7 @@ def FeedGather(rss, url, options):
|
|||
for i, item in enumerate(list(rss.items)):
|
||||
if threads == 1:
|
||||
worker(*[i, item])
|
||||
|
||||
else:
|
||||
queue.put([i, item])
|
||||
|
||||
|
@ -435,37 +453,38 @@ def FeedGather(rss, url, options):
|
|||
return rss
|
||||
|
||||
|
||||
def FeedFormat(rss, options):
|
||||
def FeedFormat(rss, options, encoding='utf-8'):
|
||||
if options.callback:
|
||||
if re.match(r'^[a-zA-Z0-9\.]+$', options.callback) is not None:
|
||||
return '%s(%s)' % (options.callback, rss.tojson())
|
||||
out = '%s(%s)' % (options.callback, rss.tojson(encoding='unicode'))
|
||||
return out if encoding == 'unicode' else out.encode(encoding)
|
||||
|
||||
else:
|
||||
raise MorssException('Invalid callback var name')
|
||||
|
||||
elif options.json:
|
||||
if options.indent:
|
||||
return rss.tojson(encoding='UTF-8', indent=4)
|
||||
return rss.tojson(encoding=encoding, indent=4)
|
||||
|
||||
else:
|
||||
return rss.tojson(encoding='UTF-8')
|
||||
return rss.tojson(encoding=encoding)
|
||||
|
||||
elif options.csv:
|
||||
return rss.tocsv(encoding='UTF-8')
|
||||
return rss.tocsv(encoding=encoding)
|
||||
|
||||
elif options.reader:
|
||||
if options.indent:
|
||||
return rss.tohtml(encoding='UTF-8', pretty_print=True)
|
||||
return rss.tohtml(encoding=encoding, pretty_print=True)
|
||||
|
||||
else:
|
||||
return rss.tohtml(encoding='UTF-8')
|
||||
return rss.tohtml(encoding=encoding)
|
||||
|
||||
else:
|
||||
if options.indent:
|
||||
return rss.torss(xml_declaration=True, encoding='UTF-8', pretty_print=True)
|
||||
return rss.torss(xml_declaration=True, encoding=encoding, pretty_print=True)
|
||||
|
||||
else:
|
||||
return rss.torss(xml_declaration=True, encoding='UTF-8')
|
||||
return rss.torss(xml_declaration=True, encoding=encoding)
|
||||
|
||||
|
||||
def process(url, cache=None, options=None):
|
||||
|
@ -499,7 +518,7 @@ def cgi_app(environ, start_response):
|
|||
if url.startswith(':'):
|
||||
split = url.split('/', 1)
|
||||
|
||||
options = split[0].replace('|', '/').replace('\\\'', '\'').split(':')[1:]
|
||||
raw_options = split[0].replace('|', '/').replace('\\\'', '\'').split(':')[1:]
|
||||
|
||||
if len(split) > 1:
|
||||
url = split[1]
|
||||
|
@ -507,10 +526,10 @@ def cgi_app(environ, start_response):
|
|||
url = ''
|
||||
|
||||
else:
|
||||
options = []
|
||||
raw_options = []
|
||||
|
||||
# init
|
||||
options = Options(filterOptions(parseOptions(options)))
|
||||
options = Options(filterOptions(parseOptions(raw_options)))
|
||||
headers = {}
|
||||
|
||||
global DEBUG
|
||||
|
@ -551,18 +570,42 @@ def cgi_app(environ, start_response):
|
|||
rss = FeedGather(rss, url, options)
|
||||
out = FeedFormat(rss, options)
|
||||
|
||||
if not options.silent:
|
||||
return out
|
||||
if options.silent:
|
||||
return ['']
|
||||
|
||||
else:
|
||||
return [out]
|
||||
|
||||
|
||||
def cgi_wrapper(environ, start_response):
|
||||
# simple http server for html and css
|
||||
def middleware(func):
|
||||
" Decorator to turn a function into a wsgi middleware "
|
||||
# This is called when parsing the code
|
||||
|
||||
def app_builder(app):
|
||||
# This is called when doing app = cgi_wrapper(app)
|
||||
|
||||
def app_wrap(environ, start_response):
|
||||
# This is called when a http request is being processed
|
||||
|
||||
return func(environ, start_response, app)
|
||||
|
||||
return app_wrap
|
||||
|
||||
return app_builder
|
||||
|
||||
|
||||
@middleware
|
||||
def cgi_file_handler(environ, start_response, app):
|
||||
" Simple HTTP server to serve static files (.html, .css, etc.) "
|
||||
|
||||
files = {
|
||||
'': 'text/html',
|
||||
'index.html': 'text/html'}
|
||||
'index.html': 'text/html',
|
||||
'sheet.xsl': 'text/xsl'}
|
||||
|
||||
if 'REQUEST_URI' in environ:
|
||||
url = environ['REQUEST_URI'][1:]
|
||||
|
||||
else:
|
||||
url = environ['PATH_INFO'][1:]
|
||||
|
||||
|
@ -591,16 +634,29 @@ def cgi_wrapper(environ, start_response):
|
|||
start_response(headers['status'], list(headers.items()))
|
||||
return ['Error %s' % headers['status']]
|
||||
|
||||
# actual morss use
|
||||
else:
|
||||
return app(environ, start_response)
|
||||
|
||||
|
||||
@middleware
|
||||
def cgi_error_handler(environ, start_response, app):
|
||||
try:
|
||||
return [cgi_app(environ, start_response) or '(empty)']
|
||||
return app(environ, start_response)
|
||||
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
raise
|
||||
|
||||
except Exception as e:
|
||||
headers = {'status': '500 Oops', 'content-type': 'text/plain'}
|
||||
headers = {'status': '500 Oops', 'content-type': 'text/html'}
|
||||
start_response(headers['status'], list(headers.items()), sys.exc_info())
|
||||
log('ERROR <%s>: %s' % (url, e.message), force=True)
|
||||
return ['An error happened:\n%s' % e.message]
|
||||
log('ERROR: %s' % repr(e), force=True)
|
||||
return [cgitb.html(sys.exc_info())]
|
||||
|
||||
|
||||
@middleware
|
||||
def cgi_encode(environ, start_response, app):
|
||||
out = app(environ, start_response)
|
||||
return [x if isinstance(x, bytes) else x.encode('utf-8') for x in out]
|
||||
|
||||
|
||||
def cli_app():
|
||||
|
@ -627,6 +683,7 @@ def isInt(string):
|
|||
try:
|
||||
int(string)
|
||||
return True
|
||||
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
|
@ -634,7 +691,12 @@ def isInt(string):
|
|||
def main():
|
||||
if 'REQUEST_URI' in os.environ:
|
||||
# mod_cgi
|
||||
wsgiref.handlers.CGIHandler().run(cgi_wrapper)
|
||||
|
||||
app = cgi_app
|
||||
app = cgi_error_handler(app)
|
||||
app = cgi_encode(app)
|
||||
|
||||
wsgiref.handlers.CGIHandler().run(app)
|
||||
|
||||
elif len(sys.argv) <= 1 or isInt(sys.argv[1]) or '--root' in sys.argv[1:]:
|
||||
# start internal (basic) http server
|
||||
|
@ -643,22 +705,30 @@ def main():
|
|||
argPort = int(sys.argv[1])
|
||||
if argPort > 0:
|
||||
port = argPort
|
||||
|
||||
else:
|
||||
raise MorssException('Port must be positive integer')
|
||||
|
||||
else:
|
||||
port = PORT
|
||||
|
||||
print('Serving http://localhost:%s/'%port)
|
||||
httpd = wsgiref.simple_server.make_server('', port, cgi_wrapper)
|
||||
app = cgi_app
|
||||
app = cgi_file_handler(app)
|
||||
app = cgi_error_handler(app)
|
||||
app = cgi_encode(app)
|
||||
|
||||
print('Serving http://localhost:%s/' % port)
|
||||
httpd = wsgiref.simple_server.make_server('', port, app)
|
||||
httpd.serve_forever()
|
||||
|
||||
else:
|
||||
# as a CLI app
|
||||
try:
|
||||
cli_app()
|
||||
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
raise
|
||||
|
||||
except Exception as e:
|
||||
print('ERROR: %s' % e.message)
|
||||
|
||||
|
|
Loading…
Reference in New Issue