49da5e632ae86a898d7684273d797389cdc1554b
[vuplus_dvbapp] / lib / python / Plugins / Extensions / SimpleRSS / plugin.py
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
10 from Plugins.Plugin import PluginDescriptor
11 from enigma import eListboxPythonMultiContent, eListbox, gFont, iServiceInformation, RT_HALIGN_LEFT, RT_HALIGN_RIGHT, RT_WRAP
12
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
17
18 from Tools.XMLTools import mergeText, elementsWithTag
19
20 from enigma import eTimer
21 from sets import Set
22
23 my_global_session = None
24
25 urls = ["http://www.heise.de/newsticker/heise.rdf", "http://rss.slashdot.org/Slashdot/slashdot/to"]
26
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)
31
32 class SimpleRSS(ConfigListScreen, Screen):
33         skin = """
34                 <screen position="100,100" size="550,400" title="Simple RSS Reader" >
35                 <widget name="config" position="20,10" size="460,350" scrollbarMode="showOnDemand" />
36                 </screen>"""
37
38         def __init__(self, session, args = None):
39                 from Tools.BoundFunction import boundFunction
40                 
41                 print "screen init"
42                 Screen.__init__(self, session)
43                 self.skin = SimpleRSS.skin
44                 
45                 self.onClose.append(self.abort)
46                 
47                 # nun erzeugen wir eine liste von elementen fuer die menu liste.
48                 self.list = [ ]
49                 self.list.append(getConfigListEntry(_("RSS Feed URI"), config.simpleRSS.hostname))
50                 
51                 # die liste selbst
52                 ConfigListScreen.__init__(self, self.list)
53
54                 self["actions"] = ActionMap([ "OkCancelActions" ], 
55                 {
56                         "ok": self.close,
57 #                       "cancel": self.close,
58                 })
59
60                 self["setupActions"] = ActionMap(["SetupActions"],
61                 {
62                         "save": self.save,
63                         "cancel": self.close
64                 }, -1)
65         
66         def abort(self):
67                 print "aborting"
68
69         def save(self):
70                 for x in self["config"].list:
71                         x[1].save()
72                 self.close()
73
74         def cancel(self):
75                 for x in self["config"].list:
76                         x[1].cancel()
77                 self.close()
78
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)
87
88         GUI_WIDGET = eListbox
89
90         def postWidgetCreate(self, instance):
91                 instance.setContent(self.l)
92                 instance.setItemHeight(100)
93
94         def buildListboxEntry(self, rss_entry):
95                 res = [ 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]))
98                 return res
99
100
101         def getCurrentEntry(self):
102                 return self.l.getCurrentSelection()
103
104 class RSSDisplay(Screen):
105         skin = """
106                 <screen position="100,100" size="460,400" title="Simple RSS Reader" >
107                 <widget name="content" position="0,0" size="460,400" />
108                 </screen>"""
109
110         def __init__(self, session, data, interactive = False):
111                 Screen.__init__(self, session)
112                 self.skin = RSSDisplay.skin
113                 
114                 if interactive:
115                         self["actions"] = ActionMap([ "OkCancelActions" ], 
116                         {
117                                 "ok": self.showCurrentEntry,
118                                 "cancel": self.close,
119                         })
120
121                 self["content"] = RSSList(data) 
122
123         def showCurrentEntry(self):
124                 current_entry = self["content"].getCurrentEntry()
125                 if current_entry is None: # empty list
126                         return
127
128                 (title, link, enclosure) = current_entry[0]
129                 
130                 if len(enclosure):
131                         (url, type) = enclosure[0] # TODO: currently, we used the first enclosure. there can be multiple.
132                         
133                         print "enclosure: url=%s, type=%s" % (url, type)
134                         
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))
139
140 class RSSPoller:
141
142         MAX_HISTORY_ELEMENTS = 100
143
144         def __init__(self):
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()
149                 self.dialog = None
150                 self.history = [ ]
151                 
152         def error(self, error):
153                 if not my_global_session:
154                         print "error polling"
155                 else:
156                         my_global_session.open(MessageBox, "Sorry, failed to fetch feed.\n" + error)
157         
158         def _gotPage(self, data):
159                 # workaround: exceptions in gotPage-callback were ignored
160                 try:
161                         self.gotPage(data)
162                 except:
163                         import traceback, sys
164                         traceback.print_exc(file=sys.stdout)
165                         raise e
166         
167         def gotPage(self, data):
168                 print "parsing.."
169                 
170                 new_items = [ ]
171                 
172                 dom = xml.dom.minidom.parseString(data)
173                 for r in elementsWithTag(dom.childNodes, "rss"):
174                         rss = r
175
176                 items = [ ]
177                 
178                 # RSS 1.0
179                 for item in elementsWithTag(r.childNodes, "item"):
180                         items.append(item)
181
182                 # RSS 2.0
183                 for channel in elementsWithTag(r.childNodes, "channel"):
184                         for item in elementsWithTag(channel.childNodes, "item"):
185                                 items.append(item)
186
187                 for item in items:
188                         title = None
189                         link = ""
190                         enclosure = [ ]
191                         
192                         print "got item"
193
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")))
201
202                         print title, link, enclosure
203                         if title is None:
204                                 continue
205
206                         rss_entry = (title.encode("UTF-8"), link.encode("UTF-8"), enclosure)
207
208                         self.history.insert(0, rss_entry)
209
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]
214
215                 self.history = self.history[:self.MAX_HISTORY_ELEMENTS]
216                 
217                 if len(new_items):
218                         self.dialog = my_global_session.instantiateDialog(RSSDisplay, new_items)
219                         self.dialog.show()
220                         self.poll_timer.start(5000, 1)
221                 else:
222                         self.poll_timer.start(60000, 1)
223
224         def poll(self):
225                 if self.dialog:
226                         print "hiding"
227                         self.dialog.hide()
228                         self.dialog = None
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)
233                 else:
234                         print "yes, session ok. starting"
235                         self.d = getPage(config.simpleRSS.hostname.value).addCallback(self._gotPage).addErrback(self.error)
236
237         def shutdown(self):
238                 self.poll_timer.timeout.get().remove(self.poll)
239                 self.poll_timer = None
240
241 def main(session):
242         print "session.open"
243         session.open(SimpleRSS)
244         print "done"
245
246 rssPoller = None
247
248 def autostart(reason, **kwargs):
249         global rssPoller
250         
251         print "autostart"
252
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"]
258                 return
259         
260         print "autostart"
261         if reason == 0:
262                 rssPoller = RSSPoller()
263         elif reason == 1:
264                 rssPoller.shutdown()
265                 rssPoller = None
266
267 def showCurrent(session, **kwargs):
268         global rssPoller
269         if rssPoller is None:
270                 return
271         session.open(RSSDisplay, rssPoller.history, interactive = True)
272
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) ]