1 # warning, this is work in progress.
2 # plus, the "global_session" stuff is of course very lame.
3 # plus, the error handling sucks.
4 from Screens.Screen import Screen
5 from Screens.MessageBox import MessageBox
6 from Components.ActionMap import ActionMap
7 from Components.GUIComponent import GUIComponent
8 from Components.Label import Label
9 from Components.MultiContent import MultiContentEntryText, RT_HALIGN_LEFT, RT_HALIGN_RIGHT, RT_WRAP
10 from Plugins.Plugin import PluginDescriptor
11 from enigma import eListboxPythonMultiContent, eListbox, gFont, iServiceInformation
13 from twisted.web import server
14 from twisted.web.resource import Resource
15 from twisted.web.client import getPage
16 import xml.dom.minidom
18 from Tools.XMLTools import mergeText, elementsWithTag
20 from enigma import eTimer
23 my_global_session = None
25 urls = ["http://www.heise.de/newsticker/heise.rdf", "http://rss.slashdot.org/Slashdot/slashdot/to"]
27 from Components.config import config, ConfigSubsection, ConfigSelection, getConfigListEntry
28 from Components.ConfigList import ConfigList, ConfigListScreen
29 config.simpleRSS = ConfigSubsection()
30 config.simpleRSS.hostname = ConfigSelection(choices = urls)
32 class SimpleRSS(ConfigListScreen, Screen):
34 <screen position="100,100" size="550,400" title="Simple RSS Reader" >
35 <widget name="config" position="20,10" size="460,350" scrollbarMode="showOnDemand" />
38 def __init__(self, session, args = None):
39 from Tools.BoundFunction import boundFunction
42 Screen.__init__(self, session)
43 self.skin = SimpleRSS.skin
45 self.onClose.append(self.abort)
47 # nun erzeugen wir eine liste von elementen fuer die menu liste.
49 self.list.append(getConfigListEntry(_("RSS Feed URI"), config.simpleRSS.hostname))
52 ConfigListScreen.__init__(self, self.list)
54 self["actions"] = ActionMap([ "OkCancelActions" ],
57 # "cancel": self.close,
60 self["setupActions"] = ActionMap(["SetupActions"],
70 for x in self["config"].list:
75 for x in self["config"].list:
79 class RSSList(GUIComponent):
80 def __init__(self, entries):
81 GUIComponent.__init__(self)
82 self.l = eListboxPythonMultiContent()
83 self.l.setFont(0, gFont("Regular", 22))
84 self.l.setFont(1, gFont("Regular", 18))
85 self.list = [self.buildListboxEntry(x) for x in entries]
86 self.l.setList(self.list)
90 def postWidgetCreate(self, instance):
91 instance.setContent(self.l)
92 instance.setItemHeight(100)
94 def buildListboxEntry(self, rss_entry):
96 res.append(MultiContentEntryText(pos=(0, 0), size=(460, 75), font=0, flags = RT_HALIGN_LEFT|RT_WRAP, text = rss_entry[0]))
97 res.append(MultiContentEntryText(pos=(0, 75), size=(460, 20), font=1, flags = RT_HALIGN_LEFT, text = rss_entry[1]))
101 def getCurrentEntry(self):
102 return self.l.getCurrentSelection()
104 class RSSDisplay(Screen):
106 <screen position="100,100" size="460,400" title="Simple RSS Reader" >
107 <widget name="content" position="0,0" size="460,400" />
110 def __init__(self, session, data, interactive = False):
111 Screen.__init__(self, session)
112 self.skin = RSSDisplay.skin
115 self["actions"] = ActionMap([ "OkCancelActions" ],
117 "ok": self.showCurrentEntry,
118 "cancel": self.close,
121 self["content"] = RSSList(data)
123 def showCurrentEntry(self):
124 current_entry = self["content"].getCurrentEntry()
125 if current_entry is None: # empty list
128 (title, link, enclosure) = current_entry[0]
131 (url, type) = enclosure[0] # TODO: currently, we used the first enclosure. there can be multiple.
133 print "enclosure: url=%s, type=%s" % (url, type)
135 if type in ["video/mpeg", "audio/mpeg"]:
136 from enigma import eServiceReference
137 # we should better launch a player or so...
138 self.session.nav.playService(eServiceReference(4097, 0, url))
142 MAX_HISTORY_ELEMENTS = 100
145 self.poll_timer = eTimer()
146 self.poll_timer.timeout.get().append(self.poll)
147 self.poll_timer.start(0, 1)
148 self.last_links = Set()
152 def error(self, error):
153 if not my_global_session:
154 print "error polling"
156 my_global_session.open(MessageBox, "Sorry, failed to fetch feed.\n" + error)
158 def _gotPage(self, data):
159 # workaround: exceptions in gotPage-callback were ignored
163 import traceback, sys
164 traceback.print_exc(file=sys.stdout)
167 def gotPage(self, data):
172 dom = xml.dom.minidom.parseString(data)
173 for r in elementsWithTag(dom.childNodes, "rss"):
179 for item in elementsWithTag(r.childNodes, "item"):
183 for channel in elementsWithTag(r.childNodes, "channel"):
184 for item in elementsWithTag(channel.childNodes, "item"):
194 for s in elementsWithTag(item.childNodes, lambda x: x in ["title", "link", "enclosure"]):
195 if s.tagName == "title":
196 title = mergeText(s.childNodes)
197 elif s.tagName == "link":
198 link = mergeText(s.childNodes)
199 elif s.tagName == "enclosure":
200 enclosure.append((s.getAttribute("url").encode("UTF-8"), s.getAttribute("type").encode("UTF-8")))
202 print title, link, enclosure
206 rss_entry = (title.encode("UTF-8"), link.encode("UTF-8"), enclosure)
208 self.history.insert(0, rss_entry)
210 if link not in self.last_links:
211 self.last_links.add(link)
212 new_items.append(rss_entry)
213 print "NEW", rss_entry[0], rss_entry[1]
215 self.history = self.history[:self.MAX_HISTORY_ELEMENTS]
218 self.dialog = my_global_session.instantiateDialog(RSSDisplay, new_items)
220 self.poll_timer.start(5000, 1)
222 self.poll_timer.start(60000, 1)
229 self.poll_timer.start(60000, 1)
230 elif not my_global_session:
231 print "no session yet."
232 self.poll_timer.start(10000, 1)
234 print "yes, session ok. starting"
235 self.d = getPage(config.simpleRSS.hostname.value).addCallback(self._gotPage).addErrback(self.error)
238 self.poll_timer.timeout.get().remove(self.poll)
239 self.poll_timer = None
243 session.open(SimpleRSS)
248 def autostart(reason, **kwargs):
253 # ouch, this is a hack
254 if kwargs.has_key("session"):
255 global my_global_session
256 print "session now available"
257 my_global_session = kwargs["session"]
262 rssPoller = RSSPoller()
267 def showCurrent(session, **kwargs):
269 if rssPoller is None:
271 session.open(RSSDisplay, rssPoller.history, interactive = True)
273 def Plugins(**kwargs):
274 return [ PluginDescriptor(name="RSS Reader", description="A (really) simple RSS reader", where = PluginDescriptor.WHERE_PLUGINMENU, fnc=main),
275 PluginDescriptor(where = [PluginDescriptor.WHERE_SESSIONSTART, PluginDescriptor.WHERE_AUTOSTART], fnc = autostart),
276 PluginDescriptor(name="View RSS", description="Let's you view current RSS entries", where = PluginDescriptor.WHERE_EXTENSIONSMENU, fnc=showCurrent) ]