os.remove seems not to support white spaces in file name
[enigma2-plugins.git] / podcast / src / plugin.py
1 ##
2 ## Podcast
3 ## by AliAbdul
4 ##
5 from Components.ActionMap import ActionMap
6 from Components.config import config, ConfigSelection, ConfigSubsection, ConfigText, ConfigYesNo, getConfigListEntry
7 from Components.ConfigList import ConfigListScreen\r
8 from Components.FileList import FileList
9 from Components.Label import Label
10 from Components.Language import language\r
11 from Components.MenuList import MenuList
12 from Components.PluginComponent import plugins
13 from Components.ProgressBar import ProgressBar\r
14 from enigma import eServiceReference, eTimer
15 from os import environ, system
16 from Plugins.Plugin import PluginDescriptor
17 from Screens.InfoBar import MoviePlayer
18 from Screens.MessageBox import MessageBox
19 from Screens.Screen import Screen
20 from Tools.BoundFunction import boundFunction
21 from Tools.Directories import fileExists, resolveFilename, SCOPE_LANGUAGE, SCOPE_PLUGINS
22 from Tools.Downloader import downloadWithProgress
23 from twisted.web.client import getPage
24 from xml.etree.cElementTree import parse
25 import gettext, re
26
27 ###################################################
28
29 def localeInit():
30         lang = language.getLanguage()
31         environ["LANGUAGE"] = lang[:2]\r
32         gettext.bindtextdomain("enigma2", resolveFilename(SCOPE_LANGUAGE))\r
33         gettext.textdomain("enigma2")
34         gettext.bindtextdomain("Podcast", "%s%s" % (resolveFilename(SCOPE_PLUGINS), "Extensions/Podcast/locale/"))
35
36 def _(txt):\r
37         t = gettext.dgettext("Podcast", txt)\r
38         if t == txt:\r
39                 t = gettext.gettext(txt)\r
40         return t
41
42 localeInit()\r
43 language.addCallback(localeInit)
44
45 ###################################################
46
47 def remove(file):
48         system('rm "' + file + '"')
49
50 ###################################################
51
52 class ChangedMoviePlayer(MoviePlayer):
53         def __init__(self, session, service):
54                 MoviePlayer.__init__(self, session, service)
55                 self.skinName = "MoviePlayer"
56
57         def leavePlayer(self):
58                 self.session.openWithCallback(self.leavePlayerConfirmed, MessageBox, _("Stop playing this movie?"))
59
60         def leavePlayerConfirmed(self, answer):
61                 if answer:
62                         self.close()
63
64         def doEofInternal(self, playing):
65                 pass
66
67         def getPluginList(self):
68                 list = []
69                 for p in plugins.getPlugins(where=PluginDescriptor.WHERE_EXTENSIONSMENU):
70                         if p.name != _("Podcast"):
71                                 list.append(((boundFunction(self.getPluginName, p.name), boundFunction(self.runPlugin, p), lambda: True), None))
72                 return list
73
74         def showMovies(self):
75                 pass
76
77 ###################################################
78
79 config.plugins.Podcast = ConfigSubsection()
80 config.plugins.Podcast.buffer = ConfigYesNo(default=True)
81 config.plugins.Podcast.bufferDevice = ConfigText(default="/media/hdd/", fixed_size=False)
82 config.plugins.Podcast.keepStored = ConfigSelection(choices={"delete": _("delete"), "keep": _("keep on device"), "ask": _("ask me")}, default="delete")
83
84 ###################################################
85
86 def encodeUrl(url):
87         url = url.replace("&", "&")
88         url = url.replace("&lt;", "<")
89         url = url.replace("&gt;", ">")
90         url = url.replace("&#39;", "'")
91         url = url.replace("&quot;", '"')
92         url = url.replace("&#42;", "*")
93         url = url.replace("&#124;", "|")
94         url = url.replace("&#039;", "'")
95         url = url.replace("&#187;", ">>")
96         return url
97
98 ###################################################
99
100 class BufferThread():
101         def __init__(self):
102                 self.progress = 0
103                 self.downloading = False
104                 self.error = ""
105                 self.download = None
106
107         def startDownloading(self, url, file):
108                 self.progress = 0
109                 self.downloading = True
110                 self.error = ""
111                 self.download = downloadWithProgress(url, file)
112                 self.download.addProgress(self.httpProgress)
113                 self.download.start().addCallback(self.httpFinished).addErrback(self.httpFailed)
114
115         def httpProgress(self, recvbytes, totalbytes):
116                 self.progress = int(100 * recvbytes / float(totalbytes))
117
118         def httpFinished(self, string=""):
119                 self.downloading = False
120                 if string is not None:
121                         self.error = str(string)
122                 else:
123                         self.error = ""
124
125         def httpFailed(self, failure_instance=None, error_message=""):
126                 self.downloading = False
127                 if error_message == "" and failure_instance is not None:
128                         error_message = failure_instance.getErrorMessage()
129                         self.error = str(error_message)
130
131         def stop(self):
132                 self.progress = 0
133                 self.downloading = False
134                 self.error = ""
135                 self.download.stop()
136
137 bufferThread = BufferThread()
138
139 ###################################################
140
141 class PodcastBuffer(Screen):
142         skin = """
143                 <screen position="center,center" size="520,80" title="%s" >\r
144                         <widget name="info" position="5,5" size="510,40" font="Regular;18" halign="center" valign="center" />
145                         <widget name="progress" position="100,50" size="320,14" pixmap="skin_default/progress_big.png" borderWidth="2" borderColor="#cccccc" />\r
146                 </screen>""" % _("Podcast")
147 \r
148         def __init__(self, session, url, file):
149                 self.session = session
150                 Screen.__init__(self, session)
151                 
152                 self.url = url
153                 self.file = file
154                 
155                 self.infoTimer = eTimer()
156                 self.infoTimer.timeout.get().append(self.updateInfo)
157                 
158                 self["info"] = Label(_("Downloading movie: %s") % self.file)
159                 self["progress"] = ProgressBar()
160                 
161                 self["actions"] = ActionMap(["OkCancelActions"], {"ok": self.okClicked, "cancel": self.exit}, -1)
162                 
163                 self.onLayoutFinish.append(self.downloadMovie)
164
165         def downloadMovie(self):
166                 bufferThread.startDownloading(self.url, self.file)
167                 self.infoTimer.start(300, False)
168
169         def updateInfo(self):
170                 if bufferThread.error != "":
171                         self["info"].setText(bufferThread.error)
172                         self.infoTimer.stop()
173                 else:
174                         progress = int(bufferThread.progress)
175                         self["progress"].setValue(progress)
176                         if progress == 100:
177                                 self.infoTimer.stop()
178                                 self.close(True)
179
180         def okClicked(self):
181                 if int(bufferThread.progress) > 0:
182                         self.infoTimer.stop()
183                         self.close(True)
184
185         def exit(self):
186                 bufferThread.download.stop()
187                 self.close(None)
188
189 ###################################################
190
191 class PodcastMovies(Screen):
192         skin = """
193                 <screen position="center,center" size="420,360" title="%s" >\r
194                         <widget name="list" position="5,5" size="410,250" scrollbarMode="showOnDemand" />\r
195                         <eLabel position="5,260" size="420,2" backgroundColor="#ffffff" />\r
196                         <widget name="info" position="5,265" size="420,90" font="Regular;18" />\r
197                 </screen>""" % _("Podcast")
198 \r
199         def __init__(self, session, url):
200                 self.session = session
201                 Screen.__init__(self, session)
202                 
203                 self.url = url
204                 self.list = []
205                 self.movies = []
206                 self.working = True
207                 
208                 self["list"] = MenuList([])
209                 self["list"].onSelectionChanged.append(self.showInfo)
210                 self["info"] = Label()
211                 
212                 self["actions"] = ActionMap(["OkCancelActions"], {"ok": self.ok, "cancel": self.exit}, -1)
213                 
214                 self.onLayoutFinish.append(self.downloadMovies)
215
216         def ok(self):
217                 if self.working == False:
218                         if len(self.list) > 0:\r
219                                 idx = self["list"].getSelectionIndex()\r
220                                 (url, length, type) = self.splitExtraInfo(self.movies[idx][1])
221                                 if config.plugins.Podcast.buffer.value:
222                                         file = url
223                                         while file.__contains__("/"):
224                                                 idx = file.index("/")
225                                                 file = file[idx+1:]
226                                         self.file = "%s%s" % (config.plugins.Podcast.bufferDevice.value, file)
227                                         self.session.openWithCallback(self.bufferCallback, PodcastBuffer, url, self.file)
228                                 else:
229                                         ref = eServiceReference(4097, 0, url)
230                                         self.session.open(ChangedMoviePlayer, ref)
231
232         def bufferCallback(self, callback):
233                 if callback is not None:
234                         ref = eServiceReference(4097, 0, self.file)
235                         self.session.openWithCallback(self.delete, ChangedMoviePlayer, ref)
236
237         def delete(self, callback=None):
238                 if bufferThread.downloading: #still downloading?
239                         bufferThread.stop()
240                 if config.plugins.Podcast.keepStored.value == "delete":
241                         remove(self.file)
242                 elif config.plugins.Podcast.keepStored.value == "ask":
243                         self.session.openWithCallback(self.deleteCallback, MessageBox, _("Delete this movie?"))
244
245         def deleteCallback(self, callback):
246                 if callback:
247                         remove(self.file)
248
249         def exit(self):
250                 if self.working == False:
251                         self.close()
252
253         def downloadMovies(self):
254                 getPage(self.url).addCallback(self.showMovies).addErrback(self.error)
255
256         def showMovies(self, page):
257                 if page.__contains__("<itunes"):
258                         reonecat = re.compile(r'<item>(.+?)</item>', re.DOTALL)
259                         for item in reonecat.findall(page):
260                                 reonecat2 = re.compile(r'<title>(.+?)</title>.+?<description>(.+?)</description>.+?<enclosure(.+?)/>.+?', re.DOTALL)\r
261                                 for title, description, extra in reonecat2.findall(item):
262                                         if title.startswith("<![CDATA["):
263                                                 title = title[9:]
264                                         if title.endswith("]]>"):
265                                                 title = title[:-3]
266                                         if description.__contains__("<![CDATA["):
267                                                 idx = description.index("<![CDATA[")
268                                                 description = description[idx+10:]
269                                         if description.endswith("]]>"):
270                                                 description = description[:-3]\r
271                                         self.list.append(encodeUrl(title))\r
272                                         self.movies.append([description, extra])
273                 else:
274                         reonecat = re.compile(r'<title>(.+?)</title>.+?<description>(.+?)</description>.+?<enclosure(.+?)/>.+?', re.DOTALL)\r
275                         for title, description, extra in reonecat.findall(page):
276                                 if title.startswith("<![CDATA["):
277                                         title = title[9:]
278                                 if title.endswith("]]>"):
279                                         title = title[:-3]
280                                 if description.__contains__("<![CDATA["):
281                                         idx = description.index("<![CDATA[")
282                                         description = description[idx+10:]
283                                 if description.endswith("]]>"):
284                                         description = description[:-3]\r
285                                 self.list.append(encodeUrl(title))\r
286                                 self.movies.append([description, extra])\r
287                 self["list"].setList(self.list)\r
288                 self.showInfo()
289                 self.working = False
290
291         def error(self, error=""):
292                 print "[Podcast] Error:", error
293                 self.instance.setTitle(_("Error getting movies"))
294                 self.working = False\r
295 \r
296         def showInfo(self):\r
297                 if len(self.list) > 0:\r
298                         idx = self["list"].getSelectionIndex()\r
299                         description = self.movies[idx][0]\r
300                         (url, length, type) = self.splitExtraInfo(self.movies[idx][1])\r
301                         self["info"].setText("%s: %s   %s: %s\n%s" % (_("Length"), length, _("Type"), type, encodeUrl(description)))\r
302 \r
303         def splitExtraInfo(self, info):\r
304                 if info.__contains__('url="'):\r
305                         idx = info.index('url="')\r
306                         url = info[idx+5:]\r
307                         idx = url.index('"')\r
308                         url = url[:idx]\r
309                 else:\r
310                         url = "N/A"\r
311                 \r
312                 if info.__contains__('length="'):\r
313                         idx = info.index('length="')\r
314                         length = info[idx+8:]\r
315                         idx = length.index('"')\r
316                         length = length[:idx]\r
317                         length = str((int(length) / 1024) / 1024) + " MB"\r
318                 else:\r
319                         length = "N/A"\r
320                 \r
321                 if info.__contains__('type="'):\r
322                         idx = info.index('type="')\r
323                         type = info[idx+6:]\r
324                         idx = type.index('"')\r
325                         type = type[:idx]\r
326                 else:\r
327                         type = "N/A"\r
328                 \r
329                 return (url, length, type)
330
331 ###################################################
332
333 class PodcastPodcasts(Screen):
334         skin = """
335                 <screen position="center,center" size="420,360" title="%s" >\r
336                         <widget name="list" position="0,0" size="420,350" scrollbarMode="showOnDemand" />\r
337                 </screen>""" % _("Podcast")
338 \r
339         def __init__(self, session, provider):
340                 self.session = session
341                 Screen.__init__(self, session)
342                 
343                 self["actions"] = ActionMap(["OkCancelActions"], {"ok": self.ok, "cancel": self.close}, -1)
344                 
345                 self.urls = []
346                 list = []
347                 for podcast in provider.findall("podcast"):
348                         name = podcast.get("name") or None
349                         name = name.encode("UTF-8") or name
350                         url = podcast.get("url") or None
351                         if name and url:
352                                 list.append(name)
353                                 self.urls.append(url)
354                 self["list"] = MenuList(list)
355
356         def ok(self):
357                 if len(self.urls) > 0:
358                         cur = self.urls[self["list"].getSelectedIndex()]
359                         self.session.open(PodcastMovies, cur)
360
361 ###################################################
362
363 class PodcastProvider(Screen):
364         skin = """
365                 <screen position="center,center" size="420,360" title="%s" >
366                         <widget name="list" position="0,0" size="420,350" scrollbarMode="showOnDemand" />\r
367                 </screen>""" % _("Podcast")
368 \r
369         def __init__(self, session, language):
370                 self.session = session
371                 Screen.__init__(self, session)
372                 
373                 self["actions"] = ActionMap(["OkCancelActions"], {"ok": self.ok, "cancel": self.close}, -1)
374                 
375                 self.providers = []
376                 list = []
377                 for provider in language.findall("provider"):
378                         name = provider.get("name") or None
379                         name = name.encode("UTF-8") or name
380                         if name:
381                                 list.append(name)
382                                 self.providers.append(provider)
383                 self["list"] = MenuList(list)
384
385         def ok(self):
386                 if len(self.providers) > 0:
387                         cur = self.providers[self["list"].getSelectedIndex()]
388                         self.session.open(PodcastPodcasts, cur)
389
390 ###################################################
391
392 class PodcastXML(Screen):
393         skin = """
394                 <screen position="center,center" size="420,360" title="%s" >\r
395                         <widget name="list" position="0,0" size="420,350" scrollbarMode="showOnDemand" />\r
396                 </screen>""" % _("Podcast")
397 \r
398         def __init__(self, session):
399                 self.session = session
400                 Screen.__init__(self, session)
401                 
402                 self["actions"] = ActionMap(["OkCancelActions"], {"ok": self.ok, "cancel": self.close}, -1)
403                 
404                 self.languages = []
405                 list = []
406                 file = "/etc/podcast/podcasts.xml"
407                 if fileExists(file):
408                         xml = parse(file).getroot()
409                         for language in xml.findall("language"):
410                                 name = language.get("name") or None
411                                 name = name.encode("UTF-8") or name
412                                 if name:
413                                         list.append(name)
414                                         self.languages.append(language)
415                 self["list"] = MenuList(list)
416
417         def ok(self):
418                 if len(self.languages) > 0:
419                         cur = self.languages[self["list"].getSelectedIndex()]
420                         self.session.open(PodcastProvider, cur)
421
422 ###################################################
423
424 class PodcastComGenre2(Screen):
425         skin = """
426                 <screen position="center,center" size="420,360" title="%s" >\r
427                         <widget name="list" position="0,0" size="420,350" scrollbarMode="showOnDemand" />\r
428                 </screen>""" % _("Podcast")
429 \r
430         def __init__(self, session, url):
431                 self.session = session
432                 Screen.__init__(self, session)
433                 
434                 self["actions"] = ActionMap(["OkCancelActions"], {"ok": self.ok, "cancel": self.exit}, -1)
435                 
436                 self.url = url
437                 self.urls = []
438                 self.working = True
439                 self["list"] = MenuList([])
440                 
441                 self.onLayoutFinish.append(self.downloadGenres)
442
443         def ok(self):
444                 if self.working == False:
445                         if len(self.urls) > 0:
446                                 self.working = True
447                                 cur = self.urls[self["list"].getSelectedIndex()]
448                                 getPage(cur).addCallback(self.getRssUrl).addErrback(self.error2)
449
450         def getRssUrl(self, page):
451                 idx = page.index('">rss feed</a><br>')
452                 page = page[:idx]
453                 while page.__contains__("http://"):
454                         idx = page.index("http://")
455                         page = page[idx+1:]
456                 self.working = False
457                 self.session.open(PodcastMovies, "h%s"%page)
458
459         def exit(self):
460                 if self.working == False:
461                         self.close()
462
463         def downloadGenres(self):
464                 getPage(self.url).addCallback(self.getGenres).addErrback(self.error)
465
466         def getGenres(self, page):
467                 list = []
468                 reonecat = re.compile(r'height="19"><a href="(.+?)">(.+?)</a>', re.DOTALL)\r
469                 for url, title in reonecat.findall(page):\r
470                         list.append(encodeUrl(title))\r
471                         self.urls.append(url)\r
472                 self["list"].setList(list)
473                 self.working = False
474
475         def error(self, error=""):
476                 print "[Podcast] Error:", error
477                 self.instance.setTitle(_("Error getting genres"))
478                 self.working = False
479
480         def error2(self, error=""):
481                 print "[Podcast] Error:", error
482                 self.instance.setTitle(_("Error getting rss feed"))
483                 self.working = False
484
485 ###################################################
486
487 class PodcastComGenre(Screen):
488         skin = """
489                 <screen position="center,center" size="420,360" title="%s" >
490                         <widget name="list" position="0,0" size="420,350" scrollbarMode="showOnDemand" />\r
491                 </screen>""" % _("Podcast")
492 \r
493         def __init__(self, session, url):
494                 self.session = session
495                 Screen.__init__(self, session)
496                 
497                 self["actions"] = ActionMap(["OkCancelActions"], {"ok": self.ok, "cancel": self.exit}, -1)
498                 
499                 self.url = url
500                 self.urls = []
501                 self.working = True
502                 self["list"] = MenuList([])
503                 
504                 self.onLayoutFinish.append(self.downloadSite)
505
506         def ok(self):
507                 if self.working == False:
508                         if len(self.urls) > 0:
509                                 cur = self.urls[self["list"].getSelectedIndex()]
510                                 self.session.open(PodcastComGenre2, cur)
511
512         def exit(self):
513                 if self.working == False:
514                         self.close()
515
516         def downloadSite(self):
517                 getPage(self.url).addCallback(self.getUrl).addErrback(self.error)
518
519         def getUrl(self, page):
520                 reonecat = re.compile(r'Get this podcast channel on your mobile phone:</strong><br><a href="(.+?)"', re.DOTALL)
521                 list = reonecat.findall(page)
522                 if len(list) > 0:
523                         getPage(list[0]).addCallback(self.getGenres).addErrback(self.error)
524                 else:
525                         self.error("Error getting movies-url")
526
527         def getGenres(self, page):
528                 list = []
529                 reonecat = re.compile(r'height="17"><a title="(.+?)" href="(.+?)">(.+?)</a>', re.DOTALL)\r
530                 for title2, url, title in reonecat.findall(page):\r
531                         list.append(encodeUrl(title))\r
532                         self.urls.append(url)\r
533                 self["list"].setList(list)
534                 self.working = False
535
536         def error(self, error=""):
537                 print "[Podcast] Error:", error
538                 self.instance.setTitle(_("Error getting genres"))
539                 self.working = False
540
541 ###################################################
542
543 class PodcastCom(Screen):
544         skin = """
545                 <screen position="center,center" size="420,360" title="%s" >
546                         <widget name="list" position="0,0" size="420,350" scrollbarMode="showOnDemand" />\r
547                 </screen>""" % _("Podcast")
548 \r
549         def __init__(self, session):
550                 self.session = session
551                 Screen.__init__(self, session)
552                 
553                 self.working = True
554                 self.urls = []
555                 
556                 self["actions"] = ActionMap(["OkCancelActions"], {"ok": self.ok, "cancel": self.exit}, -1)
557                 
558                 self["list"] = MenuList([])
559                 
560                 self.onLayoutFinish.append(self.downloadMovies)
561
562         def ok(self):
563                 if self.working == False:
564                         if len(self.urls) > 0:
565                                 cur = self.urls[self["list"].getSelectedIndex()]
566                                 self.session.open(PodcastComGenre, cur)
567
568         def exit(self):
569                 if self.working == False:
570                         self.close()
571
572         def downloadMovies(self):
573                 getPage("http://podcast.com/home.php?subpage=_pages/channels_home.php").addCallback(self.showGenres).addErrback(self.error)
574
575         def showGenres(self, page):
576                 list = []
577                 reonecat = re.compile(r'<li><a href="(.+?)" title="(.+?)">(.+?)</a></li>', re.DOTALL)\r
578                 for url, title2, title in reonecat.findall(page):
579                         if not title.startswith("<"):\r
580                                 list.append(encodeUrl(title))\r
581                                 self.urls.append(url)\r
582                 self["list"].setList(list)
583                 self.working = False
584
585         def error(self, error=""):
586                 print "[Podcast] Error:", error
587                 self.instance.setTitle(_("Error getting genres"))
588                 self.working = False
589
590 ###################################################
591
592 class LocationSelection(Screen):
593         skin = """
594         <screen position="center,center" size="560,300" title="%s">
595                 <ePixmap pixmap="skin_default/buttons/red.png" position="0,0" size="140,40" transparent="1" alphatest="on" />
596                 <ePixmap pixmap="skin_default/buttons/green.png" position="140,0" size="140,40" transparent="1" alphatest="on" />
597                 <ePixmap pixmap="skin_default/buttons/yellow.png" position="280,0" size="140,40" transparent="1" alphatest="on" />
598                 <ePixmap pixmap="skin_default/buttons/blue.png" position="420,0" size="140,40" transparent="1" alphatest="on" />
599                 <widget name="key_green" position="140,0" zPosition="1" size="140,40" font="Regular;20" valign="center" halign="center" backgroundColor="#1f771f" transparent="1" />
600                 <widget name="filelist" position="10,45" size="550,255" scrollbarMode="showOnDemand" />
601         </screen>""" % _("Podcast")\r
602 \r
603         def __init__(self, session, dir="/"):\r
604                 Screen.__init__(self, session)\r
605                 \r
606                 self["key_green"] = Label(_("Select"))\r
607                 \r
608                 try: self["filelist"] = FileList(dir, showDirectories=True, showFiles=False)\r
609                 except: self["filelist"] = FileList("/", showDirectories, showFiles)\r
610                 \r
611                 self["actions"] = ActionMap(["ColorActions", "OkCancelActions"],\r
612                         {\r
613                                 "ok": self.okClicked,\r
614                                 "cancel": self.exit,\r
615                                 "green": self.select\r
616                         }, -1)
617                 
618                 self.onLayoutFinish.append(self.updateDirectoryName)\r
619                 \r
620         def okClicked(self):\r
621                 if self["filelist"].canDescent():\r
622                         self["filelist"].descent()\r
623                         self["filelist"].instance.moveSelectionTo(0)\r
624                         self.updateDirectoryName()\r
625 \r
626         def exit(self):\r
627                 self.close(None)\r
628 \r
629         def select(self):\r
630                 dir = self["filelist"].getCurrentDirectory()\r
631                 if dir is not None:\r
632                         self.close(dir)\r
633                 else:\r
634                         self.close(None)\r
635 \r
636         def updateDirectoryName(self):
637                 try:
638                         dir = self["filelist"].getCurrentDirectory()
639                         self.instance.setTitle(dir)
640                 except:
641                         self.instance.setTitle("?")
642
643 ###################################################
644
645 class PodcastConfig(ConfigListScreen, Screen):
646         skin = """
647         <screen position="center,center" size="560,180" title="%s">
648                 <ePixmap pixmap="skin_default/buttons/red.png" position="0,0" size="140,40" transparent="1" alphatest="on" />
649                 <ePixmap pixmap="skin_default/buttons/green.png" position="140,0" size="140,40" transparent="1" alphatest="on" />
650                 <ePixmap pixmap="skin_default/buttons/yellow.png" position="280,0" size="140,40" transparent="1" alphatest="on" />
651                 <ePixmap pixmap="skin_default/buttons/blue.png" position="420,0" size="140,40" transparent="1" alphatest="on" />
652                 <widget name="key_green" position="140,0" zPosition="1" size="140,40" font="Regular;20" valign="center" halign="center" backgroundColor="#1f771f" transparent="1" />
653                 <widget name="config" position="10,45" size="550,125" scrollbarMode="showOnDemand" />
654         </screen>""" % _("Podcast")
655
656         def __init__(self, session):
657                 Screen.__init__(self, session)
658                 
659                 self["key_green"] = Label(_("Save"))
660                 
661                 ConfigListScreen.__init__(self, [])
662                         
663                 
664                 self["actions"] = ActionMap(["OkCancelActions", "ColorActions"], {"green": self.save, "cancel": self.exit}, -1)
665                 
666                 self.onLayoutFinish.append(self.createConfig)
667
668         def createConfig(self):
669                 self.deviceEntry = ConfigSelection(choices=[config.plugins.Podcast.bufferDevice.value], default=config.plugins.Podcast.bufferDevice.value)
670                 self["config"].list = [
671                         getConfigListEntry(_("Buffer:"), config.plugins.Podcast.buffer),
672                         getConfigListEntry(_("Buffer device:"), self.deviceEntry),
673                         getConfigListEntry(_("Buffer file handling:"), config.plugins.Podcast.keepStored)]
674
675         def keyLeft(self):
676                 ConfigListScreen.keyLeft(self)
677                 self.handleKeysLeftAndRight()
678
679         def keyRight(self):
680                 ConfigListScreen.keyRight(self)
681                 self.handleKeysLeftAndRight()
682
683         def handleKeysLeftAndRight(self):
684                 sel = self["config"].getCurrent()[1]
685                 if sel == self.deviceEntry:
686                         self.session.openWithCallback(self.locationSelected, LocationSelection, config.plugins.Podcast.bufferDevice.value)
687
688         def locationSelected(self, dir):
689                 if dir is not None and dir != "?":
690                         config.plugins.Podcast.bufferDevice.value = dir
691                         config.plugins.Podcast.bufferDevice.save()
692                         self.createConfig()
693
694         def save(self):
695                 for x in self["config"].list:
696                         x[1].save()
697                 self.close()
698
699         def exit(self):
700                 for x in self["config"].list:
701                         x[1].cancel()
702                 self.close()
703
704 ###################################################
705
706 class PodcastDeEpisodes(Screen):
707         skin = """
708                 <screen position="center,center" size="420,360" title="%s" >
709                         <widget name="list" position="0,0" size="420,350" scrollbarMode="showOnDemand" />\r
710                 </screen>""" % _("Podcast")
711
712         def __init__(self, session, url):
713                 self.session = session
714                 Screen.__init__(self, session)
715                 
716                 self.working = True
717                 self.url = url
718                 self.urls = []
719                 
720                 self["actions"] = ActionMap(["OkCancelActions"], {"ok": self.ok, "cancel": self.exit}, -1)
721                 
722                 self["list"] = MenuList([])
723                 
724                 self.onLayoutFinish.append(self.downloadMovies)
725
726         def ok(self):
727                 if self.working == False:
728                         self.instance.setTitle(_("Podcast"))
729                         if len(self.urls) > 0:
730                                 cur = self.urls[self["list"].getSelectedIndex()]
731                                 self.working = True
732                                 getPage(cur).addCallback(self.playPodcast).addErrback(self.error2)
733
734         def playPodcast(self, url):
735                 if url.__contains__('" id="control_download"'):
736                         self.working = False
737                         idx = url.index('" id="control_download"')
738                         url = url[:idx]
739                         while url.__contains__("http://"):
740                                 idx = url.index("http://")
741                                 url = url[idx+1:]
742                         url = "h%s"%url
743                         
744                         if config.plugins.Podcast.buffer.value:
745                                 file = url
746                                 while file.__contains__("/"):
747                                         idx = file.index("/")
748                                         file = file[idx+1:]
749                                 self.file = "%s%s" % (config.plugins.Podcast.bufferDevice.value, file)
750                                 self.session.openWithCallback(self.bufferCallback, PodcastBuffer, url, self.file)
751                         else:
752                                 ref = eServiceReference(4097, 0, url)
753                                 self.session.open(ChangedMoviePlayer, ref)
754                 else:
755                         self.error2()
756
757         def bufferCallback(self, callback):
758                 if callback is not None:
759                         ref = eServiceReference(4097, 0, self.file)
760                         self.session.openWithCallback(self.delete, ChangedMoviePlayer, ref)
761
762         def delete(self, callback=None):
763                 if bufferThread.downloading: #still downloading?
764                         bufferThread.stop()
765                 if config.plugins.Podcast.keepStored.value == "delete":
766                         remove(self.file)
767                 elif config.plugins.Podcast.keepStored.value == "ask":
768                         self.session.openWithCallback(self.deleteCallback, MessageBox, _("Delete this movie?"))
769
770         def deleteCallback(self, callback):
771                 if callback:
772                         remove(self.file)
773
774         def exit(self):
775                 if self.working == False:
776                         self.close()
777
778         def downloadMovies(self):
779                 getPage(self.url).addCallback(self.showEpisodes).addErrback(self.error)
780
781         def showEpisodes(self, page):
782                 list = []
783                 idx = page.index('<h3>')
784                 page = page[idx:]
785                 idx = page.index('</div></div>')
786                 page = page[:idx]
787                 reonecat = re.compile(r'<a href="(.+?)" title="(.+?)">', re.DOTALL)\r
788                 for url, title in reonecat.findall(page):
789                         if title.startswith("Episode: "):
790                                 title = title[9:]\r
791                         list.append(encodeUrl(title))\r
792                         self.urls.append(url)\r
793                 self["list"].setList(list)
794                 self.working = False
795
796         def error(self, error=""):
797                 print "[Podcast] Error:", error
798                 self.instance.setTitle(_("Error getting episodes"))
799                 self.working = False
800
801         def error2(self, error=""):
802                 print "[Podcast] Error: Error getting podcast url"
803                 self.instance.setTitle(_("Error getting podcast url"))
804                 self.working = False
805
806 ###################################################
807
808 class PodcastDePodcasts(Screen):
809         skin = """
810                 <screen position="center,center" size="420,360" title="%s" >
811                         <widget name="list" position="0,0" size="420,350" scrollbarMode="showOnDemand" />\r
812                 </screen>""" % _("Podcast")
813
814         def __init__(self, session, url):
815                 self.session = session
816                 Screen.__init__(self, session)
817                 
818                 self.working = True
819                 self.url = url
820                 self.urls = []
821                 
822                 self["actions"] = ActionMap(["OkCancelActions"], {"ok": self.ok, "cancel": self.exit}, -1)
823                 
824                 self["list"] = MenuList([])
825                 
826                 self.onLayoutFinish.append(self.downloadMovies)
827
828         def ok(self):
829                 if self.working == False:
830                         if len(self.urls) > 0:
831                                 cur = self.urls[self["list"].getSelectedIndex()]
832                                 self.session.open(PodcastDeEpisodes, cur)
833
834         def exit(self):
835                 if self.working == False:
836                         self.close()
837
838         def downloadMovies(self):
839                 getPage(self.url).addCallback(self.showPodcasts).addErrback(self.error)
840
841         def showPodcasts(self, page):
842                 list = []
843                 idx = page.index('<h4>Podcasts</h4>')
844                 page = page[idx:]
845                 idx = page.index('</div>')
846                 page = page[:idx]
847                 reonecat = re.compile(r'alt="(.+?)" class="(.+?)<a href="(.+?)" title="(.+?)">(.+?)<span class="(.+?)"></span>', re.DOTALL)\r
848                 for title, x, url, x2, x3, type in reonecat.findall(page):
849                         if type.__contains__("content_type_1_icon"):
850                                 text = _(" (Audio)")
851                         else:
852                                 text = _(" (Video)")\r
853                         list.append(encodeUrl(title+text))\r
854                         self.urls.append(url)\r
855                 self["list"].setList(list)
856                 self.working = False
857
858         def error(self, error=""):
859                 print "[Podcast] Error:", error
860                 self.instance.setTitle(_("Error getting podcasts"))
861                 self.working = False
862
863 ###################################################
864
865 class PodcastDeCategories(Screen):
866         skin = """
867                 <screen position="center,center" size="420,360" title="%s" >
868                         <widget name="list" position="0,0" size="420,350" scrollbarMode="showOnDemand" />\r
869                 </screen>""" % _("Podcast")
870
871         def __init__(self, session, url):
872                 self.session = session
873                 Screen.__init__(self, session)
874                 
875                 self.working = True
876                 self.url = url
877                 self.urls = []
878                 
879                 self["actions"] = ActionMap(["OkCancelActions"], {"ok": self.ok, "cancel": self.exit}, -1)
880                 
881                 self["list"] = MenuList([])
882                 
883                 self.onLayoutFinish.append(self.downloadMovies)
884
885         def ok(self):
886                 if self.working == False:
887                         if len(self.urls) > 0:
888                                 cur = self.urls[self["list"].getSelectedIndex()]
889                                 self.session.open(PodcastDePodcasts, cur)
890
891         def exit(self):
892                 if self.working == False:
893                         self.close()
894
895         def downloadMovies(self):
896                 getPage(self.url).addCallback(self.showCategories).addErrback(self.error)
897
898         def showCategories(self, page):
899                 list = []
900                 idx = page.index('<h3>')
901                 page = page[idx:]
902                 idx = page.index('</div>')
903                 page = page[:idx]
904                 reonecat = re.compile(r'<a href="(.+?)" title="(.+?)">', re.DOTALL)\r
905                 for url, title in reonecat.findall(page):\r
906                         list.append(encodeUrl(title))\r
907                         self.urls.append(url)\r
908                 self["list"].setList(list)
909                 self.working = False
910
911         def error(self, error=""):
912                 print "[Podcast] Error:", error
913                 self.instance.setTitle(_("Error getting categories"))
914                 self.working = False
915
916 ###################################################
917
918 class PodcastDe(Screen):
919         skin = """
920                 <screen position="center,center" size="420,360" title="%s" >
921                         <widget name="list" position="0,0" size="420,350" scrollbarMode="showOnDemand" />\r
922                 </screen>""" % _("Podcast")
923
924         def __init__(self, session):
925                 self.session = session
926                 Screen.__init__(self, session)
927                 
928                 self.working = True
929                 self.urls = []
930                 
931                 self["actions"] = ActionMap(["OkCancelActions"], {"ok": self.ok, "cancel": self.exit}, -1)
932                 
933                 self["list"] = MenuList([])
934                 
935                 self.onLayoutFinish.append(self.downloadMovies)
936
937         def ok(self):
938                 if self.working == False:
939                         if len(self.urls) > 0:
940                                 cur = self.urls[self["list"].getSelectedIndex()]
941                                 self.session.open(PodcastDeCategories, cur)
942
943         def exit(self):
944                 if self.working == False:
945                         self.close()
946
947         def downloadMovies(self):
948                 getPage("http://m.podcast.de/kategorien").addCallback(self.showCategories).addErrback(self.error)
949
950         def showCategories(self, page):
951                 list = []
952                 idx = page.index('<h3>')
953                 page = page[idx:]
954                 idx = page.index('</div>')
955                 page = page[:idx]
956                 reonecat = re.compile(r'<a href="(.+?)" title="(.+?)">', re.DOTALL)\r
957                 for url, title in reonecat.findall(page):\r
958                         list.append(encodeUrl(title))\r
959                         self.urls.append(url)\r
960                 self["list"].setList(list)
961                 self.working = False
962
963         def error(self, error=""):
964                 print "[Podcast] Error:", error
965                 self.instance.setTitle(_("Error getting categories"))
966                 self.working = False
967
968 ###################################################
969
970 class Podcast(Screen):
971         skin = """
972                 <screen position="center,center" size="420,360" title="%s" >
973                         <widget name="list" position="0,0" size="420,350" scrollbarMode="showOnDemand" />\r
974                 </screen>""" % _("Podcast")
975 \r
976         def __init__(self, session):
977                 self.session = session
978                 Screen.__init__(self, session)
979                 
980                 self["actions"] = ActionMap(["OkCancelActions"], {"ok": self.ok, "cancel": self.close}, -1)
981                 
982                 self["list"] = MenuList([
983                         _("podcast.de"),
984                         _("podcast.com"),
985                         _("from xml"),
986                         _("configuration")])
987
988         def ok(self):
989                 cur = self["list"].getCurrent()
990                 if cur == _("podcast.de"):
991                         self.session.open(PodcastDe)
992                 elif cur == _("podcast.com"):
993                         self.session.open(PodcastCom)
994                 elif cur == _("from xml"):
995                         self.session.open(PodcastXML)
996                 else:
997                         self.session.open(PodcastConfig)
998
999 ###################################################
1000
1001 def main(session, **kwargs):
1002         session.open(Podcast)
1003
1004 def Plugins(**kwargs):
1005         return PluginDescriptor(name=_("Podcast"), where=PluginDescriptor.WHERE_EXTENSIONSMENU, fnc=main)