add simplerss
authorMoritz Venn <ritzmo@users.schwerkraft.elitedvb.net>
Mon, 13 Aug 2007 20:16:19 +0000 (20:16 +0000)
committerMoritz Venn <ritzmo@users.schwerkraft.elitedvb.net>
Mon, 13 Aug 2007 20:16:19 +0000 (20:16 +0000)
Makefile.am
configure.ac
simplerss/CONTROL/control [new file with mode: 0644]
simplerss/Makefile.am [new file with mode: 0755]
simplerss/src/Makefile.am [new file with mode: 0755]
simplerss/src/__init__.py [new file with mode: 0644]
simplerss/src/httpclient.py [new file with mode: 0644]
simplerss/src/plugin.py [new file with mode: 0644]

index 170ed34..3aeabe1 100644 (file)
@@ -1,2 +1,2 @@
 AUTOMAKE_OPTIONS = gnu
-SUBDIRS = antiscrollbar movietagger webinterface wirelesslan netcaster lastfm logomanager vlcplayer
+SUBDIRS = antiscrollbar movietagger webinterface wirelesslan netcaster lastfm logomanager vlcplayer simplerss
index 87e1fcc..decd3ea 100644 (file)
@@ -54,4 +54,7 @@ logomanager/logodir_hdd/Makefile
 vlcplayer/Makefile
 vlcplayer/src/Makefile
 
+simplerss/Makefile
+simplerss/src/Makefile
+
 ])
diff --git a/simplerss/CONTROL/control b/simplerss/CONTROL/control
new file mode 100644 (file)
index 0000000..4ce7232
--- /dev/null
@@ -0,0 +1,9 @@
+Package: enigma2-plugin-extensions-simplerss
+Version: 0.5-20070813-r0
+Description: rss viewer for the enigma2 gui
+Architecture: mipsel
+Section: extra
+Priority: optional
+Maintainer: Moritz Venn <moritz.venn@freaque.net>
+Depends: enigma2(>1.0cvs20060516)
+Source: http://schwerkraft.elitedvb.net/scm/?group_id=11
diff --git a/simplerss/Makefile.am b/simplerss/Makefile.am
new file mode 100755 (executable)
index 0000000..308a09c
--- /dev/null
@@ -0,0 +1 @@
+SUBDIRS = src\r
diff --git a/simplerss/src/Makefile.am b/simplerss/src/Makefile.am
new file mode 100755 (executable)
index 0000000..395446a
--- /dev/null
@@ -0,0 +1,3 @@
+installdir = /usr/lib/enigma2/python/Plugins/Extensions/SimpleRSS\r
+\r
+install_PYTHON = *.py\r
diff --git a/simplerss/src/__init__.py b/simplerss/src/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/simplerss/src/httpclient.py b/simplerss/src/httpclient.py
new file mode 100644 (file)
index 0000000..22e7fbf
--- /dev/null
@@ -0,0 +1,237 @@
+from twisted.internet.protocol import ClientFactory
+from twisted.web2.client.http import HTTPClientProtocol
+from twisted.internet import reactor, error
+from urlparse import urlsplit
+from socket import gethostbyname
+from urllib import urlencode as urllib_urlencode, quote_plus as urllib_quote_plus
+
+global HTTPCLIENT_requestCount
+HTTPCLIENT_requestCount = 0 # counts requests
+    
+class Enigma2HTTPRequest:
+        
+    def __init__(self,hostname,path,port,method="GET",headerfields={}):
+        self.hostname=hostname
+        self.port=port
+        self.path=path
+        self.method=method
+        self.headerfields = headerfields
+        self.onRequestFinished = []
+        self.onRequestError = []
+        self.onHeaderLoaded = []
+        
+        self.data=""
+        self.readsize = 0
+        self.headers= {}
+        
+    def _DataRecived(self,data):
+        self.readsize += len(data)
+        self.data += data
+            
+    def getIPAdress(self):
+        """    
+            socket.gethostbyname() is syncron
+            Enigma2 is blocked while process is running    
+        """
+        try:
+            return gethostbyname(self.hostname)
+        except:
+            return False
+        
+    def HeaderLoaded(self,headers):
+        self.headers = headers
+        for i in self.onHeaderLoaded:
+            if i is not None:
+                i(headers)
+        self.onHeaderLoaded=[]
+        
+    def RequestError(self,error):
+        for i in self.onRequestError:
+            if i is not None:
+                i(error)
+        self.onRequestError = []
+        
+    def RequestFinished(self,data):
+        for i in self.onRequestFinished:
+            if i is not None:
+                i(data)
+       
+class Enigma2URLHTTPRequest(Enigma2HTTPRequest):
+    def __init__(self,url,method="GET",headerfields={}):
+        x= urlsplit(url)
+        if x[1].rfind(":")>0:
+            y = x[1].split(":")
+            hostname = y[0]
+            port = int(y[1])
+        else:
+            hostname = x[1]
+            port = 80
+        path=x[2]
+        Enigma2HTTPRequest.__init__(self,hostname,path,port,method=method,headerfields=headerfields)
+
+class Enigma2FileHTTPRequest(Enigma2URLHTTPRequest):
+    def __init__(self,targetfile,url,method="GET",headerfields={}):
+        Enigma2URLHTTPRequest.__init__(self,url,method=method,headerfields=headerfields)
+        self.filehandle = open(targetfile,"w")
+        self.onRequestFinished.append(self.close)
+        self.onRequestError.append(self.close)
+    def close(self,dummy):
+        self.filehandle.close()
+    
+    def _DataRecived(self,data):
+        self.readsize += len(data)
+        self.filehandle.write(data)
+        
+            
+        
+        
+class Enigma2HTTPProtocol(HTTPClientProtocol):
+    DEBUG = False
+    
+    def __init__(self,request,requestCount):
+        self.request = request
+        self.requestCount = requestCount
+        self.headers={}
+        self.headerread=False
+        self.responseFirstLine = True # to indikate, that first line of responseheader was read
+        HTTPClientProtocol.__init__(self)
+        self.setRawMode()
+        
+    def rawDataReceived(self,line):
+        for l in line.split(self.delimiter): 
+            if self.headerread:
+                self.request._DataRecived(l)
+            else:
+                if self.DEBUG:
+                    print "HTTP "+self.requestCount+" <<==",l
+                if l == "":
+                    self.headerread = True
+                    self.request.HeaderLoaded(self.headers)
+                else:
+                    self.parseHeaderLine(l)
+    
+    def parseHeaderLine(self,line):
+        if self.responseFirstLine is  True:
+            #print "parseHeaderLine",line.split(" ")
+            fields = line.split(" ")
+            protocoll = fields[0]
+            responsecode  = fields[1]
+            statuscode = " ".join(fields[2:])
+            self.headers["protocoll"] = protocoll
+            self.headers["responsecode"] = responsecode
+            self.headers["statuscode"] = statuscode
+            self.responseFirstLine = False
+        elif line.rfind(":"):
+            x = line.split(":")
+            key = x[0].lstrip().rstrip().lower()
+            var = ":".join(x[1:]).lstrip()
+            self.headers[key] = var        
+        else:
+            print "unknown headerline",line
+
+    def connectionMade(self):
+        if self.request.method == "POST":
+            (path,params ) = self.request.path.split("?")
+        elif self.request.method == "GET":
+            path = self.request.path
+        self.sendLine("%s %s HTTP/1.0"%(self.request.method,path))
+        self.sendLine("Host: %s"%self.request.hostname)
+        for i in self.request.headerfields:
+            self.sendLine(i+": "+self.request.headerfields[i])
+        if self.request.method == "POST":
+            self.sendLine("Content-Type: application/x-www-form-urlencoded")
+            self.sendLine("Content-Length: "+str(len(params)))
+        
+        self.sendLine("")
+        if self.request.method == "POST":
+            self.sendLine(params)
+    
+    def sendLine(self,data):
+        if self.DEBUG:
+            print "HTTP "+self.requestCount+" ==>>",data
+        HTTPClientProtocol.sendLine(self,data)
+        
+class Enigma2HTTPClientFactory(ClientFactory):
+
+    initialDelay = 20
+    maxDelay = 500
+    def __init__(self,request):
+        self.hangup_ok = False
+        self.request = request
+       
+    def startedConnecting(self, connector):
+        pass
+    
+    def buildProtocol(self, addr):
+        global HTTPCLIENT_requestCount
+        HTTPCLIENT_requestCount = HTTPCLIENT_requestCount + 1
+        return Enigma2HTTPProtocol(self.request,str(HTTPCLIENT_requestCount))
+
+    def clientConnectionLost(self, connector, reason):
+        if not self.hangup_ok:
+            self.request.RequestFinished(self.request.data)
+        ClientFactory.clientConnectionLost(self, connector, reason)
+        
+    def clientConnectionFailed(self, connector, reason):
+        self.request.RequestError(reason.getErrorMessage())
+        ClientFactory.clientConnectionFailed(self, connector, reason)
+
+def urlencode(dict):
+       return urllib_urlencode(dict)
+       
+def quote_plus(data):
+       return urllib_quote_plus(data)
+
+def getURL(url,callback=None,errorback=None,headercallback=None,method="GET",headers={}):
+    """ 
+        this will is called with a url
+        url = http://www.hostna.me/somewhere/on/the/server <string>
+    """
+    req = Enigma2URLHTTPRequest(url,method=method,headerfields=headers)
+    req.onRequestError.append(errorback)
+    req.onHeaderLoaded.append(headercallback)
+    req.onRequestFinished.append(callback)
+    ipadress = req.getIPAdress()
+    if ipadress is not False:
+        reactor.connectTCP(ipadress,req.port, Enigma2HTTPClientFactory(req))
+        return req
+    else:
+        if errorback is not None:
+            errorback("Error while resolve Hostname")
+
+def getPage(hostname,port,path,method="GET",callback=None,errorback=None,headercallback=None,headers={}):
+    """ 
+        this will is called with separte hostname,port,path
+        hostname = www.hostna.me <string>
+        port = 80 <int>
+        path= /somewhere/on/the/server <string>
+    """
+    req = Enigma2HTTPRequest(hostname,path,port,method=method,headerfields=headers)
+    req.onRequestError.append(errorback)
+    req.onRequestFinished.append(callback)
+    ipadress = req.getIPAdress()
+    if ipadress is not False:
+        reactor.connectTCP(ipadress,req.port, Enigma2HTTPClientFactory(req))
+        return req
+    else:
+        if errorback is not None:
+            errorback("Error while resolve Hostname")
+
+def getFile(filename,url,method="GET",callback=None,errorback=None,headercallback=None,headers={}):
+    """ 
+        this will is called with a url and a target file
+        fimename = /tmp/target.jpg 
+        url = http://www.hostna.me/somewhere/on/the/server.jpg <string>
+    """
+    req = Enigma2FileHTTPRequest(filename,url,method=method,headerfields=headers)
+    req.onRequestError.append(errorback)
+    req.onHeaderLoaded.append(headercallback)
+    req.onRequestFinished.append(callback)
+    ipadress = req.getIPAdress()
+    if ipadress is not False:
+        reactor.connectTCP(ipadress,req.port, Enigma2HTTPClientFactory(req))
+        return req
+    else:
+        if errorback is not None:
+            errorback("Error while resolve Hostname")
+
diff --git a/simplerss/src/plugin.py b/simplerss/src/plugin.py
new file mode 100644 (file)
index 0000000..157fa5a
--- /dev/null
@@ -0,0 +1,841 @@
+# warning, this is work in progress.
+# plus, the error handling sucks.
+#
+# TODO:
+#  - inline todos
+#  - all that stuff I forgot...
+#
+from Screens.Screen import Screen
+from Screens.MessageBox import MessageBox
+from Screens.ChoiceBox import ChoiceBox
+from Components.ActionMap import ActionMap
+from Components.Label import Label
+from Components.ScrollLabel import ScrollLabel
+from Components.GUIComponent import GUIComponent
+from Components.MultiContent import MultiContentEntryText
+from Components.Button import Button
+from Plugins.Plugin import PluginDescriptor
+from enigma import eTimer, eListboxPythonMultiContent, eListbox, gFont, RT_HALIGN_LEFT, RT_WRAP
+
+from httpclient import getPage
+from urlparse import urlsplit
+import xml.dom.minidom
+
+from sets import Set
+
+from Components.config import config, configfile, ConfigSubsection, ConfigSubList, ConfigEnableDisable, ConfigInteger, ConfigText, getConfigListEntry
+from Components.ConfigList import ConfigListScreen
+
+config.plugins.simpleRSS = ConfigSubsection()
+config.plugins.simpleRSS.show_new = ConfigEnableDisable(default=True)
+config.plugins.simpleRSS.interval = ConfigInteger(default=10, limits=(5, 300))
+config.plugins.simpleRSS.feedcount = ConfigInteger(default=0)
+config.plugins.simpleRSS.feed = ConfigSubList()
+for i in range(0, config.plugins.simpleRSS.feedcount.value):
+       config.plugins.simpleRSS.feed.append(ConfigSubsection())
+       config.plugins.simpleRSS.feed[i].uri = ConfigText(default="http://", fixed_size = False)
+       config.plugins.simpleRSS.feed[i].autoupdate = ConfigEnableDisable(default=True)
+
+class SimpleRSSEdit(ConfigListScreen, Screen):
+       skin = """
+               <screen name="SimpleRSSEdit" position="100,100" size="550,120" title="Simple RSS Reader Setup" >
+                       <widget name="config" position="20,10" size="510,75" scrollbarMode="showOnDemand" />
+                       <ePixmap name="red"    position="0,75"   zPosition="4" size="140,40" pixmap="key_red-fs8.png" transparent="1" alphatest="on" />
+                       <ePixmap name="green"  position="140,75" zPosition="4" size="140,40" pixmap="key_green-fs8.png" transparent="1" alphatest="on" />
+                       <widget name="key_red" position="0,75" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
+                       <widget name="key_green" position="140,75" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
+               </screen>"""
+
+       def __init__(self, session, id):
+               Screen.__init__(self, session)
+
+               self.list = [ getConfigListEntry(_("Autoupdate: "), config.plugins.simpleRSS.feed[id].autoupdate), getConfigListEntry(_("Feed URI: "), config.plugins.simpleRSS.feed[id].uri) ]
+
+               ConfigListScreen.__init__(self, self.list, session)
+
+               self["key_red"] = Button(_("Cancel"))
+               self["key_green"] = Button(_("OK"))
+
+               self["setupActions"] = ActionMap(["SetupActions"],
+               {
+                       "save": self.save,
+                       "cancel": self.keyCancel
+               }, -1)
+
+               self.id = id
+
+       def save(self):
+               config.plugins.simpleRSS.feed[self.id].save()
+               config.plugins.simpleRSS.feed.save()
+               self.close()
+
+class SimpleRSS(ConfigListScreen, Screen):
+       skin = """
+               <screen name="SimpleRSS" position="100,100" size="550,400" title="Simple RSS Reader Setup" >
+                       <widget name="config"  position="20,10" size="510,350" scrollbarMode="showOnDemand" />
+                       <ePixmap name="red"    position="0,360"   zPosition="4" size="140,40" pixmap="key_red-fs8.png" transparent="1" alphatest="on" />
+                       <ePixmap name="green"  position="140,360" zPosition="4" size="140,40" pixmap="key_green-fs8.png" transparent="1" alphatest="on" />
+                       <ePixmap name="yellow" position="280,360" zPosition="4" size="140,40" pixmap="key_yellow-fs8.png" transparent="1" alphatest="on" />
+                       <ePixmap name="blue"   position="420,360" zPosition="4" size="140,40" pixmap="key_blue-fs8.png" transparent="1" alphatest="on" />
+                       <widget name="key_red"    position="0,360" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
+                       <widget name="key_green"  position="140,360" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
+                       <widget name="key_yellow" position="280,360" zPosition="5" size="140,40" valign="center" halign="center"  font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
+                       <widget name="key_blue"   position="420,360" zPosition="5" size="140,40" valign="center" halign="center"  font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
+               </screen>"""
+
+       def __init__(self, session, args = None):
+               Screen.__init__(self, session)
+
+               self.onClose.append(self.abort)
+
+               # nun erzeugen wir eine liste von elementen fuer die menu liste.
+               self.list = [ ]
+               for i in range(0, config.plugins.simpleRSS.feedcount.value):
+                       self.list.append(getConfigListEntry(_("Feed: "), config.plugins.simpleRSS.feed[i].uri))
+
+               self.list.append(getConfigListEntry(_("Show new Messages: "), config.plugins.simpleRSS.show_new))
+               self.list.append(getConfigListEntry(_("Update Interval (min): "), config.plugins.simpleRSS.interval))
+
+               # die liste selbst
+               ConfigListScreen.__init__(self, self.list, session)
+
+               self["key_red"] = Button(_("Cancel"))
+               self["key_green"] = Button(_("OK"))
+               self["key_yellow"] = Button(_("New"))
+               self["key_blue"] = Button(_("Delete"))
+
+               self["setupActions"] = ActionMap(["SetupActions", "ColorActions"],
+               {
+                       "blue": self.delete,
+                       "yellow": self.new,
+                       "save": self.keySave,
+                       "cancel": self.keyCancel,
+                       "ok": self.ok
+               }, -1)
+       
+       def delete(self):
+               self.session.openWithCallback(self.deleteConfirm, MessageBox, "Really delete this entry?\nIt cannot be recovered!")
+
+       def deleteConfirm(self, result):
+               if result:
+                       id = self["config"].instance.getCurrentIndex()
+                       del config.plugins.simpleRSS.feed[id]
+                       config.plugins.simpleRSS.feedcount.value -= 1
+                       self.list.pop(id)
+                       # redraw list
+                       self["config"].setList(self.list)
+
+       def ok(self):
+               id = self["config"].instance.getCurrentIndex()
+               self.session.openWithCallback(self.refresh, SimpleRSSEdit, id)
+
+       def refresh(self):
+               pass
+
+       def new(self):
+               id = len(config.plugins.simpleRSS.feed)
+               config.plugins.simpleRSS.feed.append(ConfigSubsection())
+               config.plugins.simpleRSS.feed[id].uri = ConfigText(default="http://", fixed_size = False)
+               config.plugins.simpleRSS.feed[id].autoupdate = ConfigEnableDisable(default=True)
+               self.session.openWithCallback(self.conditionalNew, SimpleRSSEdit, id)
+
+       def conditionalNew(self):
+               id = len(config.plugins.simpleRSS.feed)-1
+               # Check if new feed differs from default
+               if config.plugins.simpleRSS.feed[id].uri.value == "http://":
+                       del config.plugins.simpleRSS.feed[id]
+               else:
+                       self.list.insert(id, getConfigListEntry(_("Feed: "), config.plugins.simpleRSS.feed[id].uri))
+                       config.plugins.simpleRSS.feedcount.value = id+1
+
+       def keySave(self):
+               global rssPoller
+               rssPoller.triggerReload()
+               ConfigListScreen.keySave(self)
+
+       def abort(self):
+               print "[SimpleRSS] Closing Setup Dialog"
+               # Keep feedcount sane
+               config.plugins.simpleRSS.feedcount.value = len(config.plugins.simpleRSS.feed)
+               config.plugins.simpleRSS.feedcount.save()
+
+class RSSList(GUIComponent):
+       def __init__(self, entries):
+               GUIComponent.__init__(self)
+               self.list = entries
+               self.l = eListboxPythonMultiContent()
+               self.l.setFont(0, gFont("Regular", 22))
+               self.l.setFont(1, gFont("Regular", 18))
+               self.l.setBuildFunc(self.buildListboxEntry)
+               self.l.setList(entries)
+               
+               self.onSelectionChanged = [ ]
+               
+       def connectSelChanged(self, fnc):
+               if not fnc in self.onSelectionChanged:
+                       self.onSelectionChanged.append(fnc)
+
+       def disconnectSelChanged(self, fnc):
+               if fnc in self.onSelectionChanged:
+                       self.onSelectionChanged.remove(fnc)
+
+       def selectionChanged(self):
+               for x in self.onSelectionChanged:
+                       x()
+
+       GUI_WIDGET = eListbox
+
+       def postWidgetCreate(self, instance):
+               instance.setContent(self.l)
+               instance.setItemHeight(100)
+               instance.selectionChanged.get().append(self.selectionChanged)
+
+       def buildListboxEntry(self, title, link, summary, enclosures):
+               res = [ None ]
+               width = self.l.getItemSize().width()
+               res.append(MultiContentEntryText(pos=(0, 0), size=(width, 75), font=0, flags = RT_HALIGN_LEFT|RT_WRAP, text = title))
+               res.append(MultiContentEntryText(pos=(0, 75), size=(width, 20), font=1, flags = RT_HALIGN_LEFT, text = link))
+               return res
+
+       def getCurrentEntry(self):
+               return self.l.getCurrentSelection()
+
+       def getCurrentIndex(self):
+               return self.instance.getCurrentIndex()
+
+       def moveToIndex(self, index):
+               self.instance.moveSelectionTo(index)
+
+       def moveToEntry(self, entry):
+               if entry is None:
+                       return
+
+               count = 0
+               for x in self.list:
+                       if entry[0] == x[0]:
+                               self.instance.moveSelectionTo(count)
+                               break
+                       count += 1
+
+       def moveDown(self):
+               self.instance.moveSelection(self.instance.moveDown)
+
+       def moveUp(self):
+               self.instance.moveSelection(self.instance.moveUp)
+
+class RSSView(Screen):
+       skin = """
+               <screen position="100,100" size="460,400" title="Simple RSS Reader" >
+                       <widget name="content" position="0,0" size="460,400" font="Regular; 22" />
+               </screen>"""
+
+       def __init__(self, session, data, enclosureCB=None, nextCB=None, previousCB=None):
+               Screen.__init__(self, session)
+
+               self.enclosureCB = enclosureCB
+               self.nextCB = nextCB
+               self.previousCB = previousCB
+
+               self.data = data
+               if data is not None:
+                       self["content"] = ScrollLabel("\n\n".join([data[0], data[2], " ".join([str(len(data[3])), "Enclosures"])]))
+               else:
+                       self["content"] = ScrollLabel()
+
+               self["actions"] = ActionMap([ "OkCancelActions", "ColorActions", "DirectionActions" ],
+               {
+                       "cancel": self.close,
+                       "ok": self.selectEnclosure,
+                       "up": self.up,
+                       "down": self.down,
+                       "right": self.next,
+                       "left": self.previous,
+               })
+
+       def up(self):
+               self["content"].pageUp()
+
+       def down(self):
+               self["content"].pageDown()
+
+       def next(self):
+               if self.nextCB is not None:
+                       self.data = self.nextCB()
+                       self.setContent()
+
+       def previous(self):
+               if self.previousCB is not None:
+                       self.data = self.previousCB()
+                       self.setContent()
+
+       def setContent(self):
+               if self.data is not None:
+                       self["content"].setText("\n\n".join([self.data[0], self.data[2], " ".join([str(len(self.data[3])), "Enclosures"])]))
+               else:
+                       self["content"].setText("")
+
+       def selectEnclosure(self):
+               if self.data is not None and self.enclosureCB is not None:
+                       self.enclosureCB(self.data)
+
+class RSSDisplay(Screen):
+       skin = """
+               <screen position="100,100" size="460,400" title="Simple RSS Reader" >
+                       <widget name="content" position="0,0" size="460,304" scrollbarMode="showOnDemand" />
+                       <widget name="summary" position="0,305" size="460,95" font="Regular;16" />
+               </screen>"""
+
+       MENU_UPDATE = 1
+       MENU_CONFIG = 2
+
+       def __init__(self, session, data, interactive = False, poller = None):
+               Screen.__init__(self, session)
+
+               if interactive:
+                       self["actions"] = ActionMap([ "OkCancelActions", "ChannelSelectBaseActions", "MenuActions" ], 
+                       {
+                               "ok": self.showCurrentEntry,
+                               "cancel": self.conditionalClose,
+                               "nextBouquet": self.next,
+                               "prevBouquet": self.previous,
+                               "menu": self.menu
+                       })
+                       self.onShown.append(self.__show)
+                       self.onClose.append(self.__close)
+
+               self.rssPoller = poller
+               self.feedview = False
+               self.feeds = None
+               self.feedid = None
+               if len(data):
+                       if isinstance(data[0], Feed):
+                               self.feedview = True
+                               # TODO: find better way to solve this
+                               self.feeds = ([(feed.title, feed.description, ' '.join([str(len(feed.history)), "Entries"]), feed.history) for feed in data])
+                               self["content"] = RSSList(self.feeds)
+                               self["summary"] = Label(self.feeds[0][2])
+                       else:
+                               self["content"] = RSSList(data)
+                               self["summary"] = Label(data[0][2])
+               else:
+                       self["content"] = RSSList(data)
+                       self["summary"] = Label("")
+
+               self["content"].connectSelChanged(self.updateSummary)
+               self.onLayoutFinish.append(self.setConditionalTitle)
+
+       def __show(self):
+               if rssPoller is not None:
+                       self.rssPoller.addCallback(self.pollCallback)
+
+       def __close(self):
+               if rssPoller is not None:
+                       self.rssPoller.removeCallback(self.pollCallback)
+
+       def pollCallback(self, id = None):
+               print "[SimpleRSS] RSSDisplay called back"
+               current_entry = self["content"].getCurrentEntry()
+
+               if self.feeds:
+                       if id is not None:
+                               print "[SimpleRSS] pollCallback updating feed", id
+                               self.feeds[id] = (self.rssPoller.feeds[id].title, self.rssPoller.feeds[id].description, ' '.join([str(len(self.rssPoller.feeds[id].history)), "Entries"]), self.rssPoller.feeds[id].history)
+                       else:
+                               print "[SimpleRSS] pollCallback updating all feeds"
+                               self.feeds = ([(feed.title, feed.description, ' '.join([str(len(feed.history)), "Entries"]), feed.history) for feed in self.rssPoller.feeds])
+
+               if self.feedview:
+                       print "[SimpleRSS] pollCallback updating Feedlist"
+                       self["content"].l.setList(self.feeds)
+               elif self.feedid:
+                       print "[SimpleRSS] pollCallback updating Itemlist"
+                       self["content"].l.setList(self.feeds[self.feedid][3])
+
+               self["content"].moveToEntry(current_entry)
+               self.setConditionalTitle()
+               self.updateSummary()
+
+       def setConditionalTitle(self):
+               # Feedview: Overview, has feeds
+               if self.feedview:
+                       self.setTitle("Simple RSS Reader")
+               # Feedid: Feed, has feeds
+               elif self.feedid is not None:
+                       self.setTitle(''.join(["Simple RSS Reader: ", self.feeds[self.feedid][0]]))
+               # None: new_items
+               else:
+                       self.setTitle("Simple RSS Reader: New Items")
+
+       def updateSummary(self):
+               current_entry = self["content"].getCurrentEntry()
+               if current_entry:
+                       self["summary"].setText(current_entry[2])
+               else:
+                       self["summary"].setText("")
+
+       def menu(self):
+               self.session.openWithCallback(self.menuChoice, ChoiceBox, "What to do?", [(_("Update Feed"), self.MENU_UPDATE), (_("Setup"), self.MENU_CONFIG)])
+
+       def menuChoice(self, result):
+               if result:
+                       if result[1] == self.MENU_UPDATE:
+                               self.rssPoller.singlePoll(self.feedid or self["content"].getCurrentIndex(), self.pollCallback)
+                               self.session.open(MessageBox, "Update is being done in Background.\nContents will automatically be updated when it's done.", type = MessageBox.TYPE_INFO, timeout = 5)
+                       elif result[1] == self.MENU_CONFIG:
+                               self.session.openWithCallback(self.menuClosed, SimpleRSS)
+
+       def menuClosed(self):
+               if self.feeds:
+                       current_entry = self["content"].getCurrentEntry()
+
+                       self.rssPoller.triggerReload()
+
+                       # TODO: fix this, its still as evil as some lines above
+                       self.feeds = ([(feed.title, feed.description, ' '.join([str(len(feed.history)), " Entries"]), feed.history) for feed in rssPoller.feeds])
+                       if self.feedview:
+                               self["content"].l.setList(self.feeds)
+
+                       self["content"].moveToEntry(current_entry)
+
+       def nextEntryCB(self):
+               self["content"].moveDown()
+               return self["content"].getCurrentEntry()
+
+       def previousEntryCB(self):
+               self["content"].moveUp()
+               return self["content"].getCurrentEntry()
+
+       def next(self):
+               if not self.feedview and self.feeds:
+                       self.feedid += 1
+                       if self.feedid == len(self.feeds):
+                               self.feedid = 0
+                       self["content"].l.setList(self.feeds[self.feedid][3])
+                       self["content"].moveToIndex(0)
+                       self.updateSummary()
+                       self.setConditionalTitle()
+
+       def previous(self):
+               if not self.feedview and self.feeds:
+                       if self.feedid == 0:
+                               self.feedid = len(self.feeds)
+                       self.feedid -= 1
+                       self["content"].l.setList(self.feeds[self.feedid][3])
+                       self["content"].moveToIndex(0)
+                       self.updateSummary()
+                       self.setConditionalTitle()
+
+       def conditionalClose(self):
+               if not self.feedview and self.feeds:
+                       self["content"].l.setList(self.feeds)
+                       self["content"].moveToIndex(self.feedid)
+                       self.feedview = True
+                       self.feedid = None
+                       self.updateSummary()
+                       self.setConditionalTitle()
+               else:
+                       self.close()
+
+       def showCurrentEntry(self):
+               current_entry = self["content"].getCurrentEntry()
+               if current_entry is None: # empty list
+                       return
+
+               # If showing feeds right now show items of marked feed
+               if self.feedview:
+                       self.feedid = self["content"].getCurrentIndex()
+                       self["content"].l.setList(current_entry[3])
+                       self["content"].moveToIndex(0)
+                       self.feedview = False
+                       self.updateSummary()
+                       self.setConditionalTitle()
+               # Else we're showing items -> show marked item
+               else:
+                       self.session.open(RSSView, current_entry, enclosureCB=self.selectEnclosure, nextCB=self.nextEntryCB, previousCB=self.previousEntryCB)
+
+       def selectEnclosure(self, current_entry = None):
+               if current_entry is None: # no entry given
+                       current_entry = self["content"].getCurrentEntry()
+
+               if current_entry is None: # empty list
+                       return
+
+               # Select stream in ChoiceBox if more than one present
+               if len(current_entry[3]) > 1:
+                       # TODO: beautify
+                       self.session.openWithCallback(self.enclosureSelected, ChoiceBox, "Select enclosure to play", [(x[0][x[0].rfind("/")+1:], x) for x in current_entry[3]])
+               # Play if one present
+               elif len(current_entry[3]):
+                       self.enclosureSelected((None, current_entry[3][0]))
+               # Nothing if none present
+
+       def enclosureSelected(self, enclosure):
+               if enclosure:
+                       (url, type) = enclosure[1]
+
+                       print "[SimpleRSS] Trying to play back enclosure: url=%s, type=%s" % (url, type)
+
+                       # TODO: other types? (showing images wouldn't be hard if the source was local)
+                       if type in ["video/mpeg", "audio/mpeg"]:
+                               # We should launch a Player here, but the MediaPlayer gets angry about our non-local sources
+                               from enigma import eServiceReference
+                               self.session.nav.playService(eServiceReference(4097, 0, url))
+
+class Feed:
+       MAX_HISTORY_ELEMENTS = 100
+
+       RSS = 1
+       ATOM = 2
+
+       def __init__(self, uri, autoupdate):
+               self.uri = uri
+               self.autoupdate = autoupdate
+               self.type = None
+               self.title = uri.encode("UTF-8")
+               self.description = ""
+               self.last_update = None
+               self.last_ids = set()
+               self.history = []
+
+       def gotDom(self, dom):
+               if self.type is None:
+                       # RSS 2.0
+                       if dom.documentElement.getAttribute("version") in ["2.0", "0.94", "0.93", "0.92", "0.91"]:
+                               self.type = self.RSS
+                               try:
+                                       self.title = dom.getElementsByTagName("channel")[0].getElementsByTagName("title")[0].childNodes[0].data.encode("UTF-8")
+                                       self.description = dom.getElementsByTagName("channel")[0].getElementsByTagName("description")[0].childNodes[0].data.encode("UTF-8")
+                               except:
+                                       pass
+                       # RSS 1.0 (NS: http://www.w3.org/1999/02/22-rdf-syntax-ns#)
+                       elif dom.documentElement.localName == "RDF":
+                               self.type = self.RSS
+                               try:
+                                       self.title = dom.getElementsByTagName("channel")[0].getElementsByTagName("title")[0].childNodes[0].data.encode("UTF-8")
+                                       self.description = dom.getElementsByTagName("channel")[0].getElementsByTagName("description")[0].childNodes[0].data.encode("UTF-8")
+                               except:
+                                       pass
+                       # Atom (NS: http://www.w3.org/2005/Atom)
+                       elif dom.documentElement.localName == "feed":
+                               self.type = self.ATOM
+                               try:
+                                       self.title = dom.getElementsByTagName("title")[0].childNodes[0].data.encode("UTF-8")
+                                       self.description = dom.getElementsByTagName("subtitle")[0].childNodes[0].data.encode("UTF-8")
+                               except:
+                                       pass
+                       else:
+                               raise NotImplementedError, 'Unsupported Feed: %s' % dom.documentElement.localName
+               if self.type == self.RSS:
+                       print "[SimpleRSS] type is rss"
+                       return self.gotRSSDom(dom)
+               elif self.type == self.ATOM:
+                       print "[SimpleRSS] type is atom"
+                       return self.gotAtomDom(dom)
+
+       def gotRSSDom(self, dom):
+               # Try to read when feed was last updated, if time equals return empty list. else fetch new items
+               try:
+                       updated = dom.getElementsByTagName("lastBuildDate")[0].childNodes[0].data
+                       if not self.last_update == updated:
+                               self.last_update = updated
+                               return self.parseRSS(dom.getElementsByTagName("item"))
+                       else:
+                               return [ ]
+               except:
+                       return self.parseRSS(dom.getElementsByTagName("item"))
+
+       def parseRSS(self, items):
+               new_items = []
+               for item in items:
+                       enclosure = []
+
+                       # Try to read title, continue if none found
+                       try:
+                               title = item.getElementsByTagName("title")[0].childNodes[0].data
+                       except:
+                               continue
+
+                       # Try to read link, empty if none
+                       try:
+                               link = item.getElementsByTagName("link")[0].childNodes[0].data
+                       except:
+                               link = ""
+                       
+                       # Try to read guid, link if none (RSS 1.0 or invalid RSS 2.0)
+                       try:
+                               guid = item.getElementsByTagName("guid")[0].childNodes[0].data
+                       except:
+                               guid = link
+
+                       # Continue if item is to be excluded
+                       if guid in self.last_ids:
+                               continue
+
+                       # Try to read summary (description element), empty if none
+                       try:
+                               summary = item.getElementsByTagName("description")[0].childNodes[0].data
+                       except:
+                               summary = ""
+
+                       # Read out enclosures
+                       for current in item.getElementsByTagName("enclosure"):
+                               enclosure.append((current.getAttribute("url").encode("UTF-8"), current.getAttribute("type").encode("UTF-8")))
+
+                       # Update Lists
+                       new_items.append((title.encode("UTF-8").strip(), link.encode("UTF-8").strip(), summary.encode("UTF-8").strip(), enclosure))
+                       self.last_ids.add(guid)
+
+               # Append known Items to new Items and evenentually cut it
+               self.history = new_items + self.history
+               self.history[:self.MAX_HISTORY_ELEMENTS]
+               
+               return new_items
+
+       def gotAtomDom(self, dom):
+               try:
+                       # Try to read when feed was last updated, if time equals return empty list. else fetch new items
+                       updated = dom.getElementsByTagName("updated")[0].childNodes[0].data
+                       if not self.last_update == updated:
+                               self.last_update = updated
+                               return self.parseAtom(dom.getElementsByTagName("entry"))
+                       else:
+                               return [ ]
+               except:
+                       return self.parseAtom(dom.getElementsByTagName("entry"))
+
+       def parseAtom(self, items):
+               new_items = []
+               for item in items:
+                       enclosure = []
+                       link = ""
+                       
+                       # Try to read title, continue if none found
+                       try:
+                               title = item.getElementsByTagName("title")[0].childNodes[0].data
+                       except:
+                               continue
+
+                       # Try to read id, continue if none found (invalid feed, should be handled differently) or to be excluded
+                       try:
+                               id = item.getElementsByTagName("id")[0].childNodes[0].data
+                               if id in self.last_ids:
+                                       continue
+                       except:
+                               continue
+
+                       # Read out enclosures and link
+                       for current in item.getElementsByTagName("link"):
+                               # Enclosure
+                               if current.getAttribute("rel") == "enclosure":
+                                       enclosure.append((current.getAttribute("href").encode("UTF-8"), current.getAttribute("type").encode("UTF-8")))
+                               # No Enclosure, assume its a link to the item
+                               else:
+                                       link = current.getAttribute("href")
+                       
+                       # Try to read summary, empty if none
+                       try:
+                               summary = item.getElementsByTagName("summary")[0].childNodes[0].data
+                       except:
+                               summary = ""
+
+                       # Update Lists
+                       new_items.append((title.encode("UTF-8").strip(), link.encode("UTF-8").strip(), summary.encode("UTF-8").strip(), enclosure))
+                       self.last_ids.add(id)
+
+                # Append known Items to new Items and evenentually cut it
+               self.history = new_items + self.history
+               self.history[:self.MAX_HISTORY_ELEMENTS]
+
+               return new_items
+
+class RSSPoller:
+       def __init__(self, session):
+               self.poll_timer = eTimer()
+               self.poll_timer.timeout.get().append(self.poll)
+               self.poll_timer.start(0, 1)
+               self.update_callbacks = [ ]
+               self.last_links = Set()
+               self.session = session
+               self.dialog = None
+               self.reloading = False
+       
+               self.feeds = [ ]
+               for i in range(0, config.plugins.simpleRSS.feedcount.value):
+                       self.feeds.append(Feed(config.plugins.simpleRSS.feed[i].uri.value, config.plugins.simpleRSS.feed[i].autoupdate.value))
+               self.new_items = [ ]
+               self.current_feed = 0
+
+       def addCallback(self, callback):
+               if callback not in self.update_callbacks:
+                       self.update_callbacks.append(callback)
+
+       def removeCallback(self, callback):
+               if callback in self.update_callbacks:
+                       self.update_callbacks.remove(callback)
+
+       def doCallback(self):
+               for callback in self.update_callbacks:
+                       try:
+                               callback()
+                       except:
+                               pass
+
+       # Single Functions are here to wrap
+       def _gotSinglePage(self, id, callback, errorback, data):
+               self._gotPage(data, id, callback, errorback)
+
+       def singleError(self, errorback, error):
+               self.error(error, errorback)
+
+       def error(self, error, errorback = None):
+               if not self.session:
+                       print "[SimpleRSS] error polling"
+               elif errorback:
+                       errorback(error)
+               else:
+                       self.session.open(MessageBox, "Sorry, failed to fetch feed.\n" + error, type = MessageBox.TYPE_INFO, timeout = 5)
+                       # Assume its just a temporary failure and jump over to next feed                          
+                       self.current_feed += 1                     
+                       self.poll_timer.start(1000, 1)
+
+       def _gotPage(self, data, id = None, callback = None, errorback = None):
+               # workaround: exceptions in gotPage-callback were ignored
+               try:
+                       self.gotPage(data, id)
+                       if callback is not None:
+                               callback(id)
+               except NotImplementedError, errmsg:
+                       # TODO: Annoying with Multifeed?
+                       self.session.open(MessageBox, "Sorry, this type of feed is unsupported.\n"+ str(errmsg), type = MessageBox.TYPE_INFO, timeout = 5)
+               except:
+                       import traceback, sys
+                       traceback.print_exc(file=sys.stdout)
+                       if errorback is not None:
+                               errorback()
+                       # Assume its just a temporary failure and jump over to next feed
+                       self.current_feed += 1
+                       self.poll_timer.start(1000, 1)
+       
+       def gotPage(self, data, id = None):
+               print "[SimpleRSS] parsing.."
+
+               # sometimes activates spinner :-/
+               dom = xml.dom.minidom.parseString(data)
+
+               print "[SimpleRSS] xml parsed.."
+
+               # For Single-Polling
+               if id is not None:
+                       self.feeds[id].gotDom(dom)
+                       print "[SimpleRSS] single feed parsed.."
+                       return
+               else:
+                       new_items = self.feeds[self.current_feed].gotDom(dom)
+
+               print "[SimpleRSS] feed parsed.."
+
+               # Append new items to locally bound ones
+               self.new_items.extend(new_items)
+
+               # Start Timer so we can either fetch next feed or show new_items
+               self.current_feed += 1
+               self.poll_timer.start(1000, 1)
+
+
+       def singlePoll(self, id, callback = None, errorback = None):
+               from Tools.BoundFunction import boundFunction
+               remote = urlsplit(self.feeds[id].uri)
+               print "[SimpleRSS] updating", remote.geturl()
+               hostname = remote.hostname
+               port = remote.port or 80
+               path = '?'.join([remote.path, remote.query])
+               print "[SimpleRSS] hostname:", hostname, ", port:", port, ", path:", path
+               getPage(hostname, port, path, callback=boundFunction(self._gotSinglePage, id, callback, errorback), errorback=boundFunction(self.error, errorback))
+
+       def poll(self):
+               # Reloading, reschedule
+               if self.reloading:
+                       print "[SimpleRSS] timer triggered while reloading, rescheduling"
+                       self.poll_timer.start(10000, 1)
+               # Dialog shown, hide
+               elif self.dialog:
+                       print "[SimpleRSS] hiding"
+                       self.dialog.hide()
+                       self.dialog = None
+                       self.new_items = [ ]
+                       self.current_feed = 0
+                       self.poll_timer.startLongTimer(config.plugins.simpleRSS.interval.value*60)
+               # End of List
+               elif len(self.feeds) <= self.current_feed:
+                       # New Items
+                       if len(self.new_items):
+                               print "[SimpleRSS] got", len(self.new_items), "new items"
+                               print "[SimpleRSS] calling back"
+                               self.doCallback()
+                               # Inform User
+                               if config.plugins.simpleRSS.show_new.value:
+                                       self.dialog = self.session.instantiateDialog(RSSDisplay, self.new_items, poller = self)
+                                       self.dialog.show()
+                                       self.poll_timer.startLongTimer(5)
+                       # No new Items
+                       else:
+                               print "[SimpleRSS] no new items"
+                               self.new_items = [ ]
+                               self.current_feed = 0
+                               self.poll_timer.startLongTimer(config.plugins.simpleRSS.interval.value*60)
+               # Feed is supposed to auto-update
+               elif self.feeds[self.current_feed].autoupdate:
+                       remote = urlsplit(self.feeds[self.current_feed].uri)
+                       hostname = remote.hostname
+                       port = remote.port or 80
+                       path = '?'.join([remote.path, remote.query])
+                       print "[SimpleRSS] hostname:", hostname, ", port:", port, ", path:", path
+                       self.d = getPage(hostname, port, path, callback=self._gotPage, errorback=self.error)
+               # Go to next feed in 100ms
+               else:
+                       print "[SimpleRSS] passing feed"
+                       self.current_feed += 1
+                       self.poll_timer.start(100, 1)
+
+       def shutdown(self):
+               self.poll_timer.timeout.get().remove(self.poll)
+               self.poll_timer = None
+
+       def triggerReload(self):
+               self.reloading = True
+
+               # TODO: Fix this evil way of updating feeds
+               newfeeds = []
+               for i in range(0, config.plugins.simpleRSS.feedcount.value):
+                       newfeeds.append(Feed(config.plugins.simpleRSS.feed[i].uri.value, config.plugins.simpleRSS.feed[i].autoupdate.value))
+
+               self.feeds = newfeeds
+
+               self.reloading = False
+
+def main(session, **kwargs):
+       print "[SimpleRSS] Displaying SimpleRSS-Setup"
+       session.open(SimpleRSS)
+
+rssPoller = None
+
+def autostart(reason, **kwargs):
+       global rssPoller
+       
+       # not nice (?), but works
+       if kwargs.has_key("session") and reason == 0:
+               rssPoller = RSSPoller(kwargs["session"])
+       elif reason == 1:
+               rssPoller.shutdown()
+               rssPoller = None
+
+def showCurrent(session, **kwargs):
+       global rssPoller
+       if rssPoller is None:
+               return
+       session.open(RSSDisplay, rssPoller.feeds, interactive = True, poller = rssPoller)
+
+def Plugins(**kwargs):
+       return [ PluginDescriptor(name="RSS Reader", description="A (really) simple RSS reader", where = PluginDescriptor.WHERE_PLUGINMENU, fnc=main),
+               PluginDescriptor(where = [PluginDescriptor.WHERE_SESSIONSTART, PluginDescriptor.WHERE_AUTOSTART], fnc = autostart),
+               PluginDescriptor(name="View RSS", description="Let's you view current RSS entries", where = PluginDescriptor.WHERE_EXTENSIONSMENU, fnc=showCurrent) ]