ZDFMediathek: - use buildfunction to build video entries and download thumbnails...
[enigma2-plugins.git] / zdfmediathek / src / plugin.py
1 # -*- coding: UTF-8 -*-\r
2 # ZDF Mediathek by AliAbdul\r
3 from Components.ActionMap import HelpableActionMap\r
4 from Components.AVSwitch import AVSwitch\r
5 from Components.Label import Label\r
6 from Components.Sources.List import List\r
7 from Components.MenuList import MenuList\r
8 from Components.MultiContent import MultiContentEntryText, MultiContentEntryPixmapAlphaTest\r
9 from Components.Pixmap import Pixmap\r
10 from Components.PluginComponent import plugins\r
11 from enigma import eListboxPythonMultiContent, ePicLoad, eServiceReference, eTimer, getDesktop, gFont\r
12 from os import listdir, path as os_path, remove as os_remove\r
13 from Plugins.Plugin import PluginDescriptor\r
14 from Screens.ChoiceBox import ChoiceBox\r
15 from Screens.HelpMenu import HelpableScreen\r
16 from Screens.InfoBar import MoviePlayer\r
17 from Screens.MessageBox import MessageBox\r
18 from Screens.Screen import Screen\r
19 from Screens.VirtualKeyBoard import VirtualKeyBoard\r
20 from time import sleep\r
21 from Tools.BoundFunction import boundFunction\r
22 from Tools.Directories import resolveFilename, SCOPE_PLUGINS, SCOPE_CURRENT_PLUGIN\r
23 from Tools.HardwareInfo import HardwareInfo\r
24 from Tools.LoadPixmap import LoadPixmap\r
25 from twisted.web.client import downloadPage, getPage\r
26 import htmlentitydefs, re, urllib2\r
27 from urllib2 import Request, URLError, urlopen as urlopen2\r
28 from socket import error\r
29 from httplib import HTTPConnection, HTTPException\r
30 \r
31 HTTPConnection.debuglevel = 1\r
32 \r
33 std_headers = {\r
34         'User-Agent': 'Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.6) Gecko/20100627 Firefox/3.6.6',\r
35         'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',\r
36         'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',\r
37         'Accept-Language': 'en-us,en;q=0.5',\r
38 }\r
39 \r
40 ###################################################\r
41 \r
42 MAIN_PAGE = "http://www.zdf.de"\r
43 \r
44 PNG_PATH = resolveFilename(SCOPE_PLUGINS)+"/Extensions/ZDFMediathek/"\r
45 \r
46 TYPE_NOTHING = 0\r
47 TYPE_MOVIE = 1\r
48 TYPE_PODCAST = 2\r
49 TYPE_MOVIELIST_CATEGORY = 3\r
50 \r
51 LIST_LEFT = 0\r
52 LIST_RIGHT = 1\r
53 LIST_NONE = 2\r
54 \r
55 deviceName = HardwareInfo().get_device_name()\r
56 \r
57 PLAY_MP4 = False\r
58 PLAY_WMV = False\r
59 \r
60 if not deviceName.startswith("dm7025"):\r
61         PLAY_MP4 = True\r
62 if deviceName.startswith("dm7020hd"):\r
63         PLAY_WMV = True\r
64 try:\r
65         from LT.LTStreamPlayer import streamplayer\r
66 except ImportError:\r
67         try:\r
68                 from Plugins.Extensions.LTMediaCenter.LTStreamPlayer import streamplayer\r
69         except ImportError:\r
70                 streamplayer = None\r
71 \r
72 try:\r
73         from Plugins.Extensions.VlcPlayer.VlcServerConfig import vlcServerConfig\r
74 except ImportError:\r
75         vlcServerConfig = None\r
76 \r
77 ###################################################\r
78 \r
79 def decode(line):\r
80         pat = re.compile(r'\\u(....)')\r
81         def sub(mo):\r
82                 return unichr(fromHex(mo.group(1)))\r
83         return pat.sub(sub, unicode(line))\r
84 \r
85 def decode2(line):\r
86         pat = re.compile(r'&#(\d+);')\r
87         def sub(mo):\r
88                 return unichr(int(mo.group(1)))\r
89         return decode3(pat.sub(sub, unicode(line)))\r
90 \r
91 def decode3(line):\r
92         dic = htmlentitydefs.name2codepoint\r
93         for key in dic.keys():\r
94                 entity = "&" + key + ";"\r
95                 line = line.replace(entity, unichr(dic[key]))\r
96         return line\r
97 \r
98 def fromHex(h):\r
99         return int(h, 16)\r
100 \r
101 ###################################################\r
102 \r
103 class ChangedMoviePlayer(MoviePlayer):\r
104         def __init__(self, session, service):\r
105                 MoviePlayer.__init__(self, session, service)\r
106                 self.skinName = "MoviePlayer"\r
107 \r
108         def leavePlayer(self):\r
109                 self.session.openWithCallback(self.leavePlayerConfirmed, MessageBox, "Abspielen beenden?")\r
110 \r
111         def leavePlayerConfirmed(self, answer):\r
112                 if answer:\r
113                         self.close()\r
114 \r
115         def doEofInternal(self, playing):\r
116                 pass\r
117 \r
118         def getPluginList(self):\r
119                 list = []\r
120                 for p in plugins.getPlugins(where=PluginDescriptor.WHERE_EXTENSIONSMENU):\r
121                         if p.name != "ZDF Mediathek":\r
122                                 list.append(((boundFunction(self.getPluginName, p.name), boundFunction(self.runPlugin, p), lambda: True), None))\r
123                 return list\r
124 \r
125         def showMovies(self):\r
126                 pass\r
127 \r
128 ###################################################\r
129 \r
130 def getMovieDetails(div):\r
131         list = []\r
132         # Lese Sendung...\r
133         reonecat = re.compile(r'<p class="grey"><a href="(.+?)">(.+?)</a></p>', re.DOTALL)\r
134         content = reonecat.findall(div)\r
135         if len(content):\r
136                 broadcast = decode2(decode(content[0][1])).encode("UTF-8")\r
137                 list.append(content[0][0])\r
138                 if broadcast.startswith("<"):\r
139                         broadcast = ""\r
140                 list.append(broadcast)\r
141         # Lese Titel...\r
142         reonecat = re.compile(r'<p><b><a href=".+?">(.+?)</a></b></p>', re.DOTALL)\r
143         titles = reonecat.findall(div)\r
144         if len(titles):\r
145                 title = titles[0]\r
146                 if '<br/>' in title:\r
147                         idx = title.index('<br/>')\r
148                         title = title[:idx]\r
149                 if '<br />' in title:\r
150                         idx = title.index('<br />')\r
151                         title = title[:idx]\r
152                 title = decode2(decode(title)).encode("UTF-8")\r
153                 list.append(title)\r
154         # Lese Thumbnail-URL...\r
155         reonecat = re.compile(r'<img src="(.+?)"', re.DOTALL)\r
156         thumbnails = reonecat.findall(div)\r
157         if len(thumbnails):\r
158                 list.append(thumbnails[0])\r
159         # Lese Videolänge...\r
160         if ('VIDEO, ' in div):\r
161                 reonecat = re.compile(r'>VIDEO, (.+?)</a></p>', re.DOTALL)\r
162                 lengths = reonecat.findall(div)\r
163                 if len(lengths):\r
164                         list.append(lengths[0])\r
165         else:\r
166                 list.append("Live")\r
167         # Alles gefunden?\r
168         if len(list) == 5:\r
169                 return list\r
170         else:\r
171                 return None\r
172 \r
173 def getCounts(counts):\r
174         count = counts[0]\r
175         if '">' in count:\r
176                 while '">' in count:\r
177                         idx = count.index('">')\r
178                         count = count[idx+2:]\r
179         if '"/>' in count:\r
180                 while '"/>' in count:\r
181                         idx = count.index('"/>')\r
182                         count = count[idx+3:]\r
183         return count\r
184 \r
185 def getCategoryDetails(div):\r
186         list = []\r
187         # Lese Rubrik...\r
188         reonecat = re.compile(r'<p class="grey"><a href="(.+?)">(.+?)</a></p>', re.DOTALL)\r
189         content = reonecat.findall(div)\r
190         if len(content):\r
191                 broadcast = decode2(decode(content[0][1])).encode("UTF-8")\r
192                 list.append(content[0][0])\r
193                 if broadcast.startswith("<"):\r
194                         broadcast = ""\r
195                 list.append(broadcast)\r
196         # Lese Titel...\r
197         reonecat = re.compile(r'<p><b><a href=".+?">(.+?)</a></b></p>', re.DOTALL)\r
198         titles = reonecat.findall(div)\r
199         if len(titles):\r
200                 title = titles[0]\r
201                 if '<br/>' in title:\r
202                         idx = title.index('<br/>')\r
203                         title = title[:idx]\r
204                 if '<br />' in title:\r
205                         idx = title.index('<br />')\r
206                         title = title[:idx]\r
207                 title = decode2(decode(title)).encode("UTF-8")\r
208                 list.append(title)\r
209         # Lese Thumbnail-URL...\r
210         reonecat = re.compile(r'<img src="(.+?)"', re.DOTALL)\r
211         thumbnails = reonecat.findall(div)\r
212         if len(thumbnails):\r
213                 list.append(thumbnails[0])\r
214         # Lese Beitragsanzahl...\r
215         reonecat = re.compile(r'">(.+?)BEITR&Auml;GE ZUR SENDUNG</a></p>', re.DOTALL)\r
216         counts = reonecat.findall(div)\r
217         if len(counts):\r
218                 count = getCounts(counts)\r
219                 list.append("%sBeitraege"%count)\r
220         else:\r
221                 reonecat = re.compile(r'">(.+?)BEITR&Auml;GE ZUM THEMA</a></p>', re.DOTALL)\r
222                 counts = reonecat.findall(div)\r
223                 if len(counts):\r
224                         count = getCounts(counts)\r
225                         list.append("%sBeitraege"%count)\r
226                 else:\r
227                         reonecat = re.compile(r'">(.+?)BEITR&Auml;GE ZUR RUBRIK</a></p>', re.DOTALL)\r
228                         counts = reonecat.findall(div)\r
229                         if len(counts):\r
230                                 count = getCounts(counts)\r
231                                 list.append("%sBeitraege"%count)\r
232                         else:\r
233                                 reonecat = re.compile(r'">(.+?)BEITR&Auml;GE</a></p>', re.DOTALL)\r
234                                 counts = reonecat.findall(div)\r
235                                 if len(counts):\r
236                                         count = getCounts(counts)\r
237                                         list.append("%sBeitraege"%count)\r
238         # Alles gefunden?\r
239         if len(list) == 5:\r
240                 return list\r
241         else:\r
242                 return None\r
243 \r
244 ###################################################\r
245 \r
246 def getMovieUrl(url):\r
247         req = Request(url, None, std_headers)\r
248         try:\r
249                 txt = urlopen2(req).read()\r
250         except (URLError, HTTPException, error), err:\r
251                 print "[ZDFMediaThek] Error: Unable to retrieve videopage - Error code: ", str(err)\r
252                 return ""\r
253 \r
254         if ('rtsp' in txt) and ('.mp4' in txt):\r
255                 idx = txt.index('rtsp')\r
256                 idx2 = txt.index('.mp4')\r
257                 return txt[idx:idx2+4]\r
258         if ('rtsp' in txt) and ('.sdp' in txt):\r
259                 idx = txt.index('rtsp')\r
260                 idx2 = txt.index('.sdp')\r
261                 return txt[idx:idx2+4]\r
262         elif ('mms' in txt) and ('.wmv' in txt):\r
263                 idx = txt.index('mms')\r
264                 idx2 = txt.index('.wmv')\r
265                 return txt[idx:idx2+4]\r
266         elif ('http' in txt) and ('.asx?' in txt):\r
267                 idx = txt.index('http')\r
268                 idx2 = txt.index('.asx?')\r
269                 return txt[idx:idx2+4]\r
270         elif ('mms' in txt) and ('reflector:' in txt):\r
271                 idx = txt.index('mms')\r
272                 idx2 = txt.index('" />')\r
273                 return txt[idx:idx2]\r
274         else:\r
275                 return None\r
276 \r
277 def getTitleLinks(html):\r
278         links = []\r
279         start = '<div id="breadcrumbContainer">'\r
280         end = '</div>'\r
281         if start in html:\r
282                 idx = html.index(start)\r
283                 html = html[idx:]\r
284                 idx = html.index(end)\r
285                 html = html[:idx]\r
286                 reonecat = re.compile(r'<a href="(.+?)">(.+?)</a>', re.DOTALL)\r
287                 for url, name in reonecat.findall(html):\r
288                         name = decode2(decode(name)).encode("UTF-8")\r
289                         links.append([url, name])\r
290         return links\r
291 \r
292 def getLeftMenu(html):\r
293         list = []\r
294         reonecat = re.compile(r'<div id="navigationContainer">(.+?)</div>', re.DOTALL)\r
295         leftMenu = reonecat.findall(html)\r
296         if len(leftMenu):\r
297                 reonecat = re.compile(r'<li><a href="(.+?)"(.+?)</a>', re.DOTALL)\r
298                 for url, name in reonecat.findall(leftMenu[0]):\r
299                         if name.startswith(' class="active">'):\r
300                                 active = True\r
301                                 name = name[16:]\r
302                         else:\r
303                                 active = False\r
304                                 name = name[1:]\r
305                         if (name != "Hilfe") and (not 'Podcasts' in name): # TODO: Podcasts brauchen noch etwas Arbeit... derzeit deaktiviert\r
306                                 list.append([url, name, active])\r
307         return list\r
308 \r
309 def getRightMenu(html):\r
310         list = []\r
311         print "# Suche Filme..."\r
312         if '" class="play" target="_blank">Abspielen</a></li>' in html:\r
313                 reonecat = re.compile(r'<li>(.+?) <a href="(.+?)" class="play" target="_blank">Abspielen</a></li>', re.DOTALL)\r
314                 for speed, movie in reonecat.findall(html):\r
315                         list.append([speed, movie])\r
316                 if len(list):\r
317                         return [TYPE_MOVIE, list]\r
318         print "# Suche podcasts..."\r
319         if '<!-- Start:Podcasts -->' in html:\r
320                 reonecat = re.compile(r'<!-- Start:Podcasts -->(.+?)<!-- Ende:Podcasts -->', re.DOTALL)\r
321                 tmp = reonecat.findall(html)\r
322                 if len(tmp):\r
323                         reonecat = re.compile(r'<p><b><a href="(.+?)".+?">(.+?)</a></b></p>', re.DOTALL)\r
324                         podcasts = reonecat.findall(tmp[0])\r
325                         for podcast in podcasts:\r
326                                 list.append([podcast[0], podcast[1]])\r
327                 if len(list):\r
328                         return [TYPE_PODCAST, list]\r
329         print "# Suche Videos und Rubriken..."\r
330         start = '<div class="beitragListe">'\r
331         if '<div class="beitragFooterSuche">' in html:\r
332                 end = '<div class="beitragFooterSuche">'\r
333         else:\r
334                 end = '<div class="beitragFooter">'\r
335         if (start in html) and (end in html):\r
336                 while (start in html) and (end in html):\r
337                         idx = html.index(start)\r
338                         html = html[idx:]\r
339                         reonecat = re.compile(r'%s(.+?)%s'%(start, end), re.DOTALL)\r
340                         blocks = reonecat.findall(html)\r
341                         if blocks:\r
342                                 reonecat = re.compile(r'<div class="image">(.+?)</li>', re.DOTALL)\r
343                                 divs = reonecat.findall(blocks[0])\r
344                                 for div in divs:\r
345                                         details = None\r
346                                         if ('VIDEO, ' in div) or ('>LIVE<' in div):\r
347                                                 details = getMovieDetails(div)\r
348                                         elif 'BEITR&Auml;GE' in div:    \r
349                                                 details = getCategoryDetails(div)\r
350                                         if details:\r
351                                                 list.append([details[0], details[1], details[2], details[3], details[4]])\r
352                         html = html[1:]\r
353                 reonecat = re.compile(r'<a href="(.+?)" class="weitereBeitraege"', re.DOTALL)\r
354                 more = reonecat.findall(html)\r
355                 if len(more):\r
356                         more = more[0]\r
357                         if 'href="' in more:\r
358                                 while 'href="' in more:\r
359                                         idx = more.index('href="')\r
360                                         more = more[idx+6:]\r
361                         list.append([more, "", "", "", "Weitere Beitraege laden."])\r
362         if len(list):\r
363                 return [TYPE_MOVIELIST_CATEGORY, list]\r
364         # Nichts :(\r
365         return [TYPE_NOTHING, list]\r
366 \r
367 ###################################################\r
368 \r
369 class LeftMenuList(MenuList):\r
370         def __init__(self):\r
371                 MenuList.__init__(self, [], False, eListboxPythonMultiContent)\r
372                 self.l.setItemHeight(20)\r
373                 self.l.setFont(0, gFont("Regular", 18))\r
374                 self.menu = []\r
375                 self.active = True\r
376                 self.current = 0\r
377 \r
378         def setActive(self, active):\r
379                 self.active = active\r
380                 self.SetList(self.menu, True)\r
381 \r
382         def entry(self, text, active, selected):\r
383                 res = [(text)]\r
384                 if text.startswith("- Heute"):\r
385                         text = "- Heute"\r
386                 elif text.startswith("- Gestern"):\r
387                         text = "- Gestern"\r
388                 elif text.startswith("- Morgen"):\r
389                         text = "- Morgen"\r
390                 if selected:\r
391                         res.append(MultiContentEntryPixmapAlphaTest(pos=(0, 0), size=(20, 20), png=LoadPixmap(cached=True, path=PNG_PATH+"active.png")))\r
392                 if active:\r
393                         res.append(MultiContentEntryText(pos=(25, 0), size=(175, 20), font=0, text=text, color=0xf47d19))\r
394                 else:\r
395                         res.append(MultiContentEntryText(pos=(25, 0), size=(175, 20), font=0, text=text, color=0xffffff))\r
396                 return res\r
397 \r
398         def SetList(self, l, moveCursor=False):\r
399                 del self.menu\r
400                 self.menu = l\r
401                 if moveCursor:\r
402                         idx = 0\r
403                         for x in l:\r
404                                 if x[2]:\r
405                                         self.current = idx\r
406                                 idx += 1\r
407                 list = []\r
408                 idx = 0\r
409                 for x in l:\r
410                         if (idx == self.current) and self.active:\r
411                                 selected = True\r
412                         else:\r
413                                 selected = False\r
414                         list.append(self.entry(x[1], x[2], selected))\r
415                         idx += 1\r
416                 self.setList(list)\r
417 \r
418         def getCurrentUrl(self):\r
419                 if len(self.menu):\r
420                         return self.menu[self.current][0]\r
421                 else:\r
422                         return None\r
423 \r
424         def select(self, index):\r
425                 if len(self.menu):\r
426                         if (index > -1) and (index < len(self.menu)):\r
427                                 self.current = index\r
428                                 self.SetList(self.menu)\r
429 \r
430         def first(self):\r
431                 self.select(0)\r
432 \r
433         def last(self):\r
434                 self.select(len(self.menu)-1)\r
435 \r
436         def previous(self):\r
437                 if len(self.menu):\r
438                         self.select(self.current-1)\r
439 \r
440         def next(self):\r
441                 if len(self.menu):\r
442                         self.select(self.current+1)\r
443 \r
444 ###################################################\r
445 \r
446 def RightMenuEntryPixmap(thumbID, png_cache):\r
447         png = png_cache.get(thumbID, None)\r
448         if png is None:\r
449                 png = png_cache.get("missing", None)\r
450                 if png is None:\r
451                         png = LoadPixmap(resolveFilename(SCOPE_CURRENT_PLUGIN, "Extensions/ZDFMediathek/logo.png"))\r
452                         png_cache["missing"] = png\r
453         return png\r
454 \r
455 \r
456 class RightMenuList(List):\r
457         \r
458         png_cache = {}\r
459         \r
460         def __init__(self, list = [ ], enableWrapAround=False):\r
461                 List.__init__(self, list, enableWrapAround, item_height = 50 )\r
462                 self.pixmaps_to_load = []\r
463                 self.picloads = {}\r
464                 self.listCompleted = []\r
465                 self.lastListLength = 0\r
466                 self.lastIndex = 0\r
467                 self.callback = None\r
468                 self.idx = 0\r
469                 self.thumb = ""\r
470                 self.active = True\r
471                 self.ListUpdate = False\r
472 \r
473         def setActive(self, active):\r
474                 self.active = active\r
475 \r
476         def buildEntries(self):\r
477                 if self.type == TYPE_PODCAST:\r
478                         list = []\r
479                         for x in self.list:\r
480                                 title = x[1]\r
481                                 if '<br/>' in title:\r
482                                         idx = title.index('<br/>')\r
483                                         title = title[:idx]\r
484                                 title = decode2(decode(title)).encode("UTF-8")\r
485                                 res = [(x[0], title)]\r
486                                 res.append(MultiContentEntryText(pos=(0, 0), size=(430, 20), font=0, text=title))\r
487                                 list.append(res)\r
488                         self.setList(list)\r
489                         if self.callback:\r
490                                 self.callback()\r
491                 elif self.type == TYPE_MOVIELIST_CATEGORY:\r
492                         for entry in self.list:\r
493                                 if entry[4] != "Weitere Beitraege laden.":\r
494                                         self.listCompleted.append(( entry[0],entry[1],entry[2],entry[3],entry[4],entry[3].rsplit("/",1)[1]))\r
495                                 else:\r
496                                         self.listCompleted.append(( entry[0],entry[1],entry[2],entry[3],entry[4], None))\r
497 \r
498         def buildEntry(self, vurl, txt1, title, turl, txt2, thumbid):\r
499                 #print "[ZDF Mediathek - buildEntry ] --> ", txt1, title, txt2, thumbid\r
500                 menupng = None\r
501                 if self.png_cache.get(thumbid, None) is None:\r
502                         if thumbid is not None:\r
503                                 self.pixmaps_to_load.append(thumbid)\r
504                                 self.downloadThumbnail(turl)\r
505                         else:\r
506                                 menupng = RightMenuEntryPixmap(thumbid, self.png_cache)\r
507                 else:\r
508                         menupng = RightMenuEntryPixmap(thumbid, self.png_cache)\r
509                 return(( vurl, txt1, title, turl, txt2, thumbid, menupng ))\r
510 \r
511         def getMovieCategoryIndexByThumbID(self, ThumbID):\r
512                 idx = 0\r
513                 for entry in self.listCompleted:\r
514                         if entry[5] == ThumbID:\r
515                                 return idx\r
516                         idx += 1\r
517                 return None\r
518 \r
519         def downloadThumbnail(self,thumbUrl):\r
520                 if thumbUrl is not None:\r
521                         thumbID = thumbUrl.rsplit("/",1)[1]\r
522                         thumbFile = None\r
523                         if not thumbUrl.startswith("http://"):\r
524                                 thumbUrl = "%s%s"%(MAIN_PAGE, thumbUrl)\r
525                         try:\r
526                                 req = urllib2.Request(thumbUrl)\r
527                                 url_handle = urllib2.urlopen(req)\r
528                                 headers = url_handle.info()\r
529                                 contentType = headers.getheader("content-type")\r
530                         except:\r
531                                 contentType = None\r
532 \r
533                         if contentType:\r
534                                 if 'image/jpeg' in contentType:\r
535                                         thumbFile = "/tmp/" + thumbID + ".jpg"\r
536                                 elif 'image/gif' in contentType:\r
537                                         thumbID = None\r
538                                 #       thumbFile = "/tmp/" + thumbID + ".gif"\r
539                                 elif 'image/png' in contentType:\r
540                                         thumbFile = "/tmp/" + thumbID + ".png"\r
541                                 else:\r
542                                         print "[ZDF Mediathek] Unknown thumbnail content-type:", contentType\r
543                         if thumbFile is not None:\r
544                                 if (os_path.exists(thumbFile) == True): #already downloaded\r
545                                         self.downloadThumbnailCallback(None, thumbFile, thumbID)\r
546                                 else:\r
547                                         if self.png_cache.get(thumbID, None) is None:\r
548                                                 downloadPage(thumbUrl, thumbFile).addCallback(self.downloadThumbnailCallback, thumbFile, thumbID).addErrback(self.downloadThumbnailError, thumbID)\r
549                                         else:\r
550                                                 self.updateEntry(thumbID, thumbFile)\r
551 \r
552         def downloadThumbnailError(self, err, thumbID):\r
553                 self.pixmaps_to_load.remove(thumbID)\r
554                 print "[ZDF Mediathek] downloadThumbnailError:", thumbID, err.getErrorMessage()\r
555 \r
556         def downloadThumbnailCallback(self, txt, thumbFile, thumbID):\r
557                 if (os_path.exists( thumbFile) == True):\r
558                         self.pixmaps_to_load.remove(thumbID)\r
559                         sc = AVSwitch().getFramebufferScale()\r
560                         self.picloads[thumbID] = ePicLoad()\r
561                         self.picloads[thumbID].PictureData.get().append(boundFunction(self.finishedThumbnailDecode, thumbID, thumbFile))\r
562                         self.picloads[thumbID].setPara((94, 60, sc[0], sc[1], False, 1, "#00000000"))\r
563                         self.picloads[thumbID].startDecode(thumbFile)\r
564 \r
565         def finishedThumbnailDecode(self, thumbID = "", thumbFile = "", picInfo = None):\r
566                 ptr = self.picloads[thumbID].getData()\r
567                 if ptr != None:\r
568                         self.png_cache[thumbID] = ptr\r
569                         del self.picloads[thumbID]\r
570                         self.updateEntry(thumbID, thumbFile)\r
571 \r
572         def updateEntry(self, thumbID, thumbFile):\r
573                 if (os_path.exists(thumbFile) == True):\r
574                         os_remove(thumbFile)\r
575                 idx = self.getMovieCategoryIndexByThumbID(thumbID)\r
576                 if idx is not None:\r
577                         print "[ZDF Mediathek] updateEntry", thumbID, thumbFile, idx\r
578                         self.entry_changed(idx)\r
579 \r
580         def SetList(self, l):\r
581                 if self.ListUpdate:\r
582                         self.lastIndex = self.index\r
583                         self.lastListLength = len(self.list)\r
584                 else:\r
585                         self.lastIndex = 0\r
586                         self.lastListLength = len(l[1])                 \r
587                 self.type = l[0]\r
588                 self.list = l[1]\r
589                 if self.type == TYPE_PODCAST:\r
590                         self.buildEntries()\r
591                 elif self.type == TYPE_MOVIELIST_CATEGORY:\r
592                         del self.listCompleted\r
593                         self.listCompleted = []\r
594                         self.idx = 0\r
595                         self.buildEntries()\r
596                         if len(self.listCompleted):\r
597                                 if self.ListUpdate:\r
598                                         if len(self.list) > self.lastListLength:\r
599                                                 self.updateList(self.listCompleted)\r
600                                                 self.setIndex(self.lastIndex)\r
601                                         else:\r
602                                                 self.setBuildFunc(self.buildEntry)\r
603                                                 self.setList(self.listCompleted)\r
604                                         self.ListUpdate = False\r
605                                 else:\r
606                                         self.setBuildFunc(self.buildEntry)\r
607                                         self.setList(self.listCompleted)\r
608                                 if self.callback:\r
609                                         self.callback()\r
610                 else:\r
611                         self.setList([])\r
612                         if self.callback:\r
613                                 self.callback()\r
614 \r
615 ###################################################\r
616 \r
617 class ZDFMediathekCache(Screen):\r
618         skin = """\r
619                 <screen position="center,center" size="76,76" flags="wfNoBorder" backgroundColor="#ffffff" >\r
620                         <eLabel position="2,2" zPosition="1" size="72,72" font="Regular;18" backgroundColor="#252525" />\r
621                         <widget name="spinner" position="14,14" zPosition="2" size="48,48" alphatest="on" />\r
622                 </screen>"""\r
623 \r
624         def __init__(self, session):\r
625                 self.session = session\r
626                 Screen.__init__(self, session)\r
627                 \r
628                 self["spinner"] = Pixmap()\r
629                 self.curr = 0\r
630                 \r
631                 self.timer = eTimer()\r
632                 self.timer.callback.append(self.showNextSpinner)\r
633 \r
634         def start(self):\r
635                 self.show()\r
636                 self.timer.start(200, False)\r
637 \r
638         def stop(self):\r
639                 self.hide()\r
640                 self.timer.stop()\r
641 \r
642         def showNextSpinner(self):\r
643                 self.curr += 1\r
644                 if self.curr > 10:\r
645                         self.curr = 0\r
646                 png = LoadPixmap(cached=True, path=PNG_PATH + str(self.curr) + ".png")\r
647                 self["spinner"].instance.setPixmap(png)\r
648 \r
649 ###################################################\r
650 \r
651 TYPE_NOTHING = 0\r
652 TYPE_MOVIE = 1\r
653 TYPE_PODCAST = 2\r
654 TYPE_MOVIELIST_CATEGORY = 3\r
655 \r
656 LIST_LEFT = 0\r
657 LIST_RIGHT = 1\r
658 LIST_NONE = 2\r
659 \r
660 class ZDFMediathek(Screen, HelpableScreen):\r
661         desktop = getDesktop(0)\r
662         size = desktop.size()\r
663         width = size.width()\r
664         if width == 720:\r
665                 skin = """<screen name="ZDFMediathek" position="0,0" size="720,576" title="ZDF Mediathek" flags="wfNoBorder" backgroundColor="#252525" >\r
666                                 <ePixmap position="20,30" size="133,40" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/ZDFMediathek/logo.png" />\r
667                                 <widget name="navigationTitle" position="250,40" size="430,25" font="Regular;18" backgroundColor="#252525" foregroundColor="#f47d19" noWrap="1" />\r
668                                 <widget name="leftList" position="20,70" size="220,440" transparent="1" selectionDisabled="1" />\r
669                                 <widget source="rightList" render="Listbox" position="250,70" size="430,480" backgroundColor="#3d3c3c" backgroundColorSelected="#565656" scrollbarMode="showOnDemand">\r
670                                         <convert type="TemplatedMultiContent">\r
671                                         {"templates":\r
672                                                 {"default": (60,[\r
673                                                                 MultiContentEntryPixmapAlphaTest(pos = (0,0), size = (94,60), png = 6),\r
674                                                                 MultiContentEntryText(pos = (100, 0), size = (430, 20), font = 0, text = 2),\r
675                                                                 MultiContentEntryText(pos = (100, 20), size = (430, 20), font = 0, text = 4),\r
676                                                                 MultiContentEntryText(pos = (100, 40), size = (430, 20), font = 1, text = 1),\r
677                                                         ]),\r
678                                                 },\r
679                                                 "fonts": [gFont("Regular", 20), gFont("Regular", 18)],\r
680                                                 "itemHeight": 60\r
681                                         }\r
682                                         </convert>\r
683                                 </widget>\r
684                                 <ePixmap pixmap="skin_default/buttons/key_menu.png" position="20,520" size="35,25" transparent="1" alphatest="on" />\r
685                                 <widget name="serverName" position="60,520" size="160,20" font="Regular;18" backgroundColor="#252525" foregroundColor="#f47d19" />\r
686                                 <widget name="fakeList" position="0,0" size="0,0" />\r
687                         </screen>"""\r
688         else:\r
689                 skin = """<screen name="ZDFMediathek" position="center,center" size="900,580" title="ZDF Mediathek" backgroundColor="#252525" >\r
690                                 <ePixmap position="20,30" size="133,40" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/ZDFMediathek/logo.png" />\r
691                                 <widget name="navigationTitle" position="250,40" size="430,25" font="Regular;18" backgroundColor="#252525" foregroundColor="#f47d19" noWrap="1" />\r
692                                 <widget name="leftList" position="20,70" size="220,440" transparent="1" selectionDisabled="1" />\r
693                                 <widget source="rightList" render="Listbox" position="250,70" size="600,496" backgroundColor="#3d3c3c" backgroundColorSelected="#565656" scrollbarMode="showOnDemand">\r
694                                         <convert type="TemplatedMultiContent">\r
695                                         {"templates":\r
696                                                 {"default": (62,[\r
697                                                                 MultiContentEntryPixmapAlphaTest(pos = (2,1), size = (94,60), png = 6),\r
698                                                                 MultiContentEntryText(pos = (100, 0), size = (500, 20), font = 0, text = 2),\r
699                                                                 MultiContentEntryText(pos = (100, 20), size = (500, 20), font = 0, text = 4),\r
700                                                                 MultiContentEntryText(pos = (100, 40), size = (500, 20), font = 1, text = 1),\r
701                                                         ]),\r
702                                                 },\r
703                                                 "fonts": [gFont("Regular", 20), gFont("Regular", 18)],\r
704                                                 "itemHeight": 62\r
705                                         }\r
706                                         </convert>\r
707                                 </widget>\r
708                                 <ePixmap pixmap="skin_default/buttons/key_menu.png" position="20,540" size="35,25" transparent="1" alphatest="on" />\r
709                                 <widget name="serverName" position="60,540" size="160,20" font="Regular;18" backgroundColor="#252525" foregroundColor="#f47d19" />\r
710                                 <widget name="fakeList" position="0,0" size="0,0" />\r
711                         </screen>"""\r
712 \r
713         def __init__(self, session):\r
714                 self.session = session\r
715                 \r
716                 Screen.__init__(self, session)\r
717                 \r
718                 self["navigationTitle"] = Label(" ")\r
719                 self["leftList"] = LeftMenuList()\r
720                 self["rightList"] = RightMenuList()\r
721                 self["fakeList"] = MenuList([])\r
722                 self["serverName"] = Label("Server")\r
723                 \r
724                 HelpableScreen.__init__(self)\r
725                 \r
726                 self["actions"] = HelpableActionMap(self, "ZDFMediathekActions",\r
727                         {\r
728                                 "back": (self.exit, "Beenden"),\r
729                                 "ok": (self.ok, "Selektieren"),\r
730                                 "left": (self.left, "Seite hoch"),\r
731                                 "right": (self.right, "Seite runter"),\r
732                                 "up": (self.up, "Hoch"),\r
733                                 "down": (self.down, "Runter"),\r
734                                 "previousList": (self.toggleList, "Liste umschalten"),\r
735                                 "nextList": (self.toggleList, "Liste umschalten"),\r
736                                 "menu": (self.selectServer, "Selektiere Server"),\r
737                                 "search": (self.search, "Suche"),\r
738                                 "previousPage": (self.previousPage, "Vorherige Seite")\r
739                         }, -2)\r
740                 \r
741                 self.cacheDialog = self.session.instantiateDialog(ZDFMediathekCache)\r
742                 self["rightList"].callback = self.deactivateCacheDialog\r
743                 self.working = False\r
744                 self.currentList = LIST_RIGHT\r
745                 self.linkPreviousPage = ""\r
746                 \r
747                 self.transcodeServer = None\r
748                 self.cacheTimer = eTimer()\r
749                 self.cacheTimer.callback.append(self.chechCachedFile)\r
750                 \r
751                 self.onLayoutFinish.append(self.getPage)\r
752                 self.onClose.append(self.__onClose)\r
753 \r
754         def __onClose(self):\r
755                 del self.cacheTimer\r
756                 self["rightList"].png_cache = {}\r
757 \r
758         def getPage(self, page=None):\r
759                 self.working = True\r
760                 if not page:\r
761                         page = "/ZDFmediathek/hauptnavigation/startseite?flash=off"\r
762                 url = "%s%s"%(MAIN_PAGE, page)\r
763                 getPage(url).addCallback(self.gotPage).addErrback(self.error)\r
764 \r
765         def error(self, err=""):\r
766                 print "[ZDF Mediathek] Error:", err\r
767                 self.working = False\r
768                 self.deactivateCacheDialog()\r
769 \r
770         def gotPage(self, html=""):\r
771                 rightMenu = getRightMenu(html)\r
772                 if rightMenu[0] == TYPE_MOVIE:\r
773                         tmplist = []\r
774                         for x in rightMenu[1]:\r
775                                 if PLAY_WMV:\r
776                                         if x[1].endswith(".asx"):\r
777                                                 if len(x[0]) > 8:\r
778                                                         x[0] = x[0].split("<li>")[-1]\r
779                                                 tmplist.append(("%s %s"%(x[0], x[1].split(".")[-1]), x[1]))\r
780                                         if x[1].endswith(".mov"):\r
781                                                 continue\r
782                                 else:\r
783                                         if x[1].endswith(".asx"):\r
784                                                 continue\r
785                                         tmplist.append(("%s %s"%(x[0], x[1].split(".")[-1]), x[1]))\r
786                         if len(tmplist):\r
787                                 if len(tmplist) == 1:  #only one entry, play directly.\r
788                                         self.play(tmplist[0])\r
789                                 else:\r
790                                         entry = None\r
791                                         for x in tmplist:\r
792                                                 if "DSL 2000" in x[0]:\r
793                                                         entry = x\r
794                                         if entry:\r
795                                                 self.play(entry)\r
796                                         else:\r
797                                                 self.session.openWithCallback(self.play, ChoiceBox, title="Selektiere...", list=tmplist)\r
798                         else:\r
799                                 self.working = False\r
800                 else:\r
801                         self.cacheDialog.start()\r
802                         self.currentList = LIST_NONE\r
803                         links = getTitleLinks(html)\r
804                         txt = ""\r
805                         for x in links:\r
806                                 txt = txt + x[1] + " / "\r
807                         if len(txt) > 1:\r
808                                 txt = txt[:-3]\r
809                                 if (len(links) > 1):\r
810                                         self.linkPreviousPage = links[-2][0]\r
811                                 else:\r
812                                         self.linkPreviousPage = ""\r
813                         else:\r
814                                 self.linkPreviousPage = ""\r
815                         self["navigationTitle"].setText(txt)\r
816                         self["leftList"].SetList(getLeftMenu(html), True)\r
817                         self["rightList"].SetList(rightMenu)\r
818                         self["leftList"].selectionEnabled(0)\r
819                         self["rightList"].setSelectionEnabled(1)\r
820                         self["rightList"].setActive(True)\r
821                         self["fakeList"].selectionEnabled(0)\r
822                         self["leftList"].setActive(False)\r
823 \r
824         def previousPage(self):\r
825                 self.getPage(self.linkPreviousPage)\r
826 \r
827         def search(self):\r
828                 self.session.openWithCallback(self.searchCallback, VirtualKeyBoard, title="Suche nach:")\r
829 \r
830         def searchCallback(self, callback):\r
831                 if callback and (callback != ""):\r
832                         self.getPage("/ZDFmediathek/suche?sucheText=%s&offset=0&flash=off"%(callback.replace(" ", "+")))\r
833 \r
834         def play(self, callback):\r
835                 self.working = False\r
836                 if callback is not None:\r
837                         url = callback[1]\r
838                         if url.endswith(".mov") or url.endswith(".asx"):\r
839                                 url = getMovieUrl(url)\r
840                                 if url and url.endswith(".asx"):\r
841                                         newurl = getMovieUrl(url)\r
842                                         if newurl:\r
843                                                 url = newurl\r
844                         print "[ZDFMediathek]->PLAY:",url\r
845                         if url:\r
846                                 if PLAY_MP4 and url.endswith(".mp4"):\r
847                                         ref = eServiceReference(4097, 0, url)\r
848                                         self.session.open(ChangedMoviePlayer, ref)\r
849                                 elif PLAY_MP4 and url.startswith("rtsp") and url.endswith(".sdp"):\r
850                                         ref = eServiceReference(4097, 0, url)\r
851                                         self.session.open(ChangedMoviePlayer, ref)\r
852                                 elif PLAY_WMV and url.endswith(".wmv"):\r
853                                         ref = eServiceReference(4097, 0, url)\r
854                                         self.session.open(ChangedMoviePlayer, ref)\r
855                                 elif PLAY_WMV and url.startswith("mms") and "reflector:" in url:\r
856                                         ref = eServiceReference(4097, 0, url)\r
857                                         self.session.open(ChangedMoviePlayer, ref)\r
858                                 else: # Die Hardware kann das Format nicht direkt abspielen, mit Stream2Dream oder vlc Server probieren...\r
859                                         if self.transcodeServer is not None:\r
860                                                 if self.transcodeServer == "LT Stream2Dream":\r
861                                                         r = streamplayer.play(url)\r
862                                                         if r == "ok":\r
863                                                                 sleep(6)\r
864                                                                 self.currentList = LIST_NONE\r
865                                                                 self.cacheDialog.start()\r
866                                                                 self.cacheTimer.start(1000, False)\r
867                                                 else:\r
868                                                         self.transcodeServer.play(self.session, url, self["rightList"].getCurrent()[1])\r
869                                         else:\r
870                                                 self.session.open(MessageBox, "Es wurde kein Server ausgewählt!", MessageBox.TYPE_ERROR)\r
871                         else:\r
872                                 self.session.open(MessageBox, "Fehler beim Ermitteln der Film-URL!", MessageBox.TYPE_ERROR)\r
873 \r
874         def chechCachedFile(self):\r
875                 try:\r
876                         f = open ("/tmp/mpstream/progress.txt")\r
877                         content = f.read()\r
878                         f.close()\r
879                         list = content.split("-")\r
880                         cacheMB = int(list[0])\r
881                         if cacheMB > 10: # Starte nach 10 MB Bufferung\r
882                                 self.cacheTimer.stop()\r
883                                 self.playCachedFile()\r
884                 except:\r
885                         pass\r
886 \r
887         def deactivateCacheDialog(self):\r
888                 self.cacheDialog.stop()\r
889                 self.currentList = LIST_RIGHT\r
890                 self.working = False\r
891 \r
892         def playCachedFile(self):\r
893                 self.deactivateCacheDialog()\r
894                 ref = eServiceReference(1, 0, "/tmp/mpstream/MPStream.ts")\r
895                 self.session.openWithCallback(self.stopStream2Dream, ChangedMoviePlayer, ref)\r
896 \r
897         def stopStream2Dream(self, callback=None):\r
898                 streamplayer.stop()\r
899                 sleep(4)\r
900 \r
901         def toggleList(self):\r
902                 if not self.working:\r
903                         if self.currentList == LIST_LEFT:\r
904                                 self.currentList = LIST_RIGHT\r
905                                 self["leftList"].setActive(False)\r
906                                 self["fakeList"].selectionEnabled(0)\r
907                                 self["rightList"].setSelectionEnabled(1)\r
908                                 self["rightList"].setActive(True)\r
909                                 self["rightList"].ListUpdate = False\r
910                         elif self.currentList == LIST_RIGHT:\r
911                                 self.currentList = LIST_LEFT\r
912                                 self["leftList"].setActive(True)\r
913                                 self["rightList"].setSelectionEnabled(0)\r
914                                 self["rightList"].setActive(False)\r
915                                 self["rightList"].ListUpdate = False\r
916                                 self["fakeList"].selectionEnabled(1)\r
917 \r
918         def selectServer(self):\r
919                 list = []\r
920                 if streamplayer:\r
921                         list.append(("LT Stream2Dream", "LT Stream2Dream"))\r
922                 if vlcServerConfig:\r
923                         serverList = vlcServerConfig.getServerlist()\r
924                         for x in serverList:\r
925                                 list.append((x.getName(), x))\r
926                 if len(list):\r
927                         self.session.openWithCallback(self.serverChosen, ChoiceBox, title="Waehle den Server...", list=list)\r
928 \r
929         def serverChosen(self, callback):\r
930                 if callback:\r
931                         server = callback[1]\r
932                         if server == "LT Stream2Dream":\r
933                                 if not streamplayer.connected:\r
934                                         self.transcodeServer = "LT Stream2Dream"\r
935                                         self["serverName"].setText("LT Stream2Dream")\r
936                                         self.connectToStream2Dream()\r
937                         else:\r
938                                 if streamplayer:\r
939                                         if streamplayer.connected:\r
940                                                 streamplayer.logout()\r
941                                 self.transcodeServer = server\r
942                                 self["serverName"].setText(server.getName())\r
943 \r
944         def connectToStream2Dream(self):\r
945                 streamplayer.login()\r
946                 try:\r
947                         list = listdir("/tmp/mp")\r
948                 except:\r
949                         list = []\r
950                 if len(list) < 2:\r
951                         self.session.open(MessageBox, "Die Verbindung zu LT Stream2Dream konnte nicht hergestellt werden!", MessageBox.TYPE_ERROR)\r
952                         streamplayer.logout()\r
953                         self.transcodeServer = None\r
954                         self["serverName"].setText("Server")\r
955 \r
956         def exit(self):\r
957                 if not self.working:\r
958                         if self.currentList == LIST_LEFT:\r
959                                 self.toggleList()\r
960                         elif self.currentList == LIST_RIGHT:\r
961                                 if streamplayer:\r
962                                         if streamplayer.connected:\r
963                                                 streamplayer.logout()\r
964                                 self.session.deleteDialog(self.cacheDialog)\r
965                                 self.close()\r
966                         else:\r
967                                 if streamplayer:\r
968                                         if streamplayer.connected:\r
969                                                 streamplayer.stop()\r
970                                                 sleep(4)\r
971                                 self.deactivateCacheDialog()\r
972 \r
973         def ok(self):\r
974                 if not self.working:\r
975                         if self.currentList == LIST_LEFT:\r
976                                 self.getPage(self["leftList"].getCurrentUrl())\r
977                         elif self.currentList == LIST_RIGHT:\r
978                                 curr = self["rightList"].getCurrent()\r
979                                 if curr:\r
980                                         if curr[4] == "Weitere Beitraege laden.":\r
981                                                 self["rightList"].ListUpdate = True\r
982                                         else:\r
983                                                 self["rightList"].ListUpdate = False\r
984                                         self.getPage(curr[0])\r
985                         elif streamplayer:\r
986                                 if streamplayer.connected:\r
987                                         if streamplayer.caching or streamplayer.streaming:\r
988                                                 self.playCachedFile()\r
989 \r
990         def left(self):\r
991                 if not self.working:\r
992                         if self.currentList == LIST_LEFT:\r
993                                 self["leftList"].first()\r
994                         elif self.currentList == LIST_RIGHT and self["rightList"].active:\r
995                                 self["rightList"].pageUp()\r
996 \r
997         def right(self):\r
998                 if not self.working:\r
999                         if self.currentList == LIST_LEFT:\r
1000                                 self["leftList"].last()\r
1001                         elif self.currentList == LIST_RIGHT and self["rightList"].active:\r
1002                                 self["rightList"].pageDown()\r
1003 \r
1004         def up(self):\r
1005                 if not self.working:\r
1006                         if self.currentList == LIST_LEFT:\r
1007                                 self["leftList"].previous()\r
1008                         elif self.currentList == LIST_RIGHT and self["rightList"].active:\r
1009                                 self["rightList"].selectPrevious()\r
1010 \r
1011         def down(self):\r
1012                 if not self.working:\r
1013                         if self.currentList == LIST_LEFT:\r
1014                                 self["leftList"].next()\r
1015                         elif self.currentList == LIST_RIGHT and self["rightList"].active:\r
1016                                 self["rightList"].selectNext()\r
1017 \r
1018 ###################################################\r
1019 \r
1020 def start(session, **kwargs):\r
1021         session.open(ZDFMediathek)\r
1022 \r
1023 def Plugins(**kwargs):\r
1024         return PluginDescriptor(name="ZDF Mediathek", description="Streame von der ZDF Mediathek", where=[PluginDescriptor.WHERE_EXTENSIONSMENU, PluginDescriptor.WHERE_PLUGINMENU], fnc=start)\r