1 # warning, this is work in progress.
2 # plus, the error handling sucks.
6 # - all that stuff I forgot...
8 from Screens.Screen import Screen
9 from Screens.MessageBox import MessageBox
10 from Screens.ChoiceBox import ChoiceBox
11 from Components.ActionMap import ActionMap
12 from Components.Label import Label
13 from Components.ScrollLabel import ScrollLabel
14 from Components.GUIComponent import GUIComponent
15 from Components.MultiContent import MultiContentEntryText
16 from Components.Button import Button
17 from Plugins.Plugin import PluginDescriptor
18 from enigma import eTimer, eListboxPythonMultiContent, eListbox, gFont, RT_HALIGN_LEFT, RT_WRAP
20 from httpclient import getPage
21 from urlparse import urlsplit
22 import xml.dom.minidom
26 from Components.config import config, configfile, ConfigSubsection, ConfigSubList, ConfigEnableDisable, ConfigInteger, ConfigText, getConfigListEntry
27 from Components.ConfigList import ConfigListScreen
29 config.plugins.simpleRSS = ConfigSubsection()
30 config.plugins.simpleRSS.show_new = ConfigEnableDisable(default=True)
31 config.plugins.simpleRSS.interval = ConfigInteger(default=10, limits=(5, 300))
32 config.plugins.simpleRSS.feedcount = ConfigInteger(default=0)
33 config.plugins.simpleRSS.feed = ConfigSubList()
34 for i in range(0, config.plugins.simpleRSS.feedcount.value):
35 config.plugins.simpleRSS.feed.append(ConfigSubsection())
36 config.plugins.simpleRSS.feed[i].uri = ConfigText(default="http://", fixed_size = False)
37 config.plugins.simpleRSS.feed[i].autoupdate = ConfigEnableDisable(default=True)
39 class SimpleRSSEdit(ConfigListScreen, Screen):
41 <screen name="SimpleRSSEdit" position="100,100" size="550,120" title="Simple RSS Reader Setup" >
42 <widget name="config" position="20,10" size="510,75" scrollbarMode="showOnDemand" />
43 <ePixmap name="red" position="0,75" zPosition="4" size="140,40" pixmap="key_red-fs8.png" transparent="1" alphatest="on" />
44 <ePixmap name="green" position="140,75" zPosition="4" size="140,40" pixmap="key_green-fs8.png" transparent="1" alphatest="on" />
45 <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" />
46 <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" />
49 def __init__(self, session, id):
50 Screen.__init__(self, session)
52 self.list = [ getConfigListEntry(_("Autoupdate: "), config.plugins.simpleRSS.feed[id].autoupdate), getConfigListEntry(_("Feed URI: "), config.plugins.simpleRSS.feed[id].uri) ]
54 ConfigListScreen.__init__(self, self.list, session)
56 self["key_red"] = Button(_("Cancel"))
57 self["key_green"] = Button(_("OK"))
59 self["setupActions"] = ActionMap(["SetupActions"],
62 "cancel": self.keyCancel
68 config.plugins.simpleRSS.feed[self.id].save()
69 config.plugins.simpleRSS.feed.save()
72 class SimpleRSS(ConfigListScreen, Screen):
74 <screen name="SimpleRSS" position="100,100" size="550,400" title="Simple RSS Reader Setup" >
75 <widget name="config" position="20,10" size="510,350" scrollbarMode="showOnDemand" />
76 <ePixmap name="red" position="0,360" zPosition="4" size="140,40" pixmap="key_red-fs8.png" transparent="1" alphatest="on" />
77 <ePixmap name="green" position="140,360" zPosition="4" size="140,40" pixmap="key_green-fs8.png" transparent="1" alphatest="on" />
78 <ePixmap name="yellow" position="280,360" zPosition="4" size="140,40" pixmap="key_yellow-fs8.png" transparent="1" alphatest="on" />
79 <ePixmap name="blue" position="420,360" zPosition="4" size="140,40" pixmap="key_blue-fs8.png" transparent="1" alphatest="on" />
80 <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" />
81 <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" />
82 <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" />
83 <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" />
86 def __init__(self, session, args = None):
87 Screen.__init__(self, session)
89 self.onClose.append(self.abort)
91 # nun erzeugen wir eine liste von elementen fuer die menu liste.
93 for i in range(0, config.plugins.simpleRSS.feedcount.value):
94 self.list.append(getConfigListEntry(_("Feed: "), config.plugins.simpleRSS.feed[i].uri))
96 self.list.append(getConfigListEntry(_("Show new Messages: "), config.plugins.simpleRSS.show_new))
97 self.list.append(getConfigListEntry(_("Update Interval (min): "), config.plugins.simpleRSS.interval))
100 ConfigListScreen.__init__(self, self.list, session)
102 self["key_red"] = Button(_("Cancel"))
103 self["key_green"] = Button(_("OK"))
104 self["key_yellow"] = Button(_("New"))
105 self["key_blue"] = Button(_("Delete"))
107 self["setupActions"] = ActionMap(["SetupActions", "ColorActions"],
111 "save": self.keySave,
112 "cancel": self.keyCancel,
117 self.session.openWithCallback(self.deleteConfirm, MessageBox, "Really delete this entry?\nIt cannot be recovered!")
119 def deleteConfirm(self, result):
121 id = self["config"].instance.getCurrentIndex()
122 del config.plugins.simpleRSS.feed[id]
123 config.plugins.simpleRSS.feedcount.value -= 1
126 self["config"].setList(self.list)
129 id = self["config"].instance.getCurrentIndex()
130 self.session.openWithCallback(self.refresh, SimpleRSSEdit, id)
136 id = len(config.plugins.simpleRSS.feed)
137 config.plugins.simpleRSS.feed.append(ConfigSubsection())
138 config.plugins.simpleRSS.feed[id].uri = ConfigText(default="http://", fixed_size = False)
139 config.plugins.simpleRSS.feed[id].autoupdate = ConfigEnableDisable(default=True)
140 self.session.openWithCallback(self.conditionalNew, SimpleRSSEdit, id)
142 def conditionalNew(self):
143 id = len(config.plugins.simpleRSS.feed)-1
144 # Check if new feed differs from default
145 if config.plugins.simpleRSS.feed[id].uri.value == "http://":
146 del config.plugins.simpleRSS.feed[id]
148 self.list.insert(id, getConfigListEntry(_("Feed: "), config.plugins.simpleRSS.feed[id].uri))
149 config.plugins.simpleRSS.feedcount.value = id+1
153 rssPoller.triggerReload()
154 ConfigListScreen.keySave(self)
157 print "[SimpleRSS] Closing Setup Dialog"
158 # Keep feedcount sane
159 config.plugins.simpleRSS.feedcount.value = len(config.plugins.simpleRSS.feed)
160 config.plugins.simpleRSS.feedcount.save()
162 class RSSList(GUIComponent):
163 def __init__(self, entries):
164 GUIComponent.__init__(self)
166 self.l = eListboxPythonMultiContent()
167 self.l.setFont(0, gFont("Regular", 22))
168 self.l.setFont(1, gFont("Regular", 18))
169 self.l.setBuildFunc(self.buildListboxEntry)
170 self.l.setList(entries)
172 self.onSelectionChanged = [ ]
174 def connectSelChanged(self, fnc):
175 if not fnc in self.onSelectionChanged:
176 self.onSelectionChanged.append(fnc)
178 def disconnectSelChanged(self, fnc):
179 if fnc in self.onSelectionChanged:
180 self.onSelectionChanged.remove(fnc)
182 def selectionChanged(self):
183 for x in self.onSelectionChanged:
186 GUI_WIDGET = eListbox
188 def postWidgetCreate(self, instance):
189 instance.setContent(self.l)
190 instance.setItemHeight(100)
191 instance.selectionChanged.get().append(self.selectionChanged)
193 def buildListboxEntry(self, title, link, summary, enclosures):
195 width = self.l.getItemSize().width()
196 res.append(MultiContentEntryText(pos=(0, 0), size=(width, 75), font=0, flags = RT_HALIGN_LEFT|RT_WRAP, text = title))
197 res.append(MultiContentEntryText(pos=(0, 75), size=(width, 20), font=1, flags = RT_HALIGN_LEFT, text = link))
200 def getCurrentEntry(self):
201 return self.l.getCurrentSelection()
203 def getCurrentIndex(self):
204 return self.instance.getCurrentIndex()
206 def moveToIndex(self, index):
207 self.instance.moveSelectionTo(index)
209 def moveToEntry(self, entry):
216 self.instance.moveSelectionTo(count)
221 self.instance.moveSelection(self.instance.moveDown)
224 self.instance.moveSelection(self.instance.moveUp)
226 class RSSView(Screen):
228 <screen position="100,100" size="460,400" title="Simple RSS Reader" >
229 <widget name="content" position="0,0" size="460,400" font="Regular; 22" />
232 def __init__(self, session, data, enclosureCB=None, nextCB=None, previousCB=None):
233 Screen.__init__(self, session)
235 self.enclosureCB = enclosureCB
237 self.previousCB = previousCB
241 self["content"] = ScrollLabel("\n\n".join([data[0], data[2], " ".join([str(len(data[3])), "Enclosures"])]))
243 self["content"] = ScrollLabel()
245 self["actions"] = ActionMap([ "OkCancelActions", "ColorActions", "DirectionActions" ],
247 "cancel": self.close,
248 "ok": self.selectEnclosure,
252 "left": self.previous,
256 self["content"].pageUp()
259 self["content"].pageDown()
262 if self.nextCB is not None:
263 self.data = self.nextCB()
267 if self.previousCB is not None:
268 self.data = self.previousCB()
271 def setContent(self):
272 if self.data is not None:
273 self["content"].setText("\n\n".join([self.data[0], self.data[2], " ".join([str(len(self.data[3])), "Enclosures"])]))
275 self["content"].setText("")
277 def selectEnclosure(self):
278 if self.data is not None and self.enclosureCB is not None:
279 self.enclosureCB(self.data)
281 class RSSDisplay(Screen):
283 <screen position="100,100" size="460,400" title="Simple RSS Reader" >
284 <widget name="content" position="0,0" size="460,304" scrollbarMode="showOnDemand" />
285 <widget name="summary" position="0,305" size="460,95" font="Regular;16" />
291 def __init__(self, session, data, interactive = False, poller = None):
292 Screen.__init__(self, session)
295 self["actions"] = ActionMap([ "OkCancelActions", "ChannelSelectBaseActions", "MenuActions" ],
297 "ok": self.showCurrentEntry,
298 "cancel": self.conditionalClose,
299 "nextBouquet": self.next,
300 "prevBouquet": self.previous,
303 self.onShown.append(self.__show)
304 self.onClose.append(self.__close)
306 self.rssPoller = poller
307 self.feedview = False
311 if isinstance(data[0], Feed):
313 # TODO: find better way to solve this
314 self.feeds = ([(feed.title, feed.description, ' '.join([str(len(feed.history)), "Entries"]), feed.history) for feed in data])
315 self["content"] = RSSList(self.feeds)
316 self["summary"] = Label(self.feeds[0][2])
318 self["content"] = RSSList(data)
319 self["summary"] = Label(data[0][2])
321 self["content"] = RSSList(data)
322 self["summary"] = Label("")
324 self["content"].connectSelChanged(self.updateSummary)
325 self.onLayoutFinish.append(self.setConditionalTitle)
328 if rssPoller is not None:
329 self.rssPoller.addCallback(self.pollCallback)
332 if rssPoller is not None:
333 self.rssPoller.removeCallback(self.pollCallback)
335 def pollCallback(self, id = None):
336 print "[SimpleRSS] RSSDisplay called back"
337 current_entry = self["content"].getCurrentEntry()
341 print "[SimpleRSS] pollCallback updating feed", id
342 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)
344 print "[SimpleRSS] pollCallback updating all feeds"
345 self.feeds = ([(feed.title, feed.description, ' '.join([str(len(feed.history)), "Entries"]), feed.history) for feed in self.rssPoller.feeds])
348 print "[SimpleRSS] pollCallback updating Feedlist"
349 self["content"].l.setList(self.feeds)
351 print "[SimpleRSS] pollCallback updating Itemlist"
352 self["content"].l.setList(self.feeds[self.feedid][3])
354 self["content"].moveToEntry(current_entry)
355 self.setConditionalTitle()
358 def setConditionalTitle(self):
359 # Feedview: Overview, has feeds
361 self.setTitle("Simple RSS Reader")
362 # Feedid: Feed, has feeds
363 elif self.feedid is not None:
364 self.setTitle(''.join(["Simple RSS Reader: ", self.feeds[self.feedid][0]]))
367 self.setTitle("Simple RSS Reader: New Items")
369 def updateSummary(self):
370 current_entry = self["content"].getCurrentEntry()
372 self["summary"].setText(current_entry[2])
374 self["summary"].setText("")
377 self.session.openWithCallback(self.menuChoice, ChoiceBox, "What to do?", [(_("Update Feed"), self.MENU_UPDATE), (_("Setup"), self.MENU_CONFIG)])
379 def menuChoice(self, result):
381 if result[1] == self.MENU_UPDATE:
382 self.rssPoller.singlePoll(self.feedid or self["content"].getCurrentIndex(), self.pollCallback)
383 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)
384 elif result[1] == self.MENU_CONFIG:
385 self.session.openWithCallback(self.menuClosed, SimpleRSS)
387 def menuClosed(self):
389 current_entry = self["content"].getCurrentEntry()
391 self.rssPoller.triggerReload()
393 # TODO: fix this, its still as evil as some lines above
394 self.feeds = ([(feed.title, feed.description, ' '.join([str(len(feed.history)), " Entries"]), feed.history) for feed in rssPoller.feeds])
396 self["content"].l.setList(self.feeds)
398 self["content"].moveToEntry(current_entry)
400 def nextEntryCB(self):
401 self["content"].moveDown()
402 return self["content"].getCurrentEntry()
404 def previousEntryCB(self):
405 self["content"].moveUp()
406 return self["content"].getCurrentEntry()
409 if not self.feedview and self.feeds:
411 if self.feedid == len(self.feeds):
413 self["content"].l.setList(self.feeds[self.feedid][3])
414 self["content"].moveToIndex(0)
416 self.setConditionalTitle()
419 if not self.feedview and self.feeds:
421 self.feedid = len(self.feeds)
423 self["content"].l.setList(self.feeds[self.feedid][3])
424 self["content"].moveToIndex(0)
426 self.setConditionalTitle()
428 def conditionalClose(self):
429 if not self.feedview and self.feeds:
430 self["content"].l.setList(self.feeds)
431 self["content"].moveToIndex(self.feedid)
435 self.setConditionalTitle()
439 def showCurrentEntry(self):
440 current_entry = self["content"].getCurrentEntry()
441 if current_entry is None: # empty list
444 # If showing feeds right now show items of marked feed
446 self.feedid = self["content"].getCurrentIndex()
447 self["content"].l.setList(current_entry[3])
448 self["content"].moveToIndex(0)
449 self.feedview = False
451 self.setConditionalTitle()
452 # Else we're showing items -> show marked item
454 self.session.open(RSSView, current_entry, enclosureCB=self.selectEnclosure, nextCB=self.nextEntryCB, previousCB=self.previousEntryCB)
456 def selectEnclosure(self, current_entry = None):
457 if current_entry is None: # no entry given
458 current_entry = self["content"].getCurrentEntry()
460 if current_entry is None: # empty list
463 # Select stream in ChoiceBox if more than one present
464 if len(current_entry[3]) > 1:
466 self.session.openWithCallback(self.enclosureSelected, ChoiceBox, "Select enclosure to play", [(x[0][x[0].rfind("/")+1:], x) for x in current_entry[3]])
467 # Play if one present
468 elif len(current_entry[3]):
469 self.enclosureSelected((None, current_entry[3][0]))
470 # Nothing if none present
472 def enclosureSelected(self, enclosure):
474 (url, type) = enclosure[1]
476 print "[SimpleRSS] Trying to play back enclosure: url=%s, type=%s" % (url, type)
478 # TODO: other types? (showing images wouldn't be hard if the source was local)
479 if type in ["video/mpeg", "audio/mpeg"]:
480 # We should launch a Player here, but the MediaPlayer gets angry about our non-local sources
481 from enigma import eServiceReference
482 self.session.nav.playService(eServiceReference(4097, 0, url))
485 MAX_HISTORY_ELEMENTS = 100
490 def __init__(self, uri, autoupdate):
492 self.autoupdate = autoupdate
494 self.title = uri.encode("UTF-8")
495 self.description = ""
496 self.last_update = None
497 self.last_ids = set()
500 def gotDom(self, dom):
501 if self.type is None:
503 if dom.documentElement.getAttribute("version") in ["2.0", "0.94", "0.93", "0.92", "0.91"]:
506 self.title = dom.getElementsByTagName("channel")[0].getElementsByTagName("title")[0].childNodes[0].data.encode("UTF-8")
507 self.description = dom.getElementsByTagName("channel")[0].getElementsByTagName("description")[0].childNodes[0].data.encode("UTF-8")
510 # RSS 1.0 (NS: http://www.w3.org/1999/02/22-rdf-syntax-ns#)
511 elif dom.documentElement.localName == "RDF":
514 self.title = dom.getElementsByTagName("channel")[0].getElementsByTagName("title")[0].childNodes[0].data.encode("UTF-8")
515 self.description = dom.getElementsByTagName("channel")[0].getElementsByTagName("description")[0].childNodes[0].data.encode("UTF-8")
518 # Atom (NS: http://www.w3.org/2005/Atom)
519 elif dom.documentElement.localName == "feed":
520 self.type = self.ATOM
522 self.title = dom.getElementsByTagName("title")[0].childNodes[0].data.encode("UTF-8")
523 self.description = dom.getElementsByTagName("subtitle")[0].childNodes[0].data.encode("UTF-8")
527 raise NotImplementedError, 'Unsupported Feed: %s' % dom.documentElement.localName
528 if self.type == self.RSS:
529 print "[SimpleRSS] type is rss"
530 return self.gotRSSDom(dom)
531 elif self.type == self.ATOM:
532 print "[SimpleRSS] type is atom"
533 return self.gotAtomDom(dom)
535 def gotRSSDom(self, dom):
536 # Try to read when feed was last updated, if time equals return empty list. else fetch new items
538 updated = dom.getElementsByTagName("lastBuildDate")[0].childNodes[0].data
539 if not self.last_update == updated:
540 self.last_update = updated
541 return self.parseRSS(dom.getElementsByTagName("item"))
545 return self.parseRSS(dom.getElementsByTagName("item"))
547 def parseRSS(self, items):
552 # Try to read title, continue if none found
554 title = item.getElementsByTagName("title")[0].childNodes[0].data
558 # Try to read link, empty if none
560 link = item.getElementsByTagName("link")[0].childNodes[0].data
564 # Try to read guid, link if none (RSS 1.0 or invalid RSS 2.0)
566 guid = item.getElementsByTagName("guid")[0].childNodes[0].data
570 # Continue if item is to be excluded
571 if guid in self.last_ids:
574 # Try to read summary (description element), empty if none
576 summary = item.getElementsByTagName("description")[0].childNodes[0].data
580 # Read out enclosures
581 for current in item.getElementsByTagName("enclosure"):
582 enclosure.append((current.getAttribute("url").encode("UTF-8"), current.getAttribute("type").encode("UTF-8")))
585 new_items.append((title.encode("UTF-8").strip(), link.encode("UTF-8").strip(), summary.encode("UTF-8").strip(), enclosure))
586 self.last_ids.add(guid)
588 # Append known Items to new Items and evenentually cut it
589 self.history = new_items + self.history
590 self.history[:self.MAX_HISTORY_ELEMENTS]
594 def gotAtomDom(self, dom):
596 # Try to read when feed was last updated, if time equals return empty list. else fetch new items
597 updated = dom.getElementsByTagName("updated")[0].childNodes[0].data
598 if not self.last_update == updated:
599 self.last_update = updated
600 return self.parseAtom(dom.getElementsByTagName("entry"))
604 return self.parseAtom(dom.getElementsByTagName("entry"))
606 def parseAtom(self, items):
612 # Try to read title, continue if none found
614 title = item.getElementsByTagName("title")[0].childNodes[0].data
618 # Try to read id, continue if none found (invalid feed, should be handled differently) or to be excluded
620 id = item.getElementsByTagName("id")[0].childNodes[0].data
621 if id in self.last_ids:
626 # Read out enclosures and link
627 for current in item.getElementsByTagName("link"):
629 if current.getAttribute("rel") == "enclosure":
630 enclosure.append((current.getAttribute("href").encode("UTF-8"), current.getAttribute("type").encode("UTF-8")))
631 # No Enclosure, assume its a link to the item
633 link = current.getAttribute("href")
635 # Try to read summary, empty if none
637 summary = item.getElementsByTagName("summary")[0].childNodes[0].data
642 new_items.append((title.encode("UTF-8").strip(), link.encode("UTF-8").strip(), summary.encode("UTF-8").strip(), enclosure))
643 self.last_ids.add(id)
645 # Append known Items to new Items and evenentually cut it
646 self.history = new_items + self.history
647 self.history[:self.MAX_HISTORY_ELEMENTS]
652 def __init__(self, session):
653 self.poll_timer = eTimer()
654 self.poll_timer.timeout.get().append(self.poll)
655 self.poll_timer.start(0, 1)
656 self.update_callbacks = [ ]
657 self.last_links = Set()
658 self.session = session
660 self.reloading = False
663 for i in range(0, config.plugins.simpleRSS.feedcount.value):
664 self.feeds.append(Feed(config.plugins.simpleRSS.feed[i].uri.value, config.plugins.simpleRSS.feed[i].autoupdate.value))
666 self.current_feed = 0
668 def addCallback(self, callback):
669 if callback not in self.update_callbacks:
670 self.update_callbacks.append(callback)
672 def removeCallback(self, callback):
673 if callback in self.update_callbacks:
674 self.update_callbacks.remove(callback)
676 def doCallback(self):
677 for callback in self.update_callbacks:
683 # Single Functions are here to wrap
684 def _gotSinglePage(self, id, callback, errorback, data):
685 self._gotPage(data, id, callback, errorback)
687 def singleError(self, errorback, error):
688 self.error(error, errorback)
690 def error(self, error, errorback = None):
692 print "[SimpleRSS] error polling"
696 self.session.open(MessageBox, "Sorry, failed to fetch feed.\n" + error, type = MessageBox.TYPE_INFO, timeout = 5)
697 # Assume its just a temporary failure and jump over to next feed
698 self.current_feed += 1
699 self.poll_timer.start(1000, 1)
701 def _gotPage(self, data, id = None, callback = None, errorback = None):
702 # workaround: exceptions in gotPage-callback were ignored
704 self.gotPage(data, id)
705 if callback is not None:
707 except NotImplementedError, errmsg:
708 # TODO: Annoying with Multifeed?
709 self.session.open(MessageBox, "Sorry, this type of feed is unsupported.\n"+ str(errmsg), type = MessageBox.TYPE_INFO, timeout = 5)
711 import traceback, sys
712 traceback.print_exc(file=sys.stdout)
713 if errorback is not None:
715 # Assume its just a temporary failure and jump over to next feed
716 self.current_feed += 1
717 self.poll_timer.start(1000, 1)
719 def gotPage(self, data, id = None):
720 print "[SimpleRSS] parsing.."
722 # sometimes activates spinner :-/
723 dom = xml.dom.minidom.parseString(data)
725 print "[SimpleRSS] xml parsed.."
729 self.feeds[id].gotDom(dom)
730 print "[SimpleRSS] single feed parsed.."
733 new_items = self.feeds[self.current_feed].gotDom(dom)
735 print "[SimpleRSS] feed parsed.."
737 # Append new items to locally bound ones
738 self.new_items.extend(new_items)
740 # Start Timer so we can either fetch next feed or show new_items
741 self.current_feed += 1
742 self.poll_timer.start(1000, 1)
745 def singlePoll(self, id, callback = None, errorback = None):
746 from Tools.BoundFunction import boundFunction
747 remote = urlsplit(self.feeds[id].uri)
748 print "[SimpleRSS] updating", remote.geturl()
749 hostname = remote.hostname
750 port = remote.port or 80
751 path = '?'.join([remote.path, remote.query])
752 print "[SimpleRSS] hostname:", hostname, ", port:", port, ", path:", path
753 getPage(hostname, port, path, callback=boundFunction(self._gotSinglePage, id, callback, errorback), errorback=boundFunction(self.error, errorback))
756 # Reloading, reschedule
758 print "[SimpleRSS] timer triggered while reloading, rescheduling"
759 self.poll_timer.start(10000, 1)
762 print "[SimpleRSS] hiding"
766 self.current_feed = 0
767 self.poll_timer.startLongTimer(config.plugins.simpleRSS.interval.value*60)
769 elif len(self.feeds) <= self.current_feed:
771 if len(self.new_items):
772 print "[SimpleRSS] got", len(self.new_items), "new items"
773 print "[SimpleRSS] calling back"
776 if config.plugins.simpleRSS.show_new.value:
777 self.dialog = self.session.instantiateDialog(RSSDisplay, self.new_items, poller = self)
779 self.poll_timer.startLongTimer(5)
782 print "[SimpleRSS] no new items"
784 self.current_feed = 0
785 self.poll_timer.startLongTimer(config.plugins.simpleRSS.interval.value*60)
786 # Feed is supposed to auto-update
787 elif self.feeds[self.current_feed].autoupdate:
788 remote = urlsplit(self.feeds[self.current_feed].uri)
789 hostname = remote.hostname
790 port = remote.port or 80
791 path = '?'.join([remote.path, remote.query])
792 print "[SimpleRSS] hostname:", hostname, ", port:", port, ", path:", path
793 self.d = getPage(hostname, port, path, callback=self._gotPage, errorback=self.error)
794 # Go to next feed in 100ms
796 print "[SimpleRSS] passing feed"
797 self.current_feed += 1
798 self.poll_timer.start(100, 1)
801 self.poll_timer.timeout.get().remove(self.poll)
802 self.poll_timer = None
804 def triggerReload(self):
805 self.reloading = True
807 # TODO: Fix this evil way of updating feeds
809 for i in range(0, config.plugins.simpleRSS.feedcount.value):
810 newfeeds.append(Feed(config.plugins.simpleRSS.feed[i].uri.value, config.plugins.simpleRSS.feed[i].autoupdate.value))
812 self.feeds = newfeeds
814 self.reloading = False
816 def main(session, **kwargs):
817 print "[SimpleRSS] Displaying SimpleRSS-Setup"
818 session.open(SimpleRSS)
822 def autostart(reason, **kwargs):
825 # not nice (?), but works
826 if kwargs.has_key("session") and reason == 0:
827 rssPoller = RSSPoller(kwargs["session"])
832 def showCurrent(session, **kwargs):
834 if rssPoller is None:
836 session.open(RSSDisplay, rssPoller.feeds, interactive = True, poller = rssPoller)
838 def Plugins(**kwargs):
839 return [ PluginDescriptor(name="RSS Reader", description="A (really) simple RSS reader", where = PluginDescriptor.WHERE_PLUGINMENU, fnc=main),
840 PluginDescriptor(where = [PluginDescriptor.WHERE_SESSIONSTART, PluginDescriptor.WHERE_AUTOSTART], fnc = autostart),
841 PluginDescriptor(name="View RSS", description="Let's you view current RSS entries", where = PluginDescriptor.WHERE_EXTENSIONSMENU, fnc=showCurrent) ]