Fix showing error-message in errorPolling,
[enigma2-plugins.git] / simplerss / src / RSSScreens.py
1 from enigma import eTimer
2
3 from Screens.Screen import Screen
4 from Screens.MessageBox import MessageBox
5 from Screens.ChoiceBox import ChoiceBox
6 from Components.ActionMap import ActionMap
7 from Components.Label import Label
8 from Components.ScrollLabel import ScrollLabel
9 from Components.Pixmap import Pixmap
10
11 from RSSList import RSSList
12 from RSSSetup import RSSSetup
13
14 class PictureView(Screen):
15         """Downloads a Picture, shows it and delete the temporary file"""
16
17         skin = """
18                 <screen position="100,100" size="460,400" title="Simple RSS Reader" >
19                         <widget name="content" position="0,0" size="460,400" alphatest="on"/>
20                 </screen>"""
21
22         filename = '/tmp/simplerss_enclosure'
23
24         def __init__(self, session, url):
25                 Screen.__init__(self, session)
26
27                 self.url = url
28
29                 self["actions"] = ActionMap([ "OkCancelActions" ], 
30                 {
31                         "ok": self.close,
32                         "cancel": self.close,
33                 })
34
35                 self["content"] = Pixmap()
36
37                 self.onLayoutFinish.append(self.fetchFile)
38
39         def fetchFile(self):
40                 # Fetch file
41                 from twisted.web.client import downloadPage
42                 downloadPage(self.url, self.filename).addCallback(self.gotFile).addErrback(self.error)
43
44         def gotFile(self, data = ""):
45                 # Determine Aspect
46                 from Components.AVSwitch import AVSwitch
47                 aspect = AVSwitch().getAspectRatioSetting()/2
48
49                 # Load Picture
50                 from enigma import loadPic
51                 ptr = loadPic(self.filename, 460, 400, aspect)
52
53                 # Show Picture
54                 self["content"].instance.setPixmap(ptr)
55
56                 # Remove Temporary File
57                 from os import unlink
58                 unlink(self.filename)
59
60         def error(self):
61                 self.session.open(
62                         MessageBox,
63                         "Error while loading Picture.",
64                         type = MessageBox.TYPE_ERROR,
65                         timeout = 3
66                 )
67                 self.close()
68
69 class RSSBaseView(Screen):
70         """Base Screen for all Screens used in SimpleRSS"""
71
72         def __init__(self, session, poller):
73                 Screen.__init__(self, session)
74                 self.rssPoller = poller
75                 self.pollDialog = None
76
77         def errorPolling(self, errmsg = ""):
78                 # An error occured while polling
79                 self.session.open(
80                         MessageBox,
81                         "Error while parsing Feed, this usually means there is something wrong with it.",
82                         type = MessageBox.TYPE_ERROR,
83                         timeout = 3
84                 )
85
86                 # Don't show "we're updating"-dialog any longer
87                 if self.pollDialog:
88                         self.pollDialog.close()
89                         self.pollDialog = None
90
91         def singleUpdate(self, feedid, errback = None):
92                 # Don't do anything if we have no poller
93                 if self.rssPoller is None:
94                         return
95
96                 # Default errorback to self.errorPolling
97                 # If an empty errorback is wanted the Screen needs to provide it
98                 if errback is None:
99                         errback = self.errorPolling
100
101                 # Tell Poller to poll
102                 self.rssPoller.singlePoll(feedid, callback=True, errorback=errback)
103
104                 # Open Dialog and save locally
105                 self.pollDialog = self.session.open(
106                         MessageBox,
107                         "Update is being done in Background.\nContents will automatically be updated when it's done.",
108                         type = MessageBox.TYPE_INFO,
109                         timeout = 5
110                 )
111
112         def selectEnclosure(self, enclosures):
113                 # Empty List
114                 if enclosures is None:
115                         return
116
117                 count = len(enclosures)
118                 # Select stream in ChoiceBox if more than one present
119                 if count > 1:
120                         self.session.openWithCallback(
121                                 self.enclosureSelected,
122                                 ChoiceBox,
123                                 "Select enclosure to play",
124                                 [(x[0][x[0].rfind("/")+1:].replace('%20', ' ').replace('%5F', '_').replace('%2D', '-'), x) for x in enclosures]
125                         )
126                 # Play if one present
127                 elif count:
128                         self.enclosureSelected((None, enclosures[0]))
129
130         def enclosureSelected(self, enclosure):
131                 if enclosure:
132                         (url, type) = enclosure[1]
133
134                         print "[SimpleRSS] Trying to play back enclosure: url=%s, type=%s" % (url, type)
135
136                         if type in ["video/mpeg", "audio/mpeg"]:
137                                 from enigma import eServiceReference
138                                 from Screens.MediaPlayer import MediaPlayer
139
140                                 mp = self.session.open(MediaPlayer)
141                                 ref = eServiceReference(4097, 0, url)
142
143                                 mp.switchToPlayList()
144                                 mp.playlist.addFile(ref)
145                                 mp.playlist.updateList()
146
147                                 mp.playServiceRefEntry(ref)
148                         elif type in ["image/jpeg", "image/png", "image/gif", "image/bmp"]:
149                                 self.session.open(PictureView, url)
150
151 class RSSEntryView(RSSBaseView):
152         """Shows a RSS Item"""
153         skin = """
154                 <screen position="100,100" size="460,420" title="Simple RSS Reader" >
155                         <widget name="info" position="0,0" size="460, 20" halign="right" font="Regular; 18" />
156                         <widget name="content" position="0,20" size="460,420" font="Regular; 22" />
157                 </screen>"""
158
159         def __init__(self, session, data, feedTitle="", cur_idx=None, entries=None, nextEntryCB=None, previousEntryCB=None, nextFeedCB=None, previousFeedCB=None):
160                 RSSBaseView.__init__(self, session, None)
161
162                 self.data = data
163                 self.feedTitle = feedTitle
164                 self.nextEntryCB = nextEntryCB
165                 self.previousEntryCB = previousEntryCB
166                 self.nextFeedCB = nextFeedCB
167                 self.previousFeedCB = previousFeedCB
168                 self.cur_idx = cur_idx
169                 self.entries = entries
170
171                 if cur_idx is not None and entries is not None:
172                         self["info"] = Label("Entry %s/%s" % (cur_idx+1, entries))
173                 else:
174                         self["info"] = Label()
175
176                 if data is not None:
177                         self["content"] = ScrollLabel("\n\n".join([data[0], data[2], " ".join([str(len(data[3])), "Enclosures"])]))
178                 else:
179                         self["content"] = ScrollLabel()
180
181                 self["actions"] = ActionMap([ "OkCancelActions", "ChannelSelectBaseActions", "ColorActions", "DirectionActions" ],
182                 {
183                         "cancel": self.close,
184                         "ok": self.selectEnclosure,
185                         "yellow": self.selectEnclosure,
186                         "up": self.up,
187                         "down": self.down,
188                         "right": self.next,
189                         "left": self.previous,
190                         "nextBouquet": self.nextFeed,
191                         "prevBouquet": self.previousFeed,
192                 })
193
194                 self.onLayoutFinish.append(self.setConditionalTitle)
195
196         def setConditionalTitle(self):
197                 self.setTitle(': '.join(["Simple RSS Reader", self.feedTitle]))
198
199         def up(self):
200                 self["content"].pageUp()
201
202         def down(self):
203                 self["content"].pageDown()
204
205         def next(self):
206                 if self.nextEntryCB is not None:
207                         (self.data, self.cur_idx, self.entries) = self.nextEntryCB()
208                         self.setContent()
209
210         def previous(self):
211                 if self.previousEntryCB is not None:
212                         (self.data, self.cur_idx, self.entries) = self.previousEntryCB()
213                         self.setContent()
214
215         def nextFeed(self):
216                 # Show next Feed
217                 if self.nextFeedCB is not None:
218                         result = self.nextFeedCB()
219                         self.feedTitle = result[0]
220                         self.entries = len(result[1])
221                         if self.entries:
222                                 self.cur_idx = 0
223                                 self.data = result[1][0]
224                         else:
225                                 self.cur_idx = None
226                                 self.data = None
227                         self.setConditionalTitle()
228                         self.setContent()
229
230         def previousFeed(self):
231                 # Show previous Feed
232                 if self.previousFeedCB is not None:
233                         result = self.previousFeedCB()
234                         self.feedTitle = result[0]
235                         self.entries = len(result[1])
236                         if self.entries:
237                                 self.cur_idx = 0
238                                 self.data = result[1][0]
239                         else:
240                                 self.cur_idx = None
241                                 self.data = None
242                         self.setConditionalTitle()
243                         self.setContent()
244
245         def setContent(self):
246                 if self.cur_idx is not None and self.entries is not None:
247                         self["info"].setText("Entry %s/%s" % (self.cur_idx+1, self.entries))
248                 else:
249                         self["info"].setText("")
250                 if self.data is not None:
251                         self["content"].setText("\n\n".join([self.data[0], self.data[2], " ".join([str(len(self.data[3])), "Enclosures"])]))
252                 else:
253                         self["content"].setText("No such Item.")
254
255         def selectEnclosure(self):
256                 if self.data is not None:
257                         RSSBaseView.selectEnclosure(self, self.data[3])
258
259 class RSSFeedView(RSSBaseView):
260         """Shows a RSS-Feed"""
261         skin = """
262                 <screen position="100,100" size="460,415" title="Simple RSS Reader" >
263                         <widget name="info" position="0,0" size="460,20" halign="right" font="Regular; 18" />
264                         <widget name="content" position="0,20" size="460,300" scrollbarMode="showOnDemand" />
265                         <widget name="summary" position="0,320" size="460,95" font="Regular;16" />
266                 </screen>"""
267
268         def __init__(self, session, data, feedTitle = "", newItems=False, nextFeedCB=None, previousFeedCB=None, rssPoller=None, id = None):
269                 RSSBaseView.__init__(self, session, rssPoller)
270
271                 self.data = data
272                 self.feedTitle = feedTitle
273                 self.newItems = newItems
274                 self.id = id
275                 self.nextFeedCB=nextFeedCB
276                 self.previousFeedCB=previousFeedCB
277
278                 self["content"] = RSSList(data)
279                 self["summary"] = Label()
280                 self["info"] = Label()
281
282                 if not newItems:
283                         self["actions"] = ActionMap([ "OkCancelActions", "ChannelSelectBaseActions", "MenuActions", "ColorActions" ], 
284                         {
285                                 "ok": self.showCurrentEntry,
286                                 "cancel": self.close,
287                                 "nextBouquet": self.next,
288                                 "prevBouquet": self.previous,
289                                 "menu": self.menu,
290                                 "yellow": self.selectEnclosure,
291                         })
292                         self.onShown.append(self.__show)
293                         self.onClose.append(self.__close)
294
295                         self.timer = None
296                 else:
297                         self["actions"] = ActionMap([ "OkCancelActions" ], 
298                         {
299                                 "cancel": self.close,
300                         })
301
302                         self.timer = eTimer()
303                         self.timer.timeout.get().append(self.timerTick)
304                         self.onExecBegin.append(self.startTimer)
305
306                 self["content"].connectSelChanged(self.updateInfo)
307                 self.onLayoutFinish.extend([self.updateInfo, self.setConditionalTitle])
308
309         def startTimer(self):
310                 self.timer.startLongTimer(5)
311
312         def timerTick(self):
313                 self.close()
314
315         def __show(self):
316                 self.rssPoller.addCallback(self.pollCallback)
317
318         def __close(self):
319                 if self.timer is not None:
320                         self.timer.timeout.get().remove(self.timerTick)
321                         self.timer = None
322                 self.rssPoller.removeCallback(self.pollCallback)
323
324         def pollCallback(self, id = None):
325                 print "[SimpleRSS] SimpleRSSFeed called back"
326                 current_entry = self["content"].getCurrentEntry()
327
328                 if id is not None and self.id == id+1:
329                         print "[SimpleRSS] pollCallback recieved local feed", self.id
330                         self.feedTitle = self.rssPoller.feeds[id].title
331                         self.data = self.rssPoller.feeds[id].history
332                 elif self.id == 0:
333                         print "[SimpleRSS] pollCallback recieved all or non-local feed, updating active view (new_items)"
334                         self.data = self.rssPoller.new_items
335                 else:
336                         print "[SimpleRSS] pollCallback recieved all or non-local feed, updating", self.id
337                         self.feedTitle = self.rssPoller.feeds[self.id-1].title
338                         self.data = self.rssPoller.feeds[self-id-1].history
339
340                 self["content"].l.setList(self.data)
341                 self["content"].moveToEntry(current_entry)
342
343                 self.setConditionalTitle()
344                 self.updateInfo()
345
346         def setConditionalTitle(self):
347                 if not self.newItems:
348                         self.setTitle(': '.join(["Simple RSS Reader", self.feedTitle]))
349                 else:
350                         self.setTitle("Simple RSS Reader: New Items")
351
352         def updateInfo(self):
353                 current_entry = self["content"].getCurrentEntry()
354                 if current_entry:
355                         self["summary"].setText(current_entry[2])
356
357                         cur_idx = self["content"].getCurrentIndex()
358                         self["info"].setText("Entry %s/%s" % (cur_idx+1, len(self.data)))
359                 else:
360                         self["summary"].setText("Feed is empty.")
361                         self["info"].setText("")
362
363         def menu(self):
364                 if self.id > 0:
365                         self.singleUpdate(self.id-1)
366
367         def nextEntryCB(self):
368                 self["content"].moveDown()
369                 return (self["content"].getCurrentEntry(), self["content"].getCurrentIndex(), len(self.data))
370
371         def previousEntryCB(self):
372                 self["content"].moveUp()
373                 return (self["content"].getCurrentEntry(), self["content"].getCurrentIndex(), len(self.data))
374
375         # TODO: Fix moving back to previously marked entry (same goes for self.previous)
376         def next(self):
377                 # Show next Feed
378                 if self.nextFeedCB is not None:
379                         result = self.nextFeedCB()
380                         (self.feedTitle, self.data, self.id) = result
381                         #current_entry = self["content"].getCurrentEntry()
382                         self["content"].l.setList(self.data) # Update list
383                         self["content"].moveToIndex(0)
384                         #self["content"].moveToEntry(current_entry)
385                         self.updateInfo() # In case entry is no longer in history
386                         self.setConditionalTitle() # Update title
387                         return result
388                 return (self.feedTitle, self.data, self.id)
389
390         def previous(self):
391                 # Show previous Feed
392                 if self.previousFeedCB is not None:
393                         result = self.previousFeedCB()
394                         (self.feedTitle, self.data, self.id) = result
395                         #current_entry = self["content"].getCurrentEntry()
396                         self["content"].l.setList(self.data) # Update list
397                         self["content"].moveToIndex(0)
398                         #self["content"].moveToEntry(current_entry)
399                         self.updateInfo() # In case entry is no longer in history
400                         self.setConditionalTitle() # Update title
401                         return result
402                 return (self.feedTitle, self.data, self.id)
403
404         def checkEmpty(self):
405                 if self.id > 0 and not len(self.data):
406                         self.singleUpdate(self.id-1)
407
408         def showCurrentEntry(self):
409                 current_entry = self["content"].getCurrentEntry()
410                 if current_entry is None: # empty list
411                         return
412
413                 self.session.openWithCallback(
414                         self.updateInfo,
415                         RSSEntryView,
416                         current_entry,
417                         cur_idx=self["content"].getCurrentIndex(),
418                         entries=len(self.data),
419                         feedTitle=self.feedTitle,
420                         nextEntryCB=self.nextEntryCB,
421                         previousEntryCB=self.previousEntryCB,
422                         nextFeedCB=self.next,
423                         previousFeedCB=self.previous
424                 )
425
426         def selectEnclosure(self):
427                 current_entry = self["content"].getCurrentEntry()
428                 if current_entry is None: # empty list
429                         return
430
431                 RSSBaseView.selectEnclosure(self, current_entry[3])
432
433 class RSSOverview(RSSBaseView):
434         """Shows an Overview over all RSS-Feeds known to rssPoller"""
435         skin = """
436                 <screen position="100,100" size="460,415" title="Simple RSS Reader" >
437                         <widget name="info" position="0,0" size="460,20" halign="right" font="Regular; 18" />
438                         <widget name="content" position="0,20" size="460,300" scrollbarMode="showOnDemand" />
439                         <widget name="summary" position="0,320" size="460,95" font="Regular;16" />
440                 </screen>"""
441
442         def __init__(self, session, poller):
443                 RSSBaseView.__init__(self, session, poller)
444
445                 self["actions"] = ActionMap([ "OkCancelActions", "MenuActions", "ColorActions" ], 
446                 {
447                         "ok": self.showCurrentEntry,
448                         "cancel": self.close,
449                         "menu": self.menu,
450                         "yellow": self.selectEnclosure,
451                 })
452
453                 self.fillFeeds()
454
455                 # We always have at least "New Items"-Feed
456                 self["content"] = RSSList(self.feeds)
457                 self["summary"] = Label(self.feeds[0][2])
458                 self["info"] = Label("Feed 1/%s" % len(self.feeds))
459
460                 self["content"].connectSelChanged(self.updateInfo)
461                 self.onShown.append(self.__show)
462                 self.onClose.append(self.__close)
463
464         def __show(self):
465                 self.rssPoller.addCallback(self.pollCallback)
466
467         def __close(self):
468                 self.rssPoller.removeCallback(self.pollCallback)
469
470         def fillFeeds(self):
471                 self.feeds = [(
472                         "New Items",
473                         "New Items since last Auto-Update",
474                         ' '.join([str(len(self.rssPoller.new_items)), "Entries"]),
475                         self.rssPoller.new_items
476                 )]
477                 self.feeds.extend([
478                         (
479                                 feed.title,
480                                 feed.description,
481                                 ' '.join([str(len(feed.history)), "Entries"]),
482                                 feed.history
483                         )
484                                 for feed in self.rssPoller.feeds
485                 ])
486
487         def pollCallback(self, id = None):
488                 print "[SimpleRSS] SimpleRSS called back"
489                 current_entry = self["content"].getCurrentEntry()
490
491                 if id is not None:
492                         print "[SimpleRSS] pollCallback updating feed", id
493                         self.feeds[id+1] = (
494                                 self.rssPoller.feeds[id].title,
495                                 self.rssPoller.feeds[id].description,
496                                 ' '.join([str(len(self.rssPoller.feeds[id].history)), "Entries"]),
497                                 self.rssPoller.feeds[id].history
498                         )
499                 else:
500                         print "[SimpleRSS] pollCallback updating all feeds"
501                         self.fillFeeds()
502
503                 self["content"].l.setList(self.feeds)
504                 self["content"].moveToEntry(current_entry)
505
506                 self.updateInfo()
507
508         def updateInfo(self):
509                 current_entry = self["content"].getCurrentEntry()
510                 if current_entry:
511                         self["summary"].setText(current_entry[2])
512                         self["info"].setText("Feed %s/%s" % (self["content"].getCurrentIndex()+1, len(self.feeds)))
513                 # Should never happen
514                 else:
515                         self["summary"].setText("")
516                         self["info"].setText("")
517
518         def menu(self):
519                 cur_idx = self["content"].getCurrentIndex()
520                 if cur_idx > 0:
521                         possible_actions = [
522                                 (_("Update Feed"), "update"),
523                                 (_("Setup"), "setup"),
524                                 (_("Close"), "close")
525                         ]
526                 else:
527                         possible_actions = [
528                                 (_("Setup"), "setup"),
529                                 (_("Close"), "close")
530                         ]
531                 self.session.openWithCallback(
532                         self.menuChoice,
533                         ChoiceBox,
534                         "What to do?",
535                         possible_actions
536                 )
537
538         def menuChoice(self, result):
539                 if result:
540                         if result[1] == "update":
541                                 cur_idx = self["content"].getCurrentIndex()
542                                 if cur_idx > 0:
543                                         self.singleUpdate(cur_idx-1)
544                         elif result[1] == "setup":
545                                 self.session.openWithCallback(self.refresh, RSSSetup, rssPoller=self.rssPoller)
546                         elif result[1] == "close":
547                                 self.close()
548
549         def refresh(self):
550                 current_entry = self["content"].getCurrentEntry()
551
552                 self.fillFeeds()
553                 self["content"].l.setList(self.feeds)
554
555                 self["content"].moveToEntry(current_entry)
556                 self.updateInfo()
557
558         def nextFeedCB(self):
559                 self["content"].moveUp()
560                 current_entry = self["content"].getCurrentEntry()
561                 return (current_entry[0], current_entry[3], self["content"].getCurrentIndex())
562
563         def previousFeedCB(self):
564                 self["content"].moveDown()
565                 current_entry = self["content"].getCurrentEntry()
566                 return (current_entry[0], current_entry[3], self["content"].getCurrentIndex())
567
568         def showCurrentEntry(self):
569                 current_entry = self["content"].getCurrentEntry()
570                 if current_entry is None: # empty list
571                         return
572
573                 self.session.openWithCallback(
574                         self.refresh,
575                         RSSFeedView,
576                         current_entry[3],
577                         feedTitle=current_entry[0],
578                         nextFeedCB=self.nextFeedCB,
579                         previousFeedCB=self.previousFeedCB,
580                         rssPoller=self.rssPoller,
581                         id=self["content"].getCurrentIndex()
582                 )
583
584         def selectEnclosure(self):
585                 current_entry = self["content"].getCurrentEntry()
586                 if current_entry is None: # empty list
587                         return
588
589                 # Build a list of all enclosures in this feed
590                 enclosures = []
591                 for entry in current_entry[3]:
592                                 enclosures.extend(entry[3])
593                 RSSBaseView.selectEnclosure(self, enclosures)