New Items displayed in Feeds,
[enigma2-plugins.git] / simplerss / src / RSSScreens.py
1 from Screens.Screen import Screen
2 from Screens.MessageBox import MessageBox
3 from Screens.ChoiceBox import ChoiceBox
4 from Components.ActionMap import ActionMap
5 from Components.Label import Label
6 from Components.ScrollLabel import ScrollLabel
7
8 from RSSList import RSSList
9 from RSSSetup import RSSSetup
10
11 class RSSBaseView(Screen):
12         """Base Screen for all Screens used in SimpleRSS"""
13
14         def __init__(self, session):
15                 Screen.__init__(self, session)
16
17         def errorPolling(self):
18                 self.session.open(
19                         MessageBox,
20                         "Error while parsing Feed, this usually means there is something wrong with it.",
21                         type = MessageBox.TYPE_ERROR,
22                         timeout = 5
23                 )
24
25         def singleUpdate(self, feedid, errback = None):
26                 # Default errorback to self.errorPolling
27                 # If an empty errorback is wanted the Screen needs to provide it
28                 if errback is None:
29                         errback = self.errorPolling
30                 self.rssPoller.singlePoll(feedid, callback=True, errorback=errback)
31                 self.session.open(
32                         MessageBox,
33                         "Update is being done in Background.\nContents will automatically be updated when it's done.",
34                         type = MessageBox.TYPE_INFO,
35                         timeout = 5
36                 )
37
38         def selectEnclosure(self, enclosures):
39                 # Empty List
40                 if enclosures is None:
41                         return
42                 # Select stream in ChoiceBox if more than one present
43                 elif len(enclosures) > 1:
44                         self.session.openWithCallback(
45                                 self.enclosureSelected,
46                                 ChoiceBox,
47                                 "Select enclosure to play",
48                                 [(x[0][x[0].rfind("/")+1:].replace('%20', ' '), x) for x in enclosures]
49                         )
50                 # Play if one present
51                 else:
52                         self.enclosureSelected((None, enclosures[0]))
53
54         def enclosureSelected(self, enclosure):
55                 if enclosure:
56                         (url, type) = enclosure[1]
57
58                         print "[SimpleRSS] Trying to play back enclosure: url=%s, type=%s" % (url, type)
59
60                         # TODO: other types? (showing images wouldn't be hard if the source was local)
61                         if type in ["video/mpeg", "audio/mpeg"]:
62                                 # We should launch a Player here, but the MediaPlayer gets angry about our non-local sources
63                                 from enigma import eServiceReference
64                                 self.session.nav.playService(eServiceReference(4097, 0, url))
65
66 class RSSEntryView(RSSBaseView):
67         """Shows a RSS Item"""
68         skin = """
69                 <screen position="100,100" size="460,400" title="Simple RSS Reader" >
70                         <widget name="content" position="0,0" size="460,400" font="Regular; 22" />
71                 </screen>"""
72
73         def __init__(self, session, data, feedTitle="", nextEntryCB=None, previousEntryCB=None, nextFeedCB=None, previousFeedCB=None):
74                 RSSBaseView.__init__(self, session)
75
76                 self.data = data
77                 self.feedTitle = feedTitle
78                 self.nextEntryCB = nextEntryCB
79                 self.previousEntryCB = previousEntryCB
80                 self.nextFeedCB = nextFeedCB
81                 self.previousFeedCB = previousFeedCB
82
83                 if data is not None:
84                         self["content"] = ScrollLabel("\n\n".join([data[0], data[2], " ".join([str(len(data[3])), "Enclosures"])]))
85                 else:
86                         self["content"] = ScrollLabel()
87
88                 self["actions"] = ActionMap([ "OkCancelActions", "ChannelSelectBaseActions", "ColorActions", "DirectionActions" ],
89                 {
90                         "cancel": self.close,
91                         "ok": self.selectEnclosure,
92                         "yellow": self.selectEnclosure,
93                         "up": self.up,
94                         "down": self.down,
95                         "right": self.next,
96                         "left": self.previous,
97                         "nextBouquet": self.nextFeed,
98                         "prevBouquet": self.previousFeed,
99                 })
100
101                 self.onLayoutFinish.append(self.setConditionalTitle)
102
103         def setConditionalTitle(self):
104                 self.setTitle(': '.join(["Simple RSS Reader", self.feedTitle]))
105
106         def up(self):
107                 self["content"].pageUp()
108
109         def down(self):
110                 self["content"].pageDown()
111
112         def next(self):
113                 if self.nextEntryCB is not None:
114                         self.data = self.nextEntryCB()
115                         self.setContent()
116
117         def previous(self):
118                 if self.previousEntryCB is not None:
119                         self.data = self.previousEntryCB()
120                         self.setContent()
121
122         def nextFeed(self):
123                 # Show next Feed
124                 if self.nextFeedCB is not None:
125                         result = self.nextFeedCB()
126                         self.feedTitle = result[0]
127                         if len(result[1]):
128                                 self.data = result[1][0]
129                         else:
130                                 self.data = None
131                         self.setConditionalTitle()
132                         self.setContent()
133
134         def previousFeed(self):
135                 # Show previous Feed
136                 if self.previousFeedCB is not None:
137                         result = self.previousFeedCB()
138                         self.feedTitle = result[0]
139                         if len(result[1]):
140                                 self.data = result[1][0]
141                         else:
142                                 self.data = None
143                         self.setConditionalTitle()
144                         self.setContent()
145
146         def setContent(self):
147                 if self.data is not None:
148                         self["content"].setText("\n\n".join([self.data[0], self.data[2], " ".join([str(len(self.data[3])), "Enclosures"])]))
149                 else:
150                         self["content"].setText("No such Item.")
151
152         def selectEnclosure(self):
153                 if self.data is not None:
154                         RSSBaseView.selectEnclosure(self, self.data[3])
155
156 class RSSFeedView(RSSBaseView):
157         """Shows a RSS-Feed"""
158         skin = """
159                 <screen position="100,100" size="460,400" title="Simple RSS Reader" >
160                         <widget name="content" position="0,0" size="460,304" scrollbarMode="showOnDemand" />
161                         <widget name="summary" position="0,305" size="460,95" font="Regular;16" />
162                 </screen>"""
163
164         def __init__(self, session, data, feedTitle = "", newItems=False, nextFeedCB=None, previousFeedCB=None, rssPoller=None, id = None):
165                 RSSBaseView.__init__(self, session)
166
167                 self.data = data
168                 self.feedTitle = feedTitle
169                 self.newItems = newItems
170                 self.id = id
171                 self.nextFeedCB=nextFeedCB
172                 self.previousFeedCB=previousFeedCB
173                 self.rssPoller=rssPoller
174
175                 self["content"] = RSSList(data)
176                 self["summary"] = Label()
177
178                 if not newItems:
179                         self["actions"] = ActionMap([ "OkCancelActions", "ChannelSelectBaseActions", "MenuActions", "ColorActions" ], 
180                         {
181                                 "ok": self.showCurrentEntry,
182                                 "cancel": self.close,
183                                 "nextBouquet": self.next,
184                                 "prevBouquet": self.previous,
185                                 "menu": self.menu,
186                                 "yellow": self.selectEnclosure,
187                         })
188                         self.onShown.append(self.__show)
189                         self.onClose.append(self.__close)
190                 
191                 self["content"].connectSelChanged(self.updateSummary)
192                 self.onLayoutFinish.extend([self.setConditionalTitle, self.updateSummary])
193
194         def __show(self):
195                 self.rssPoller.addCallback(self.pollCallback)
196
197         def __close(self):
198                 self.rssPoller.removeCallback(self.pollCallback)
199
200         def pollCallback(self, id = None):
201                 print "[SimpleRSS] SimpleRSSFeed called back"
202                 current_entry = self["content"].getCurrentEntry()
203
204                 if id is not None and self.id == id+1:
205                         print "[SimpleRSS] pollCallback recieved local feed", self.id
206                         self.feedTitle = self.rssPoller.feeds[id].title
207                         self.data = self.rssPoller.feeds[id].history
208                 elif self.id == 0:
209                         print "[SimpleRSS] pollCallback recieved all or non-local feed, updating active view (new_items)"
210                         self.data = self.rssPoller.new_items
211                 else:
212                         print "[SimpleRSS] pollCallback recieved all or non-local feed, updating", self.id
213                         self.feedTitle = self.rssPoller.feeds[self.id-1].title
214                         self.data = self.rssPoller.feeds[self-id-1].history
215
216                 self["content"].l.setList(self.data)
217                 self["content"].moveToEntry(current_entry)
218
219                 self.setConditionalTitle()
220                 self.updateSummary()
221
222         def setConditionalTitle(self):
223                 if not self.newItems:
224                         self.setTitle(': '.join(["Simple RSS Reader", self.feedTitle]))
225                 else:
226                         self.setTitle("Simple RSS Reader: New Items")
227
228         def updateSummary(self):
229                 current_entry = self["content"].getCurrentEntry()
230                 if current_entry:
231                         self["summary"].setText(current_entry[2])
232                 else:
233                         self["summary"].setText("Feed is empty.")
234
235         def menu(self):
236                 if self.id > 0:
237                         self.singleUpdate(self.id-1)
238
239         def nextEntryCB(self):
240                 self["content"].moveDown()
241                 return self["content"].getCurrentEntry()
242
243         def previousEntryCB(self):
244                 self["content"].moveUp()
245                 return self["content"].getCurrentEntry()
246
247         def next(self):
248                 # Show next Feed
249                 if self.nextFeedCB is not None:
250                         result = self.nextFeedCB()
251                         (self.feedTitle, self.data, self.id) = result
252                         current_entry = self["content"].getCurrentEntry()
253                         self["content"].l.setList(self.data) # Update list
254                         self["content"].moveToEntry(current_entry)
255                         self.updateSummary() # In case entry is no longer in history
256                         self.setConditionalTitle() # Update title
257                         return result
258                 return (self.feedTitle, self.data, self.id)
259
260         def previous(self):
261                 # Show previous Feed
262                 if self.previousFeedCB is not None:
263                         result = self.previousFeedCB()
264                         (self.feedTitle, self.data, self.id) = result
265                         current_entry = self["content"].getCurrentEntry()
266                         self["content"].l.setList(self.data) # Update list
267                         self["content"].moveToEntry(current_entry)
268                         self.updateSummary() # In case entry is no longer in history
269                         self.setConditionalTitle() # Update title
270                         return result
271                 return (self.feedTitle, self.data, self.id)
272
273         def showCurrentEntry(self):
274                 current_entry = self["content"].getCurrentEntry()
275                 if current_entry is None: # empty list
276                         return
277
278                 self.session.openWithCallback(
279                         self.updateSummary,
280                         RSSEntryView,
281                         current_entry,
282                         feedTitle=self.feedTitle,
283                         nextEntryCB=self.nextEntryCB,
284                         previousEntryCB=self.previousEntryCB,
285                         nextFeedCB=self.next,
286                         previousFeedCB=self.previous
287                 )
288
289         def selectEnclosure(self):
290                 current_entry = self["content"].getCurrentEntry()
291                 if current_entry is None: # empty list
292                         return
293
294                 RSSBaseView.selectEnclosure(self, current_entry[3])
295
296 class RSSOverview(RSSBaseView):
297         """Shows an Overview over all RSS-Feeds known to rssPoller"""
298         skin = """
299                 <screen position="100,100" size="460,400" title="Simple RSS Reader" >
300                         <widget name="content" position="0,0" size="460,304" scrollbarMode="showOnDemand" />
301                         <widget name="summary" position="0,305" size="460,95" font="Regular;16" />
302                 </screen>"""
303
304         def __init__(self, session, poller):
305                 RSSBaseView.__init__(self, session)
306
307                 self.rssPoller = poller
308                 
309                 self["actions"] = ActionMap([ "OkCancelActions", "MenuActions", "ColorActions" ], 
310                 {
311                         "ok": self.showCurrentEntry,
312                         "cancel": self.close,
313                         "menu": self.menu,
314                         "yellow": self.selectEnclosure,
315                 })
316
317                 self.fillFeeds()
318
319                 # We always have at least "New Items"-Feed
320                 self["content"] = RSSList(self.feeds)
321                 self["summary"] = Label(self.feeds[0][2])
322
323                 self["content"].connectSelChanged(self.updateSummary)
324                 self.onShown.append(self.__show)
325                 self.onClose.append(self.__close)
326
327         def __show(self):
328                 self.rssPoller.addCallback(self.pollCallback)
329
330         def __close(self):
331                 self.rssPoller.removeCallback(self.pollCallback)
332
333         def fillFeeds(self):
334                 self.feeds = [(
335                         "New Items",
336                         "New Items since last Auto-Update",
337                         ' '.join([str(len(self.rssPoller.new_items)), "Entries"]),
338                         self.rssPoller.new_items
339                 )]
340                 self.feeds.extend([
341                         (
342                                 feed.title,
343                                 feed.description,
344                                 ' '.join([str(len(feed.history)), "Entries"]),
345                                 feed.history
346                         )
347                                 for feed in self.rssPoller.feeds
348                 ])
349
350         def pollCallback(self, id = None):
351                 print "[SimpleRSS] SimpleRSS called back"
352                 current_entry = self["content"].getCurrentEntry()
353
354                 if id is not None:
355                         print "[SimpleRSS] pollCallback updating feed", id
356                         self.feeds[id+1] = (
357                                 self.rssPoller.feeds[id].title,
358                                 self.rssPoller.feeds[id].description,
359                                 ' '.join([str(len(self.rssPoller.feeds[id].history)), "Entries"]),
360                                 self.rssPoller.feeds[id].history
361                         )
362                 else:
363                         print "[SimpleRSS] pollCallback updating all feeds"
364                         self.fillFeeds()
365
366                 self["content"].l.setList(self.feeds)
367                 self["content"].moveToEntry(current_entry)
368
369                 self.updateSummary()
370
371         def updateSummary(self):
372                 current_entry = self["content"].getCurrentEntry()
373                 if current_entry:
374                         self["summary"].setText(current_entry[2])
375                 else:
376                         self["summary"].setText("")
377
378         def menu(self):
379                 cur_idx = self["content"].getCurrentIndex()
380                 if cur_idx > 0:
381                         possible_actions = [
382                                 (_("Update Feed"), "update"),
383                                 (_("Setup"), "setup"),
384                                 (_("Close"), "close")
385                         ]
386                 else:
387                         possible_actions = [
388                                 (_("Setup"), "setup"),
389                                 (_("Close"), "close")
390                         ]
391                 self.session.openWithCallback(
392                         self.menuChoice,
393                         ChoiceBox,
394                         "What to do?",
395                         possible_actions
396                 )
397
398         def menuChoice(self, result):
399                 if result:
400                         if result[1] == "update":
401                                 cur_idx = self["content"].getCurrentIndex()
402                                 if cur_idx > 0:
403                                         self.singleUpdate(cur_idx-1)
404                         elif result[1] == "setup":
405                                 self.session.openWithCallback(self.refresh, RSSSetup, rssPoller=self.rssPoller)
406                         elif result[1] == "close":
407                                 self.close()
408
409         def refresh(self):
410                 current_entry = self["content"].getCurrentEntry()
411
412                 self.fillFeeds()
413                 self["content"].l.setList(self.feeds)
414
415                 self["content"].moveToEntry(current_entry)
416                 self.updateSummary()
417
418         def nextFeedCB(self):
419                 self["content"].moveUp()
420                 current_entry = self["content"].getCurrentEntry()
421                 return (current_entry[0], current_entry[3], self["content"].getCurrentIndex())
422
423         def previousFeedCB(self):
424                 self["content"].moveDown()
425                 current_entry = self["content"].getCurrentEntry()
426                 return (current_entry[0], current_entry[3], self["content"].getCurrentIndex())
427
428         def showCurrentEntry(self):
429                 current_entry = self["content"].getCurrentEntry()
430                 if current_entry is None: # empty list
431                         return
432
433                 self.session.openWithCallback(
434                         self.refresh,
435                         RSSFeedView,
436                         current_entry[3],
437                         feedTitle=current_entry[0],
438                         nextFeedCB=self.nextFeedCB,
439                         previousFeedCB=self.previousFeedCB,
440                         rssPoller=self.rssPoller,
441                         id=self["content"].getCurrentIndex()
442                 )
443
444         def selectEnclosure(self):
445                 current_entry = self["content"].getCurrentEntry()
446                 if current_entry is None: # empty list
447                         return
448
449                 # Build a list of all enclosures in this feed
450                 enclosures = []
451                 for entry in current_entry[3]:
452                                 enclosures.extend(entry[3])
453                 RSSBaseView.selectEnclosure(self, enclosures)