--- /dev/null
+#!/usr/bin/env python
+import sys
+import feedparser
+
+import getopt
+import os
+import re
+import time
+
+DEFAULT_TEMPLATE = """\
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=<tpl:charset filter="xmlq"/>" />
+ <title><tpl:feed value="title" filter="xml" /></title>
+ </head>
+ <body>
+ <div id="container">
+ <h1><a href="<tpl:feed value="link" filter="xmlq"/>"><tpl:feed value="title" filter="xml"/></a></h1>
+ <div id="content">
+ <tpl:entries>
+ <div class="entry">
+ <h3><a href="<tpl:entry value="link" filter="xmlq"/>"><tpl:entry value="title" filter="xml" /></a></h3>
+ <div class="entrybody"><tpl:entry value="summary" /></div>
+ <p class="posted">Posted by <tpl:entry value="author" filter="xml" /> on <tpl:entry value="modified" /></p>
+ </div>
+ </tpl:entries>
+ </div>
+ </div>
+ </body>
+</html>
+"""
+
+class Template:
+ """Generic template library. Initially implemented in bloglines2html.py"""
+
+ re_parse = re.compile(r'<tpl:(\S+)\b(.*?)(?:/>|>(.*?)</tpl:\1>)',
+ re.U | re.S)
+ re_parse_attr = re.compile(r'([a-z]+)\s*=\s*"([^"]*)"', re.U|re.I)
+
+ # Default Date/Time format
+ datetime_format = '%Y-%m-%d %H:%M:%S'
+
+ def __init__(self):
+ self.var = {}
+
+ def format_datetime(self, data, format):
+ return time.strftime(format, data)
+
+ def get_charset(self):
+ return 'utf-8'
+
+ def parse(self, text):
+ return self.re_parse.sub(self.process_tag, text)
+
+ def parse_attr(self, text):
+ return dict(self.re_parse_attr.findall(text.strip()))
+
+ def process_tag(self, match):
+ tag = match.group(1)
+ attr = self.parse_attr(match.group(2))
+ data = match.group(3) or u''
+
+ try:
+ handler = getattr(self, 'tag_%s' % tag)
+ except AttributeError:
+ return ''
+ else:
+ result = handler(attr, data)
+ if result is None:
+ result = ''
+ elif (isinstance(result, tuple) or
+ isinstance(result, time.struct_time)) and len(result) == 9:
+ result = self.format_datetime(result,
+ attr.get('format', self.datetime_format))
+
+ if 'filter' in attr:
+ result = do_filters(result, attr['filter'])
+
+ if isinstance(result, unicode):
+ result = result.encode(self.get_charset())
+ else:
+ result = str(result)
+
+ return result
+
+ def tag_charset(self, attr, data):
+ return self.get_charset()
+
+ def tag_var(self, attr, data):
+ # Handle <tpl:var name="foo" />
+ val = self.var.get(attr.get('name'), attr.get('default', ''))
+ if isinstance(val, unicode):
+ val = val.encode(self.get_charset())
+ return val
+
+
+class RSSTemplate(Template):
+ """Template with tag handlers for feed data."""
+
+ re_range = re.compile(r'([\-\d]+)?(:([\-\d]+)?(:([\-\d]+)?)?)?')
+
+ def __init__(self, feed):
+ Template.__init__(self)
+ self.feed = feed
+ self.content = None
+ self.contributors = None
+ self.enclosures = None
+ self.entries = None
+
+ def get_charset(self):
+ return self.feed.encoding
+
+ def get_loop(self, obj, objattr, attr, data):
+ def range_value(val):
+ if val is None:
+ return val
+ else:
+ return int(val)
+
+ if obj:
+ items = obj.get(objattr)
+ if items:
+ result = []
+ if 'select' in attr:
+ match = self.re_range.match(attr['select'])
+ if match:
+ # Only a single item is selected.
+ match = [range_value(x)
+ for x in list(match.group(1, 3, 5))]
+ items = items[slice(*match)]
+
+ for item in items:
+ setattr(self, objattr, item)
+ result.append(self.parse(data))
+
+ setattr(self, objattr, None)
+ return ''.join(result)
+
+ return ''
+
+ def get_value(self, obj, attr, data):
+ if (obj is not None) and ('value' in attr):
+ key = attr['value']
+ detail = attr.get('detail')
+ value = None
+
+ # Check whether it is requesting a datetime field.
+ if (key+'_parsed') in obj:
+ return obj.get(key+'_parsed')
+
+ # Check whether it is trying to get details.
+ if detail:
+ obj2 = attr.get('%s_detail' % key)
+ if obj2:
+ value = obj2.get(detail)
+ else:
+ value = obj.get(key)
+
+ if isinstance(value, basestring):
+ return value
+
+ return ''
+
+ def tag_content(self, attr, data):
+ return self.get_value(self.content, attr, data)
+
+ def tag_contents(self, attr, data):
+ return self.get_loop(self.entries, 'content', attr, data)
+
+ def tag_contributor(self, attr, data):
+ return self.get_value(self.contributors, attr, data)
+
+ def tag_contributors(self, attr, data):
+ return self.get_loop(self.entries, 'contributors', attr, data)
+
+ def tag_enclosure(self, attr, data):
+ return self.get_value(self.enclosures, attr, data)
+
+ def tag_enclosures(self, attr, data):
+ return self.get_loop(self.entries, 'enclosures', attr, data)
+
+ def tag_entries(self, attr, data):
+ return self.get_loop(self.feed, 'entries', attr, data)
+
+ def tag_entry(self, attr, data):
+ return self.get_value(self.entries, attr, data)
+
+ def tag_feed(self, attr, data):
+ return self.get_value(self.feed.feed, attr, data)
+
+ def tag_if_not(self, attr, data):
+ # Handle <tpl:if_not_tag tag="foo"> ... </tpl:if_not_tag>
+ try:
+ handler = getattr(self, 'tag_%s' % attr['tag'])
+ except (AttributeError, KeyError):
+ pass
+ else:
+ val = handler(attr, data)
+ match = attr.get('match')
+
+ if ((not match) and (not val)) or (match and (val != match)):
+ return self.parse(data)
+
+ return self.parse(data)
+
+ def tag_if(self, attr, data):
+ # Handle <tpl:if_tag tag="foo"> ... </tpl:if_tag>
+ try:
+ handler = getattr(self, 'tag_%s' % attr['tag'])
+ except (AttributeError, KeyError):
+ pass
+ else:
+ val = handler(attr, data)
+ match = attr.get('match')
+
+ if ((not match) and val) or (match and (val == match)):
+ return self.parse(data)
+
+ return ''
+
+ def tag_now(self, attr, data):
+ # Handle <tpl:now format="..." />
+ return time.localtime()
+
+
+def do_filters(data, filters):
+ for f in filters.split(','):
+ f = f.strip()
+
+ # Try to find the filter using the global name space.
+ try:
+ f = globals()['filter_%s' % f]
+ except KeyError:
+ pass
+ else:
+ data = f(data)
+
+ return data
+
+
+def filter_basename(data):
+ """Return the basename of the filename."""
+ idx = data.rfind('/')
+ if idx < 0:
+ idx = data.rfind('\\')
+ if idx < 0:
+ return data
+
+ return data[idx+1:]
+
+
+def filter_reducespace(data):
+ return re.sub(r'[\t\r\n\s]+', ' ', data).strip()
+
+
+def filter_xml(data):
+ """Escape the XML entities."""
+ data = data.replace('&', '&')
+ data = data.replace('<', '<')
+ data = data.replace('>', '>')
+ return data
+
+
+def filter_xmlq(data):
+ """Escape the XML entities + quote, useful for XML attributes."""
+ data = filter_xml(data)
+ data = data.replace('"', '"')
+ return data
+
+
+def rss2html(inp):
+ # Parse the feed data and supply that to RSSTemplate object
+ template = RSSTemplate(inp)
+
+ result = template.parse(DEFAULT_TEMPLATE)
+
+ return result
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <title>Nieuw in de voorverkoop Paradiso</title>
+ </head>
+ <body>
+ <div id="container">
+ <h1><a href="http://www.paradiso.nl/web/show/id=178182">Nieuw in de voorverkoop Paradiso</a></h1>
+ <div id="content">
+
+ <div class="entry">
+ <h3><a href="http://www.paradiso.nl/web/Agenda-Item/Dondergrondse-Fuck-Forever-band-night.htm">donderdag 26 juni 2014 23:30 - Dondergrondse: Fuck Forever band night</a></h3>
+ <div class="entrybody"></div>
+ <p class="posted">Posted by on </p>
+ </div>
+
+ <div class="entry">
+ <h3><a href="http://www.paradiso.nl/web/Agenda-Item/Dondergrondse-18.htm">donderdag 19 juni 2014 23:30 - Dondergrondse</a></h3>
+ <div class="entrybody"></div>
+ <p class="posted">Posted by on </p>
+ </div>
+
+ <div class="entry">
+ <h3><a href="http://www.paradiso.nl/web/Agenda-Item/Kina-Grannis-Locatie-Bitterzoet.htm">zaterdag 7 juni 2014 20:00 - Kina Grannis - Locatie: Bitterzoet</a></h3>
+ <div class="entrybody"></div>
+ <p class="posted">Posted by on </p>
+ </div>
+
+ <div class="entry">
+ <h3><a href="http://www.paradiso.nl/web/Agenda-Item/Jay-Brannan.htm">vrijdag 3 oktober 2014 19:30 - Jay Brannan</a></h3>
+ <div class="entrybody"></div>
+ <p class="posted">Posted by on </p>
+ </div>
+
+ <div class="entry">
+ <h3><a href="http://www.paradiso.nl/web/Agenda-Item/Dondergrondse-hosted-by-The-Daily-Indie-Live-The-Futures-Dust.htm">donderdag 12 juni 2014 23:30 - Dondergrondse: hosted by The Daily Indie – Live: The Future’s Dust</a></h3>
+ <div class="entrybody"></div>
+ <p class="posted">Posted by on </p>
+ </div>
+
+ <div class="entry">
+ <h3><a href="http://www.paradiso.nl/web/Agenda-Item/Blitz-Kids.htm">woensdag 23 juli 2014 20:00 - Blitz Kids</a></h3>
+ <div class="entrybody"></div>
+ <p class="posted">Posted by on </p>
+ </div>
+
+ <div class="entry">
+ <h3><a href="http://www.paradiso.nl/web/Agenda-Item/Noodlanding-301.htm">donderdag 26 juni 2014 23:30 - Noodlanding!</a></h3>
+ <div class="entrybody">Dansnacht, alternatieve hits</div>
+ <p class="posted">Posted by on </p>
+ </div>
+
+ <div class="entry">
+ <h3><a href="http://www.paradiso.nl/web/Agenda-Item/Noodlanding-300.htm">donderdag 19 juni 2014 23:30 - Noodlanding!</a></h3>
+ <div class="entrybody">Dansnacht, alternatieve hits</div>
+ <p class="posted">Posted by on </p>
+ </div>
+
+ <div class="entry">
+ <h3><a href="http://www.paradiso.nl/web/Agenda-Item/Natural-Child-3VOOR12-presents-Locatie-Tolhuistuin-zaal.htm">maandag 15 september 2014 19:30 - Natural Child – 3VOOR12 presents - Locatie: Tolhuistuin (zaal)</a></h3>
+ <div class="entrybody">aftrap officiële openingsweek van nieuwe zaal Tolhuistuin</div>
+ <p class="posted">Posted by on </p>
+ </div>
+
+ <div class="entry">
+ <h3><a href="http://www.paradiso.nl/web/Agenda-Item/Ben-Miller-Band.htm">zaterdag 5 juli 2014 22:00 - Ben Miller Band</a></h3>
+ <div class="entrybody"></div>
+ <p class="posted">Posted by on </p>
+ </div>
+
+ <div class="entry">
+ <h3><a href="http://www.paradiso.nl/web/Agenda-Item/Zoot-Woman-ADE-Locatie-Tolhuistuin-zaal.htm">donderdag 16 oktober 2014 20:30 - Zoot Woman – ADE - Locatie: Tolhuistuin (zaal)</a></h3>
+ <div class="entrybody"></div>
+ <p class="posted">Posted by on </p>
+ </div>
+
+ <div class="entry">
+ <h3><a href="http://www.paradiso.nl/web/Agenda-Item/Little-Barrie-Locatie-Bitterzoet.htm">woensdag 25 juni 2014 21:00 - Little Barrie - Locatie: Bitterzoet</a></h3>
+ <div class="entrybody"></div>
+ <p class="posted">Posted by on </p>
+ </div>
+
+ <div class="entry">
+ <h3><a href="http://www.paradiso.nl/web/Agenda-Item/Earth-Wind-Fire-Experience-feat.-Al-McKay.htm">woensdag 3 september 2014 19:30 - Earth Wind & Fire Experience feat. Al McKay</a></h3>
+ <div class="entrybody"></div>
+ <p class="posted">Posted by on </p>
+ </div>
+
+ <div class="entry">
+ <h3><a href="http://www.paradiso.nl/web/Agenda-Item/Direct-2.htm">zondag 19 oktober 2014 19:00 - Di-rect</a></h3>
+ <div class="entrybody"></div>
+ <p class="posted">Posted by on </p>
+ </div>
+
+ <div class="entry">
+ <h3><a href="http://www.paradiso.nl/web/Agenda-Item/Royal-Wood.htm">vrijdag 10 oktober 2014 19:30 - Royal Wood</a></h3>
+ <div class="entrybody"></div>
+ <p class="posted">Posted by on </p>
+ </div>
+
+ <div class="entry">
+ <h3><a href="http://www.paradiso.nl/web/Agenda-Item/Dark-Alley-Cabaret.htm">zondag 1 juni 2014 20:00 - Dark Alley Cabaret</a></h3>
+ <div class="entrybody">Amsterdam Burlesque Weekend</div>
+ <p class="posted">Posted by on </p>
+ </div>
+
+ <div class="entry">
+ <h3><a href="http://www.paradiso.nl/web/Agenda-Item/The-News-Reality-Opera-van-Jacob-TV-door-de-Nederlandse-Reisopera.htm">maandag 2 juni 2014 20:30 - The News – Reality Opera van Jacob TV door de Nederlandse Reisopera</a></h3>
+ <div class="entrybody"></div>
+ <p class="posted">Posted by on </p>
+ </div>
+
+ <div class="entry">
+ <h3><a href="http://www.paradiso.nl/web/Agenda-Item/Wij-zijn-18-filmpremiere-Palio-Superspeed-Donkey-afterparty.htm">zaterdag 24 mei 2014 21:00 - ‘Wij zijn 18’: filmpremiere, Palio Superspeed Donkey & afterparty</a></h3>
+ <div class="entrybody"></div>
+ <p class="posted">Posted by on </p>
+ </div>
+
+ <div class="entry">
+ <h3><a href="http://www.paradiso.nl/web/Agenda-Item/Wiz-Khalifa.htm">maandag 30 juni 2014 20:30 - Wiz Khalifa</a></h3>
+ <div class="entrybody"></div>
+ <p class="posted">Posted by on </p>
+ </div>
+
+ <div class="entry">
+ <h3><a href="http://www.paradiso.nl/web/Agenda-Item/Dave-Hause-Locatie-Bitterzoet.htm">woensdag 20 augustus 2014 21:00 - Dave Hause - Locatie: Bitterzoet</a></h3>
+ <div class="entrybody"></div>
+ <p class="posted">Posted by on </p>
+ </div>
+
+ </div>
+ <div id="footer">Generated on 2014-05-05 13:23:47 by <a href="http://scott.yang.id.au/project/feed2html">Feed2HTML 1.0</a></div>
+ </div>
+ </body>
+</html>