Fix linebreaks
[vuplus_dvbapp-plugin] / ardmediathek / src / plugin.py
1 # -*- coding: UTF-8 -*-
2 # ARD Mediathek by AliAbdul\r
3 from Components.ActionMap import ActionMap
4 from Components.AVSwitch import AVSwitch\r
5 from Components.Label import Label\r
6 from Components.MenuList import MenuList\r
7 from Components.MultiContent import MultiContentEntryText, MultiContentEntryPixmapAlphaTest
8 from Components.Pixmap import Pixmap
9 from Components.PluginComponent import plugins\r
10 from enigma import eListboxPythonMultiContent, ePicLoad, eServiceReference, eTimer, getDesktop, gFont
11 from os import listdir
12 from Plugins.Plugin import PluginDescriptor
13 from Screens.ChoiceBox import ChoiceBox
14 from Screens.InfoBar import MoviePlayer
15 from Screens.MessageBox import MessageBox\r
16 from Screens.Screen import Screen
17 from time import sleep
18 from Tools.BoundFunction import boundFunction
19 from Tools.Directories import resolveFilename, SCOPE_PLUGINS
20 from Tools.LoadPixmap import LoadPixmap\r
21 from twisted.web.client import downloadPage, getPage\r
22 import re, urllib2
23
24 ###################################################
25
26 MAIN_PAGE = "http://www.ardmediathek.de"
27
28 PNG_PATH = resolveFilename(SCOPE_PLUGINS)+"/Extensions/ARDMediathek/"
29
30 try:
31         from LT.LTStreamPlayer import streamplayer
32 except ImportError:
33         try:
34                 from Plugins.Extensions.LTMediaCenter.LTStreamPlayer import streamplayer
35         except ImportError:
36                 streamplayer = None
37
38 try:
39         from Plugins.Extensions.VlcPlayer.VlcServerConfig import vlcServerConfig
40 except ImportError:
41         vlcServerConfig = None
42
43 ###################################################
44
45 def encodeHtml(html):
46         html = html.replace("&", "&")
47         html = html.replace("&lt;", "<")
48         html = html.replace("&gt;", ">")
49         html = html.replace("&#39;", "'")
50         html = html.replace("&quot;", '"')
51         html = html.replace("&#42;", "*")
52         html = html.replace("&#124;", "|")
53         html = html.replace("&#034;", '"')
54         html = html.replace("&#039;", "'")
55         return html
56
57 ###################################################
58
59 class ChangedMoviePlayer(MoviePlayer):
60         def __init__(self, session, service):
61                 MoviePlayer.__init__(self, session, service)
62                 self.skinName = "MoviePlayer"
63
64         def leavePlayer(self):
65                 self.session.openWithCallback(self.leavePlayerConfirmed, MessageBox, "Abspielen beenden?")
66
67         def leavePlayerConfirmed(self, answer):
68                 if answer:
69                         self.close()
70
71         def doEofInternal(self, playing):
72                 pass
73
74         def getPluginList(self):
75                 list = []
76                 for p in plugins.getPlugins(where=PluginDescriptor.WHERE_EXTENSIONSMENU):
77                         if p.name != "ARD Mediathek":
78                                 list.append(((boundFunction(self.getPluginName, p.name), boundFunction(self.runPlugin, p), lambda: True), None))
79                 return list
80
81         def showMovies(self):
82                 pass
83
84 ###################################################
85
86 def getCategories(html):
87         list = []
88         start = """<div class="mt-reset mt-categories">"""
89         end = '</div>'
90         if start and end in html:
91                 idx = html.index(start)
92                 html = html[idx:]
93                 idx = html.index(end)
94                 html = html[:idx]
95                 reonecat = re.compile(r'<li><a href="(.+?)" title="">(.+?)</a></li>', re.DOTALL)
96                 for url, name in reonecat.findall(html):
97                         list.append([MAIN_PAGE + url, encodeHtml(name)])
98         return list
99
100 def getMovies(html):
101         list = []
102         start = '<li class="zelle'
103         end = '</li>'
104         if (start in html) and (end in html):
105                 while (start in html) and (end in html):
106                         idx = html.index(start)
107                         html = html[idx:]
108                         idx = html.index(end)
109                         div = html[:idx]
110                         html = html[idx:]
111                         reonecat = re.compile(r'<img src="(.+?)"', re.DOTALL)
112                         thumbs = reonecat.findall(div)
113                         if len(thumbs):
114                                 thumb = MAIN_PAGE + thumbs[0]
115                                 if thumb.endswith("drop.jpg") and len(thumbs) > 1:
116                                         thumb = MAIN_PAGE + thumbs[1]
117                         else:
118                                 thumb = None
119                         reonecat = re.compile(r'<a href="(.+?)"', re.DOTALL)
120                         urls = reonecat.findall(div)
121                         if len(urls):
122                                 url = MAIN_PAGE + urls[0]
123                         else:
124                                 url = None
125                         reonecat = re.compile(r'<span class="beitragstitel"><strong>(.+?)</strong></span>', re.DOTALL)
126                         titles = reonecat.findall(div)
127                         if len(titles):
128                                 title = encodeHtml(titles[0])
129                         else:
130                                 title = None
131                         reonecat = re.compile(r'<span class="infotext">(.+?)</span><br />', re.DOTALL)
132                         infos = reonecat.findall(div)
133                         if len(infos):
134                                 info = encodeHtml(infos[0])
135                         else:
136                                 info = None
137                         reonecat = re.compile(r'<span class="cliplaenge">(.+?)</span>', re.DOTALL)
138                         lengths = reonecat.findall(div)
139                         if len(lengths):
140                                 length = lengths[0]
141                         else:
142                                 length = None
143                         if title and info and length and url and thumb:
144                                 list.append([title, info, length, url, thumb])
145         return list
146
147 def getMovieUrls(url):
148         try:\r
149                 f = urllib2.urlopen(url)\r
150                 html = f.read()\r
151                 f.close()
152         except:
153                 html = ""
154         list = []
155         if 'player.avaible_url' in html:
156                 content = html.split("\n")
157                 for line in content:
158                         if 'player.avaible_url' in line:
159                                 reonecat = re.compile(r'player.avaible_url(.+?) = "(.+?)";', re.DOTALL)
160                                 for tmp, url in reonecat.findall(line):
161                                         if 'flashmedia' in tmp:
162                                                 type = "flv"
163                                         elif 'microsoftmedia' in tmp:
164                                                 type = "wmv"
165                                         else:
166                                                 type = "n/a"
167                                         if not url.startswith("rtmpt"):
168                                                 list.append([type, url])
169         return list
170
171 def getPageNavigation(html):
172         list = []
173         start = '<!-- ANFANG navigation folge seiten -->'
174         end = '<!-- ENDE navigation folgeseiten -->'
175         if (start in html) and (end in html):
176                 idx = html.index(start)
177                 idx2 = html.index(end)
178                 lines = html[idx:idx2].split("\n")
179                 for line in lines:
180                         if ('<strong>' in line) and ('</strong>' in line):
181                                 reonecat = re.compile(r'<strong>(.+?)</strong>', re.DOTALL)
182                                 pages = reonecat.findall(line)
183                                 if len(pages):
184                                         page = pages[0]
185                                         if 'left aktiv' in line:
186                                                 list.append([page, None])
187                                         else:
188                                                 reonecat = re.compile(r'<a href="(.+?)"><strong>', re.DOTALL)
189                                                 urls = reonecat.findall(line)
190                                                 if len(urls):
191                                                         list.append([page, urls[0]])
192         return list
193
194 ###################################################
195
196 class ARDMediathekCache(Screen):
197         skin = """
198                 <screen position="center,center" size="76,76" flags="wfNoBorder" backgroundColor="#ffffff" >
199                         <eLabel position="2,2" zPosition="1" size="72,72" font="Regular;18" backgroundColor="#252525" />
200                         <widget name="spinner" position="14,14" zPosition="2" size="48,48" alphatest="on" />
201                 </screen>"""
202
203         def __init__(self, session):
204                 self.session = session
205                 Screen.__init__(self, session)
206                 
207                 self["spinner"] = Pixmap()
208                 self.curr = 0
209                 self.__shown = False
210                 
211                 self.timer = eTimer()
212                 self.timer.callback.append(self.showNextSpinner)
213
214         def start(self):
215                 self.__shown = True
216                 self.show()
217                 self.timer.start(200, False)
218
219         def stop(self):
220                 self.hide()
221                 self.timer.stop()
222                 self.__shown = False
223
224         def isShown(self):
225                 return self.__shown
226
227         def showNextSpinner(self):
228                 self.curr += 1
229                 if self.curr > 10:
230                         self.curr = 0
231                 png = LoadPixmap(cached=True, path=PNG_PATH + str(self.curr) + ".png")
232                 self["spinner"].instance.setPixmap(png)
233
234 ###################################################
235
236 class ARDMenuList(MenuList):
237         def __init__(self):
238                 MenuList.__init__(self, [], False, eListboxPythonMultiContent)
239                 self.l.setItemHeight(25)
240                 self.l.setFont(0, gFont("Regular", 20))
241
242 def ARDMenuListEntry(url, name):
243         res = [(url, name)]
244         res.append(MultiContentEntryText(pos=(0, 0), size=(580, 20), font=0, text=name, color=0xffffff))
245         return res
246
247 def ARDMenuListSubEntry(movie, thumb):
248         res = [(movie[3], movie)]
249         if thumb:
250                 res.append(MultiContentEntryPixmapAlphaTest(pos=(0, 0), size=(75, 50), png=thumb))
251         res.append(MultiContentEntryText(pos=(80, 0), size=(500, 25), font=0, text=movie[2] + " - " + movie[0]))
252         res.append(MultiContentEntryText(pos=(80, 25), size=(500, 25), font=0, text=movie[1]))
253         return res
254
255 ###################################################
256
257 class ARDMediathek(Screen):
258         def __init__(self, session):
259                 self.session = session
260                 
261                 desktop = getDesktop(0)
262                 size = desktop.size()
263                 width = size.width()
264                 
265                 if width == 720:
266                         self.skin = """<screen position="0,0" size="720,576" flags="wfNoBorder" >"""
267                 else:
268                         self.skin = """<screen position="center,center" size="720,576" title="ARD Mediathek" >"""
269                 self.skin += """<ePixmap position="0,0" zPosition="-1" size="720,576" pixmap="%s" />
270                                 <widget name="list" position="70,100" size="580,400" backgroundColor="#064b99" backgroundColorSelected="#003579" scrollbarMode="showOnDemand" />
271                                 <ePixmap pixmap="skin_default/buttons/key_menu.png" position="70,520" size="35,25" transparent="1" alphatest="on" />
272                                 <widget name="pageNavigation" position="260,520" size="380,400" halign="right" font="Regular;20" backgroundColor="#2666ad" foregroundColor="#ffffff" />
273                                 <widget name="serverName" position="120,520" size="250,20" font="Regular;20" backgroundColor="#2666ad" foregroundColor="#ffffff" />
274                         </screen>""" % (PNG_PATH+"background.png")
275                 
276                 Screen.__init__(self, session)
277                 
278                 self["list"] = ARDMenuList()
279                 self["pageNavigation"] = Label()
280                 self["serverName"] = Label("Server")
281                 
282                 self["actions"] = ActionMap(["ARDMediathekActions"],
283                         {
284                                 "back": self.exit,
285                                 "ok": self.ok,
286                                 "left": self.left,
287                                 "right": self.right,
288                                 "up": self.up,
289                                 "down": self.down,
290                                 "menu": self.selectServer,
291                                 "mainpage": self.mainpage,
292                                 "previousPage": self.previousPage,
293                                 "nextPage": self.nextPage
294                         }, -1)
295                 
296                 self.cacheDialog = self.session.instantiateDialog(ARDMediathekCache)
297                 
298                 self.working = False
299                 self.mainpage = True
300                 self.pages = None
301                 self.transcodeServer = None
302                 self.movies = []
303                 self.listMovies = []
304                 
305                 self.cacheTimer = eTimer()
306                 self.cacheTimer.callback.append(self.chechCachedFile)
307                 
308                 self.onLayoutFinish.append(self.getPage)
309
310         def mainpage(self):
311                 self.getPage()
312
313         def getPage(self, url=None):
314                 self.working = True
315                 self.cacheDialog.start()
316                 self.mainpage = False
317                 if not url:
318                         self.mainpage = True
319                         url = MAIN_PAGE + "/ard/servlet/"
320                 getPage(url).addCallback(self.gotPage).addErrback(self.error)
321
322         def error(self, err=""):
323                 print "[ARD Mediathek] Error:", err
324                 self.working = False
325                 self.deactivateCacheDialog()
326
327         def gotPage(self, html=""):
328                 list = []
329                 if not self.mainpage:
330                         del self.movies
331                         del self.listMovies
332                         self.listMovies = []
333                         self.movies = getMovies(html)
334                         self["list"].l.setItemHeight(50)
335                         self.pages = getPageNavigation(html)
336                         txt = ""
337                         for page in self.pages:
338                                 if page[1]:
339                                         txt = txt + page[0] + " "
340                                 else:
341                                         txt = txt + "|" + page[0] + "| "
342                         self["pageNavigation"].setText(txt)
343                         self["pageNavigation"].show()
344                         self.buildList()
345                 else:
346                         categories = getCategories(html)
347                         for category in categories:
348                                 list.append(ARDMenuListEntry(category[0], category[1]))
349                         self["list"].l.setItemHeight(25)
350                         self["list"].setList(list)
351                         self.pages = None
352                         self["pageNavigation"].setText("")
353                         self["pageNavigation"].hide()
354                         self.deactivateCacheDialog()
355
356         def buildList(self):
357                 if len(self.movies):
358                         movie = self.movies[0]
359                         thumbUrl = movie[4]
360                         try:
361                                 req = urllib2.Request(thumbUrl)
362                                 url_handle = urllib2.urlopen(req)\r
363                                 headers = url_handle.info()\r
364                                 contentType = headers.getheader("content-type")
365                         except:
366                                 contentType = None
367                         if contentType:
368                                 if 'image/jpeg' in contentType:
369                                         self.thumb = "/tmp/ard.jpg"
370                                 elif 'image/gif' in contentType:
371                                         self.thumb = "/tmp/ard.gif"
372                                 elif 'image/png' in contentType:
373                                         self.thumb = "/tmp/ard.png"
374                                 else:
375                                         print "[ARD Mediathek] Unknown thumbnail content-type:", contentType
376                                         self.thumb = None
377                         else:
378                                 self.thumb = None
379                         if self.thumb:
380                                 downloadPage(thumbUrl, self.thumb).addCallback(self.downloadThumbnailCallback).addErrback(self.downloadThumbnailError)
381                         else:
382                                 self.buildEntry(None)
383                 else:
384                         self["list"].setList(self.listMovies)
385                         self.deactivateCacheDialog()
386
387         def downloadThumbnailError(self, err):
388                 print "[ARD Mediathek] Error:", err
389                 self.buildEntry(None)
390
391         def downloadThumbnailCallback(self, txt=""):
392                 sc = AVSwitch().getFramebufferScale()
393                 self.picload = ePicLoad()
394                 self.picload.PictureData.get().append(self.buildEntry)
395                 self.picload.setPara((75, 50, sc[0], sc[1], False, 1, "#00000000"))
396                 self.picload.startDecode(self.thumb)
397
398         def buildEntry(self, picInfo=None):
399                 movie = self.movies[0]
400                 thumb = None
401                 if picInfo:
402                         ptr = self.picload.getData()
403                         if ptr != None:
404                                 thumb = ptr
405                 self.listMovies.append(ARDMenuListSubEntry(movie, thumb))
406                 del self.movies[0]
407                 self.buildList()
408
409         def chechCachedFile(self):
410                 try:
411                         f = open ("/tmp/mpstream/progress.txt")
412                         content = f.read()
413                         f.close()
414                         list = content.split("-")
415                         cacheMB = int(list[0])
416                         if cacheMB > 5: # Starte nach 5 MB Bufferung
417                                 self.cacheTimer.stop()
418                                 self.playCachedFile()
419                 except:
420                         pass
421
422         def deactivateCacheDialog(self):
423                 self.cacheDialog.stop()
424                 self.working = False
425
426         def playCachedFile(self):
427                 self.deactivateCacheDialog()
428                 ref = eServiceReference(1, 0, "/tmp/mpstream/MPStream.ts")
429                 self.session.openWithCallback(self.stopStream2Dream, ChangedMoviePlayer, ref)
430
431         def stopStream2Dream(self, callback=None):
432                 streamplayer.stop()
433                 sleep(4)
434
435         def selectServer(self):
436                 list = []
437                 if streamplayer:
438                         list.append(("LT Stream2Dream", "LT Stream2Dream"))
439                 if vlcServerConfig:
440                         serverList = vlcServerConfig.getServerlist()
441                         for x in serverList:
442                                 list.append((x.getName(), x))
443                 if len(list):
444                         self.session.openWithCallback(self.serverChosen, ChoiceBox, title="Waehle den Server...", list=list)
445
446         def serverChosen(self, callback):
447                 if callback:
448                         server = callback[1]
449                         if server == "LT Stream2Dream":
450                                 if not streamplayer.connected:
451                                         self.transcodeServer = "LT Stream2Dream"
452                                         self["serverName"].setText("LT Stream2Dream")
453                                         self.connectToStream2Dream()
454                         else:
455                                 if streamplayer:
456                                         if streamplayer.connected:
457                                                 streamplayer.logout()
458                                 self.transcodeServer = server
459                                 self["serverName"].setText(server.getName())
460
461         def connectToStream2Dream(self):
462                 streamplayer.login()
463                 try:
464                         list = listdir("/tmp/mp")
465                 except:
466                         list = []
467                 if len(list) < 2:
468                         self.session.open(MessageBox, "Die Verbindung zu LT Stream2Dream konnte nicht hergestellt werden!", MessageBox.TYPE_ERROR)
469                         streamplayer.logout()
470                         self.transcodeServer = None
471                         self["serverName"].setText("Server")
472
473         def exit(self):
474                 if not self.working:
475                         if self.cacheDialog.isShown() == False:
476                                 if streamplayer:
477                                         if streamplayer.connected:
478                                                 streamplayer.logout()
479                                 self.session.deleteDialog(self.cacheDialog)
480                                 self.close()
481                         else:
482                                 if streamplayer:
483                                         if streamplayer.connected:
484                                                 streamplayer.stop()
485                                                 sleep(4)
486                                 self.deactivateCacheDialog()
487
488         def ok(self):
489                 if not self.working:
490                         if self.cacheDialog.isShown() == False:
491                                 curr = self["list"].getCurrent()
492                                 if curr:
493                                         if not self.mainpage:
494                                                 movies = getMovieUrls(curr[0][0])
495                                                 list = []
496                                                 for x in movies:
497                                                         list.append((x[0], x[1]))
498                                                 if len(list):
499                                                         self.session.openWithCallback(self.play, ChoiceBox, title="Selektiere...", list=list)
500                                         else:
501                                                 self.getPage(curr[0][0])
502                         else:
503                                 if streamplayer:
504                                         if streamplayer.connected:
505                                                 if streamplayer.caching or streamclient.streaming:
506                                                         self.playCachedFile()
507
508         def play(self, callback):
509                 if callback is not None:
510                         url = callback[1]
511                         if self.transcodeServer is not None:
512                                 if self.transcodeServer == "LT Stream2Dream":
513                                         r = streamplayer.play(url)
514                                         if r == "ok":
515                                                 sleep(6)
516                                                 self.cacheDialog.start()
517                                                 self.cacheTimer.start(1000, False)
518                                         else:
519                                                 self.session.open(MessageBox, "LT Stream2Dream konnte den Stream nicht starten!", MessageBox.TYPE_ERROR)
520                                 else:
521                                         self.transcodeServer.play(self.session, url, self["list"].getCurrent()[0][1][1])
522                         else:
523                                 self.session.open(MessageBox, "Es wurde kein Server ausgewählt!", MessageBox.TYPE_ERROR)
524
525         def left(self):
526                 if not self.working:
527                         self["list"].pageUp()
528
529         def right(self):
530                 if not self.working:
531                         self["list"].pageDown()
532
533         def up(self):
534                 if not self.working:
535                         self["list"].up()
536
537         def down(self):
538                 if not self.working:
539                         self["list"].down()
540
541         def removeSessionId(self, url):
542                 ret = url
543                 start = ';jsessionid='
544                 if start in url:
545                         idx = url.index(start)
546                         ret = url[:idx]
547                         url = url[idx:]
548                         idx = url.index("?")
549                         url = url[idx:]
550                         ret += url
551                 return encodeHtml(ret)
552
553         def previousPage(self):
554                 if not self.working:
555                         page = None
556                         if self.pages:
557                                 for x in self.pages:
558                                         if not x[1]:
559                                                 break
560                                         else:
561                                                 page = x
562                         if page:
563                                 self.getPage(self.removeSessionId(MAIN_PAGE + page[1]))
564
565         def nextPage(self):
566                 if not self.working:
567                         page = None
568                         if self.pages:
569                                 curPage = False
570                                 for x in self.pages:
571                                         if not x[1]:
572                                                 curPage = True
573                                         else:
574                                                 if curPage:
575                                                         page = x
576                                                         break
577                         if page:
578                                 self.getPage(self.removeSessionId(MAIN_PAGE + page[1]))
579
580 ###################################################
581
582 def start(session, **kwargs):
583         session.open(ARDMediathek)
584
585 def Plugins(**kwargs):
586         return PluginDescriptor(name="ARD Mediathek", description="Streame von der ARD Mediathek", where=[PluginDescriptor.WHERE_EXTENSIONSMENU, PluginDescriptor.WHERE_PLUGINMENU], fnc=start)