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