remove shared copies of NTIVirtualKeyBoard
[enigma2-plugins.git] / imdb / src / plugin.py
1 # -*- coding: UTF-8 -*-
2 from __future__ import print_function
3 from Plugins.Plugin import PluginDescriptor
4 from twisted.web.client import downloadPage
5 from enigma import ePicLoad, eServiceReference
6 from Screens.Screen import Screen
7 from Screens.EpgSelection import EPGSelection
8 from Screens.ChannelSelection import SimpleChannelSelection
9 from Screens.ChoiceBox import ChoiceBox
10 from Components.ActionMap import ActionMap
11 from Components.Pixmap import Pixmap
12 from Components.Label import Label
13 from Components.ScrollLabel import ScrollLabel
14 from Components.Button import Button
15 from Components.AVSwitch import AVSwitch
16 from Components.MenuList import MenuList
17 from Components.Language import language
18 from Components.ProgressBar import ProgressBar
19 from Components.Sources.StaticText import StaticText
20 from Components.config import config, ConfigSubsection, ConfigYesNo
21 from Tools.Directories import fileExists, resolveFilename, SCOPE_PLUGINS, SCOPE_SKIN_IMAGE
22 from os import environ as os_environ
23 from Plugins.SystemPlugins.Toolkit.NTIVirtualKeyBoard import NTIVirtualKeyBoard
24 import re
25 try:
26         import htmlentitydefs
27         from urllib import quote_plus
28         iteritems = lambda d: d.iteritems()
29 except ImportError as ie:
30         from html import entities as htmlentitydefs
31         from urllib.parse import quote_plus
32         iteritems = lambda d: d.items()
33         unichr = chr
34 import gettext
35
36 config.plugins.imdb = ConfigSubsection()
37 config.plugins.imdb.force_english = ConfigYesNo(default=False)
38
39 def localeInit():
40         lang = language.getLanguage()[:2] # getLanguage returns e.g. "fi_FI" for "language_country"
41         os_environ["LANGUAGE"] = lang # Enigma doesn't set this (or LC_ALL, LC_MESSAGES, LANG). gettext needs it!
42         gettext.bindtextdomain("IMDb", resolveFilename(SCOPE_PLUGINS, "Extensions/IMDb/locale"))
43
44 def _(txt):
45         t = gettext.dgettext("IMDb", txt)
46         if t == txt:
47                 print("[IMDb] fallback to default translation for", txt)
48                 t = gettext.gettext(txt)
49         return t
50
51 localeInit()
52 language.addCallback(localeInit)
53
54 class IMDBChannelSelection(SimpleChannelSelection):
55         def __init__(self, session):
56                 SimpleChannelSelection.__init__(self, session, _("Channel Selection"))
57                 self.skinName = "SimpleChannelSelection"
58
59                 self["ChannelSelectEPGActions"] = ActionMap(["ChannelSelectEPGActions"],
60                         {
61                                 "showEPGList": self.channelSelected
62                         }
63                 )
64
65         def channelSelected(self):
66                 ref = self.getCurrentSelection()
67                 if (ref.flags & 7) == 7:
68                         self.enterPath(ref)
69                 elif not (ref.flags & eServiceReference.isMarker):
70                         self.session.openWithCallback(
71                                 self.epgClosed,
72                                 IMDBEPGSelection,
73                                 ref,
74                                 openPlugin = False
75                         )
76
77         def epgClosed(self, ret = None):
78                 if ret:
79                         self.close(ret)
80
81 class IMDBEPGSelection(EPGSelection):
82         def __init__(self, session, ref, openPlugin = True):
83                 EPGSelection.__init__(self, session, ref)
84                 self.skinName = "EPGSelection"
85                 self["key_green"].setText(_("Lookup"))
86                 self.openPlugin = openPlugin
87
88         def infoKeyPressed(self):
89                 self.timerAdd()
90
91         def timerAdd(self):
92                 cur = self["list"].getCurrent()
93                 evt = cur[0]
94                 sref = cur[1]
95                 if not evt: 
96                         return
97
98                 if self.openPlugin:
99                         self.session.open(
100                                 IMDB,
101                                 evt.getEventName()
102                         )
103                 else:
104                         self.close(evt.getEventName())
105
106         def onSelectionChanged(self):
107                 pass
108
109 class IMDB(Screen):
110         skin = """
111                 <screen name="IMDB" position="center,center" size="600,420" title="Internet Movie Database Details Plugin" >
112                         <ePixmap pixmap="skin_default/buttons/red.png" position="0,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
113                         <ePixmap pixmap="skin_default/buttons/green.png" position="140,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
114                         <ePixmap pixmap="skin_default/buttons/yellow.png" position="280,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
115                         <ePixmap pixmap="skin_default/buttons/blue.png" position="420,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
116                         <ePixmap pixmap="skin_default/buttons/key_menu.png" position="565,5" zPosition="0" size="35,25" alphatest="on" />
117                         <widget name="key_red" position="0,0" zPosition="1" size="140,40" font="Regular;20" valign="center" halign="center" backgroundColor="#9f1313" transparent="1" />
118                         <widget name="key_green" position="140,0" zPosition="1" size="140,40" font="Regular;20" valign="center" halign="center" backgroundColor="#1f771f" transparent="1" />
119                         <widget name="key_yellow" position="280,0" zPosition="1" size="140,40" font="Regular;20" valign="center" halign="center" backgroundColor="#a08500" transparent="1" />
120                         <widget name="key_blue" position="420,0" zPosition="1" size="140,40" font="Regular;20" valign="center" halign="center" backgroundColor="#18188b" transparent="1" />
121                         <widget source="title" render="Label" position="10,40" size="330,45" valign="center" font="Regular;22"/>
122                         <widget name="detailslabel" position="105,90" size="485,140" font="Regular;18" />
123                         <widget name="castlabel" position="10,235" size="580,155" font="Regular;18" />
124                         <widget name="extralabel" position="10,40" size="580,350" font="Regular;18" />
125                         <widget name="ratinglabel" position="340,62" size="250,20" halign="center" font="Regular;18" foregroundColor="#f0b400"/>
126                         <widget name="statusbar" position="10,404" size="580,16" font="Regular;16" foregroundColor="#cccccc" />
127                         <widget name="poster" position="4,90" size="96,140" alphatest="on" />
128                         <widget name="menu" position="10,115" size="580,275" zPosition="3" scrollbarMode="showOnDemand" />
129                         <widget name="starsbg" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/IMDb/starsbar_empty.png" position="340,40" zPosition="0" size="210,21" transparent="1" alphatest="on" />
130                         <widget name="stars" position="340,40" size="210,21" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/IMDb/starsbar_filled.png" transparent="1" />
131                 </screen>"""
132
133         def __init__(self, session, eventName, callbackNeeded=False):
134                 Screen.__init__(self, session)
135
136                 self.eventName = eventName
137                 
138                 self.callbackNeeded = callbackNeeded
139                 self.callbackData = ""
140                 self.callbackGenre = ""
141
142                 self.dictionary_init()
143
144                 self["poster"] = Pixmap()
145                 self.picload = ePicLoad()
146                 self.picload.PictureData.get().append(self.paintPosterPixmapCB)
147
148                 self["stars"] = ProgressBar()
149                 self["starsbg"] = Pixmap()
150                 self["stars"].hide()
151                 self["starsbg"].hide()
152                 self.ratingstars = -1
153
154                 self["title"] = StaticText(_("The Internet Movie Database"))
155                 # map new source -> old component
156                 def setText(txt):
157                         StaticText.setText(self["title"], txt)
158                         self["titellabel"].setText(txt)
159                 self["title"].setText = setText
160                 self["titellabel"] = Label()
161                 self["detailslabel"] = ScrollLabel("")
162                 self["castlabel"] = ScrollLabel("")
163                 self["extralabel"] = ScrollLabel("")
164                 self["statusbar"] = Label("")
165                 self["ratinglabel"] = Label("")
166                 self.resultlist = []
167                 self["menu"] = MenuList(self.resultlist)
168                 self["menu"].hide()
169
170                 self["key_red"] = Button(_("Exit"))
171                 self["key_green"] = Button("")
172                 self["key_yellow"] = Button("")
173                 self["key_blue"] = Button("")
174
175                 # 0 = multiple query selection menu page
176                 # 1 = movie info page
177                 # 2 = extra infos page
178                 self.Page = 0
179
180                 self["actions"] = ActionMap(["OkCancelActions", "ColorActions", "MovieSelectionActions", "DirectionActions"],
181                 {
182                         "ok": self.showDetails,
183                         "cancel": self.exit,
184                         "down": self.pageDown,
185                         "up": self.pageUp,
186                         "red": self.exit,
187                         "green": self.showMenu,
188                         "yellow": self.showDetails,
189                         "blue": self.showExtras,
190                         "contextMenu": self.contextMenuPressed,
191                         "showEventInfo": self.showDetails
192                 }, -1)
193
194                 self.getIMDB()
195
196         def exit(self):
197                 if self.callbackNeeded:
198                         self.close([self.callbackData, self.callbackGenre])
199                 else:
200                         self.close()
201
202         event_quoted = property(lambda self: quote_plus(self.eventName.encode('utf8')))
203
204         def dictionary_init(self):
205                 syslang = language.getLanguage()
206                 if "de" not in syslang or config.plugins.imdb.force_english.value:
207                         self.IMDBlanguage = ""  # set to empty ("") for english version
208
209                         self.generalinfomask = re.compile(
210                         '<h1 class="header".*?>(?P<title>.*?)<.*?</h1>.*?'
211                         '(?:.*?<h4 class="inline">\s*(?P<g_director>Regisseur|Directors?):\s*</h4>.*?<a.*?>(?P<director>.*?)</a>)*'
212                         '(?:.*?<h4 class="inline">\s*(?P<g_creator>Sch\S*?pfer|Creators?):\s*</h4>.*?<a.*?>(?P<creator>.*?)</a>)*'
213                         '(?:.*?<h4 class="inline">\s*(?P<g_seasons>Seasons?):\s*</h4>.*?<a.*?>(?P<seasons>(?:\d+|unknown)?)</a>)*'
214                         '(?:.*?<h4 class="inline">\s*(?P<g_writer>Drehbuch|Writer).*?</h4>.*?<a.*?>(?P<writer>.*?)</a>)*'
215                         '(?:.*?<h4 class="inline">\s*(?P<g_country>Land|Country):\s*</h4>.*?<a.*?>(?P<country>.*?)</a>)*'
216                         '(?:.*?<h4 class="inline">\s*(?P<g_premiere>Premiere|Release Date).*?</h4>\s+(?P<premiere>.*?)\s*<span)*'
217                         '(?:.*?<h4 class="inline">\s*(?P<g_alternativ>Auch bekannt als|Also Known As):\s*</h4>\s*(?P<alternativ>.*?)\s*<span)*'
218                         , re.DOTALL)
219
220                         self.extrainfomask = re.compile(
221                         '(?:.*?<h4 class="inline">(?P<g_outline>Kurzbeschreibung|Plot Outline):</h4>(?P<outline>.+?)<)*'
222                         '(?:.*?<h2>(?P<g_synopsis>Storyline)</h2>.*?<p>(?P<synopsis>.+?)\s*</p>)*'
223                         '(?:.*?<h4 class="inline">(?P<g_keywords>Plot Keywords):</h4>(?P<keywords>.+?)(?:Mehr|See more</a>|</div>))*'
224                         '(?:.*?<h4 class="inline">(?P<g_tagline>Werbezeile|Tagline?):</h4>\s*(?P<tagline>.+?)<)*'
225                         '(?:.*?<h4 class="inline">(?P<g_awards>Filmpreise|Awards):</h4>\s*(?P<awards>.+?)(?:Mehr|See more</a>|</div>))*'
226                         '(?:.*?<h4 class="inline">(?P<g_language>Sprache|Language):</h4>\s*(?P<language>.+?)</div>)*'
227                         '(?:.*?<h4 class="inline">(?P<g_locations>Drehorte|Filming Locations):</h4>.*?<a.*?>(?P<locations>.+?)</a>)*'
228                         '(?:.*?<h4 class="inline">(?P<g_runtime>L\S*?nge|Runtime):</h4>\s*(?P<runtime>.+?)</div>)*'
229                         '(?:.*?<h4 class="inline">(?P<g_sound>Tonverfahren|Sound Mix):</h4>\s*(?P<sound>.+?)</div>)*'
230                         '(?:.*?<h4 class="inline">(?P<g_color>Farbe|Color):</h4>\s*(?P<color>.+?)</div>)*'
231                         '(?:.*?<h4 class="inline">(?P<g_aspect>Seitenverh\S*?ltnis|Aspect Ratio):</h4>\s*(?P<aspect>.+?)(?:Mehr|See more</a>|</div>))*'
232                         '(?:.*?<h4 class="inline">(?P<g_cert>Altersfreigabe|Certification):</h4>\s*(?P<cert>.+?)</div>)*'
233                         '(?:.*?<h4 class="inline">(?P<g_company>Firma|Company):</h4>\s*(?P<company>.+?)(?:Mehr|See more</a>|</div>))*'
234                         '(?:.*?<h4>(?P<g_trivia>Dies und das|Trivia)</h4>\s*(?P<trivia>.+?)(?:<span))*'
235                         '(?:.*?<h4>(?P<g_goofs>Pannen|Goofs)</h4>\s*(?P<goofs>.+?)(?:<span))*'
236                         '(?:.*?<h4>(?P<g_quotes>Dialogzitate|Quotes)</h4>\s*(?P<quotes>.+?)(?:<span))*'
237                         '(?:.*?<h4>(?P<g_connections>Bez\S*?ge zu anderen Titeln|Movie Connections)</h4>\s*(?P<connections>.+?)(?:<span))*'
238                         '(?:.*?<h2>(?P<g_comments>Nutzerkommentare|User Reviews)</h2>.*?<a href="/user/ur\d{7,7}/comments">(?P<commenter>.+?)</a>.*?<p>(?P<comment>.+?)</p>)*'
239                         , re.DOTALL)
240
241                         self.genreblockmask = re.compile('<h4 class="inline">Genre:</h4>\s<div class="info-content">\s+?(.*?)\s+?(?:Mehr|See more|</p|<a class|</div>)', re.DOTALL)
242                         self.ratingmask = re.compile('="ratingValue">(?P<rating>.*?)</', re.DOTALL)
243                         self.castmask = re.compile('<td class="name">\s*<a.*?>(.*?)</a>.*?<td class="character">\s*<div>\s*(?:<a.*?>)?(.*?)(?:</a>)?\s*( \(as.*?\))?\s*</div>', re.DOTALL)
244                         self.postermask = re.compile('<td .*?id="img_primary">.*?<img .*?src=\"(http.*?)\"', re.DOTALL)
245                 else:
246                         self.IMDBlanguage = "german." # it's a subdomain, so add a '.' at the end
247
248                         self.generalinfomask = re.compile(
249                         '<h1>\s*(?P<title>.*?) <.*?</h1>.*?'
250                         '(?:.*?<h5>(?P<g_director>Regisseur|Directors?):</h5>.*?<a href=\".*?\">(?P<director>.*?)</a>)*'
251                         '(?:.*?<h5>(?P<g_creator>Sch\S*?pfer|Creators?):</h5>.*?<a href=\".*?\">(?P<creator>.*?)</a>)*'
252                         '(?:.*?<h5>(?P<g_seasons>Seasons):</h5>(?:.*?)<a href=\".*?\">(?P<seasons>\d+?)</a>\s+?(?:<a class|\|\s+?<a href="episodes#season-unknown))*'
253                         '(?:.*?<h5>(?P<g_writer>Drehbuch|Writer).*?</h5>.*?<a href=\".*?\">(?P<writer>.*?)</a>)*'
254                         '(?:.*?<h5>(?P<g_premiere>Premiere|Release Date).*?</h5>\s+<div.*?>\s?(?P<premiere>.*?)\n\s.*?<)*'
255                         '(?:.*?<h5>(?P<g_alternativ>Auch bekannt als|Also Known As):</h5><div.*?>\s*(?P<alternativ>.*?)(?:<br>)?\s*<a.*?>(?:Mehr|See more))*'
256                         '(?:.*?<h5>(?P<g_country>Land|Country):</h5>\s+<div.*?>(?P<country>.*?)</div>(?:.*?Mehr|\s+?</div>))*'
257                         , re.DOTALL)
258
259                         self.extrainfomask = re.compile(
260                         '(?:.*?<h5>(?P<g_tagline>Werbezeile|Tagline?):</h5>\n(?P<tagline>.+?)<)*'
261                         '(?:.*?<h5>(?P<g_outline>Kurzbeschreibung|Handlung):</h5>(?P<outline>.+?)<a class)*'
262                         '(?:.*?<h5>(?P<g_synopsis>Plot Synopsis):</h5>(?:.*?)(?:<a href=\".*?\">)*?(?P<synopsis>.+?)(?:</a>|</div>))*'
263                         '(?:.*?<h5>(?P<g_keywords>Plot Keywords):</h5>(?P<keywords>.+?)(?:Mehr|See more</a>|</div>))*'
264                         '(?:.*?<h5>(?P<g_awards>Filmpreise|Awards):</h5>(?P<awards>.+?)(?:Mehr|See more</a>|</div>))*'
265                         '(?:.*?<h5>(?P<g_runtime>L\S*?nge|Runtime):</h5>(?P<runtime>.+?)</div>)*'
266                         '(?:.*?<h5>(?P<g_language>Sprache|Language):</h5>(?P<language>.+?)</div>)*'
267                         '(?:.*?<h5>(?P<g_color>Farbe|Color):</h5>(?P<color>.+?)</div>)*'
268                         '(?:.*?<h5>(?P<g_aspect>Seitenverh\S*?ltnis|Aspect Ratio):</h5>(?P<aspect>.+?)(?:Mehr|See more</a>|</div>))*'
269                         '(?:.*?<h5>(?P<g_sound>Tonverfahren|Sound Mix):</h5>(?P<sound>.+?)</div>)*'
270                         '(?:.*?<h5>(?P<g_cert>Altersfreigabe|Certification):</h5>(?P<cert>.+?)</div>)*'
271                         '(?:.*?<h5>(?P<g_locations>Drehorte|Filming Locations):</h5>(?P<locations>.+?)(?:Mehr|See more</a>|</div>))*'
272                         '(?:.*?<h5>(?P<g_company>Firma|Company):</h5>(?P<company>.+?)(?:Mehr|See more</a>|</div>))*'
273                         '(?:.*?<h5>(?P<g_trivia>Dies und das|Trivia):</h5>(?P<trivia>.+?)(?:Mehr|See more</a>|</div>))*'
274                         '(?:.*?<h5>(?P<g_goofs>Pannen|Goofs):</h5>(?P<goofs>.+?)(?:Mehr|See more</a>|</div>))*'
275                         '(?:.*?<h5>(?P<g_quotes>Dialogzitate|Quotes):</h5>(?P<quotes>.+?)(?:Mehr|See more</a>|</div>))*'
276                         '(?:.*?<h5>(?P<g_connections>Bez\S*?ge zu anderen Titeln|Movie Connections):</h5>(?P<connections>.+?)(?:Mehr|See more</a>|</div>))*'
277                         '(?:.*?<h3>(?P<g_comments>Nutzerkommentare|User Comments)</h3>.*?<a href="/user/ur\d{7,7}/comments">(?P<commenter>.+?)\n</div>.*?<p>(?P<comment>.+?)</p>)*'
278                         , re.DOTALL)
279
280                         self.genreblockmask = re.compile('<h5>Genre:</h5>\s<div class="info-content">\s+?(.*?)\s+?(?:Mehr|See more|</p|<a class|</div>)', re.DOTALL)
281                         self.ratingmask = re.compile('<h5>(?P<g_rating>Nutzer-Bewertung|User Rating):</h5>.*?<b>(?P<rating>.*?)/10</b>', re.DOTALL)
282                         self.castmask = re.compile('<td class="nm">.*?>(.*?)</a>.*?<td class="char">(?:<a.*?>)?(.*?)(?:</a>)?(\s\(.*?\))?</td>', re.DOTALL)
283                         self.postermask = re.compile('<div class="photo">.*?<img .*? src=\"(http.*?)\" .*?>', re.DOTALL)
284
285                 self.htmltags = re.compile('<.*?>')
286
287         def resetLabels(self):
288                 self["detailslabel"].setText("")
289                 self["ratinglabel"].setText("")
290                 self["title"].setText("")
291                 self["castlabel"].setText("")
292                 self["titellabel"].setText("")
293                 self["extralabel"].setText("")
294                 self.ratingstars = -1
295
296         def pageUp(self):
297                 if self.Page == 0:
298                         self["menu"].instance.moveSelection(self["menu"].instance.moveUp)
299                 if self.Page == 1:
300                         self["castlabel"].pageUp()
301                         self["detailslabel"].pageUp()
302                 if self.Page == 2:
303                         self["extralabel"].pageUp()
304
305         def pageDown(self):
306                 if self.Page == 0:
307                         self["menu"].instance.moveSelection(self["menu"].instance.moveDown)
308                 if self.Page == 1:
309                         self["castlabel"].pageDown()
310                         self["detailslabel"].pageDown()
311                 if self.Page == 2:
312                         self["extralabel"].pageDown()
313
314         def showMenu(self):
315                 if ( self.Page is 1 or self.Page is 2 ) and self.resultlist:
316                         self["menu"].show()
317                         self["stars"].hide()
318                         self["starsbg"].hide()
319                         self["ratinglabel"].hide()
320                         self["castlabel"].hide()
321                         self["poster"].hide()
322                         self["extralabel"].hide()
323                         self["title"].setText(_("Ambiguous results"))
324                         self["detailslabel"].setText(_("Please select the matching entry"))
325                         self["detailslabel"].show()
326                         self["key_blue"].setText("")
327                         self["key_green"].setText(_("Title Menu"))
328                         self["key_yellow"].setText(_("Details"))
329                         self.Page = 0
330
331         def showDetails(self):
332                 self["ratinglabel"].show()
333                 self["castlabel"].show()
334                 self["detailslabel"].show()
335
336                 if self.resultlist and self.Page == 0:
337                         link = self["menu"].getCurrent()[1]
338                         title = self["menu"].getCurrent()[0]
339                         self["statusbar"].setText(_("Re-Query IMDb: %s...") % (title))
340                         localfile = "/tmp/imdbquery2.html"
341                         fetchurl = "http://" + self.IMDBlanguage + "imdb.com/title/" + link
342                         print("[IMDB] downloading query " + fetchurl + " to " + localfile)
343                         downloadPage(fetchurl,localfile).addCallback(self.IMDBquery2).addErrback(self.fetchFailed)
344                         self["menu"].hide()
345                         self.resetLabels()
346                         self.Page = 1
347
348                 if self.Page == 2:
349                         self["extralabel"].hide()
350                         self["poster"].show()
351                         if self.ratingstars > 0:
352                                 self["starsbg"].show()
353                                 self["stars"].show()
354                                 self["stars"].setValue(self.ratingstars)
355
356                         self.Page = 1
357
358         def showExtras(self):
359                 if self.Page == 1:
360                         self["extralabel"].show()
361                         self["detailslabel"].hide()
362                         self["castlabel"].hide()
363                         self["poster"].hide()
364                         self["stars"].hide()
365                         self["starsbg"].hide()
366                         self["ratinglabel"].hide()
367                         self.Page = 2
368
369         def contextMenuPressed(self):
370                 list = [
371                         (_("Enter search"), self.openVirtualKeyBoard),
372                         (_("Select from EPG"), self.openChannelSelection),
373                 ]
374
375                 if fileExists(resolveFilename(SCOPE_PLUGINS, "Extensions/YTTrailer/plugin.py")):
376                         list.extend((
377                                 (_("Play Trailer"), self.openYttrailer),
378                                 (_("Search Trailer"), self.searchYttrailer),
379                         ))
380
381                 self.session.openWithCallback(
382                         self.menuCallback,
383                         ChoiceBox,
384                         list = list,
385                 )
386
387         def menuCallback(self, ret = None):
388                 ret and ret[1]()
389
390         def openYttrailer(self):
391                 try:
392                         from Plugins.Extensions.YTTrailer.plugin import YTTrailer, baseEPGSelection__init__
393                 except ImportError as ie:
394                         pass
395                 if baseEPGSelection__init__ is None:
396                         return
397
398                 ytTrailer = YTTrailer(self.session)
399                 ytTrailer.showTrailer(self.eventName)
400
401         def searchYttrailer(self):
402                 try:
403                         from Plugins.Extensions.YTTrailer.plugin import YTTrailerList, baseEPGSelection__init__
404                 except ImportError as ie:
405                         pass
406                 if baseEPGSelection__init__ is None:
407                         return
408
409                 self.session.open(YTTrailerList, self.eventName)
410
411         def openVirtualKeyBoard(self):
412                 self.session.openWithCallback(
413                         self.gotSearchString,
414                         NTIVirtualKeyBoard,
415                         title = _("Enter text to search for")
416                 )
417
418         def openChannelSelection(self):
419                 self.session.openWithCallback(
420                         self.gotSearchString,
421                         IMDBChannelSelection
422                 )
423
424         def gotSearchString(self, ret = None):
425                 if ret:
426                         self.eventName = ret
427                         self.Page = 0
428                         self.resultlist = []
429                         self["menu"].hide()
430                         self["ratinglabel"].show()
431                         self["castlabel"].show()
432                         self["detailslabel"].show()
433                         self["poster"].hide()
434                         self["stars"].hide()
435                         self["starsbg"].hide()
436                         self.getIMDB()
437
438         def getIMDB(self):
439                 self.resetLabels()
440                 if not self.eventName:
441                         s = self.session.nav.getCurrentService()
442                         info = s and s.info()
443                         event = info and info.getEvent(0) # 0 = now, 1 = next
444                         if event:
445                                 self.eventName = event.getEventName()
446                 if self.eventName:
447                         self["statusbar"].setText(_("Query IMDb: %s...") % (self.eventName))
448                         localfile = "/tmp/imdbquery.html"
449                         if self.IMDBlanguage:
450                                 fetchurl = "http://" + self.IMDBlanguage + "imdb.com/find?q=" + self.event_quoted + "&s=tt&site=aka"
451                         else:
452                                 fetchurl = "http://akas.imdb.com/find?s=tt;mx=20;q=" + self.event_quoted
453                         print("[IMDB] Downloading Query " + fetchurl + " to " + localfile)
454                         downloadPage(fetchurl,localfile).addCallback(self.IMDBquery).addErrback(self.fetchFailed)
455                 else:
456                         self["statusbar"].setText(_("Could't get Eventname"))
457
458         def fetchFailed(self,string):
459                 print("[IMDB] fetch failed", string)
460                 self["statusbar"].setText(_("IMDb Download failed"))
461
462         def html2utf8(self,in_html):
463                 entitydict = {}
464
465                 entities = re.finditer('&([^#][A-Za-z]{1,5}?);', in_html)
466                 for x in entities:
467                         key = x.group(0)
468                         if key not in entitydict:
469                                 entitydict[key] = htmlentitydefs.name2codepoint[x.group(1)]
470
471                 entities = re.finditer('&#x([0-9A-Fa-f]{2,2}?);', in_html)
472                 for x in entities:
473                         key = x.group(0)
474                         if key not in entitydict:
475                                 entitydict[key] = "%d" % int(key[3:5], 16)
476
477                 entities = re.finditer('&#(\d{1,5}?);', in_html)
478                 for x in entities:
479                         key = x.group(0)
480                         if key not in entitydict:
481                                 entitydict[key] = x.group(1)
482
483                 for key, codepoint in iteritems(entitydict):
484                         in_html = in_html.replace(key, unichr(int(codepoint)).encode('latin-1', 'ignore'))
485                 self.inhtml = in_html.decode('latin-1').encode('utf8')
486
487         def IMDBquery(self,string):
488                 print("[IMDBquery]")
489                 self["statusbar"].setText(_("IMDb Download completed"))
490
491                 self.html2utf8(open("/tmp/imdbquery.html", "r").read())
492
493                 self.generalinfos = self.generalinfomask.search(self.inhtml)
494
495                 if self.generalinfos:
496                         self.IMDBparse()
497                 else:
498                         if re.search("<title>(?:IMDb.{0,9}Search|IMDb Titelsuche)</title>", self.inhtml):
499                                 searchresultmask = re.compile("<tr> <td.*?img src.*?>.*?<a href=\".*?/title/(tt\d{7,7})/\".*?>(.*?)</td>", re.DOTALL)
500                                 searchresults = searchresultmask.finditer(self.inhtml)
501                                 self.resultlist = [(self.htmltags.sub('',x.group(2)), x.group(1)) for x in searchresults]
502                                 Len = len(self.resultlist)
503                                 self["menu"].l.setList(self.resultlist)
504                                 if Len == 1:
505                                         self["statusbar"].setText(_("Re-Query IMDb: %s...") % (self.resultlist[0][0],))
506                                         self.eventName = self.resultlist[0][1]
507                                         localfile = "/tmp/imdbquery.html"
508                                         fetchurl = "http://" + self.IMDBlanguage + "imdb.com/find?q=" + self.event_quoted + "&s=tt&site=aka"
509                                         print("[IMDB] Downloading Query " + fetchurl + " to " + localfile)
510                                         downloadPage(fetchurl,localfile).addCallback(self.IMDBquery).addErrback(self.fetchFailed)
511                                 elif Len > 1:
512                                         self.Page = 1
513                                         self.showMenu()
514                                 else:
515                                         self["detailslabel"].setText(_("No IMDb match."))
516                                         self["statusbar"].setText(_("No IMDb match."))
517                         else:
518                                 splitpos = self.eventName.find('(')
519                                 if splitpos > 0 and self.eventName.endswith(')'):
520                                         self.eventName = self.eventName[splitpos+1:-1]
521                                         self["statusbar"].setText(_("Re-Query IMDb: %s...") % (self.eventName))
522                                         localfile = "/tmp/imdbquery.html"
523                                         fetchurl = "http://" + self.IMDBlanguage + "imdb.com/find?q=" + self.event_quoted + "&s=tt&site=aka"
524                                         print("[IMDB] Downloading Query " + fetchurl + " to " + localfile)
525                                         downloadPage(fetchurl,localfile).addCallback(self.IMDBquery).addErrback(self.fetchFailed)
526                                 else:
527                                         self["detailslabel"].setText(_("IMDb query failed!"))
528
529         def IMDBquery2(self,string):
530                 self["statusbar"].setText(_("IMDb Re-Download completed"))
531                 self.html2utf8(open("/tmp/imdbquery2.html", "r").read())
532                 self.generalinfos = self.generalinfomask.search(self.inhtml)
533                 self.IMDBparse()
534
535         def IMDBparse(self):
536                 print("[IMDBparse]")
537                 self.Page = 1
538                 Detailstext = _("No details found.")
539                 if self.generalinfos:
540                         self["key_yellow"].setText(_("Details"))
541                         self["statusbar"].setText(_("IMDb Details parsed"))
542                         Titeltext = self.generalinfos.group("title")
543                         if len(Titeltext) > 57:
544                                 Titeltext = Titeltext[0:54] + "..."
545                         self["title"].setText(Titeltext)
546
547                         Detailstext = ""
548
549                         genreblock = self.genreblockmask.findall(self.inhtml)
550                         if genreblock:
551                                 genres = self.htmltags.sub('', genreblock[0])
552                                 if genres:
553                                         Detailstext += "Genre: "
554                                         Detailstext += genres
555                                         self.callbackGenre = genres
556
557                         for category in ("director", "creator", "writer", "seasons"):
558                                 if self.generalinfos.group(category):
559                                         Detailstext += "\n" + self.generalinfos.group('g_'+category) + ": " + self.generalinfos.group(category)
560
561                         for category in ("premiere", "country", "alternativ"):
562                                 if self.generalinfos.group(category):
563                                         Detailstext += "\n" + self.generalinfos.group('g_'+category) + ": " + self.htmltags.sub('', self.generalinfos.group(category).replace('\n',' ').replace("<br>", '\n').replace("<br />",'\n').replace("  ",' '))
564
565                         rating = self.ratingmask.search(self.inhtml)
566                         Ratingtext = _("no user rating yet")
567                         if rating:
568                                 rating = rating.group("rating")
569                                 if rating != '<span id="voteuser"></span>':
570                                         Ratingtext = _("User Rating") + ": " + rating + " / 10"
571                                         self.ratingstars = int(10*round(float(rating.replace(',','.')),1))
572                                         self["stars"].show()
573                                         self["stars"].setValue(self.ratingstars)
574                                         self["starsbg"].show()
575                         self["ratinglabel"].setText(Ratingtext)
576
577                         castresult = self.castmask.finditer(self.inhtml)
578                         if castresult:
579                                 Casttext = ""
580                                 for x in castresult:
581                                         Casttext += "\n" + self.htmltags.sub('', x.group(1))
582                                         if x.group(2):
583                                                 Casttext += _(" as ") + self.htmltags.sub('', x.group(2).replace('/ ...','')).replace('\n', ' ')
584                                                 if x.group(3):
585                                                         Casttext += x.group(3)
586                                 if Casttext:
587                                         Casttext = _("Cast: ") + Casttext
588                                 else:
589                                         Casttext = _("No cast list found in the database.")
590                                 self["castlabel"].setText(Casttext)
591
592                         posterurl = self.postermask.search(self.inhtml)
593                         if posterurl and posterurl.group(1).find("jpg") > 0:
594                                 posterurl = posterurl.group(1)
595                                 self["statusbar"].setText(_("Downloading Movie Poster: %s...") % (posterurl))
596                                 localfile = "/tmp/poster.jpg"
597                                 print("[IMDB] downloading poster " + posterurl + " to " + localfile)
598                                 downloadPage(posterurl,localfile).addCallback(self.IMDBPoster).addErrback(self.fetchFailed)
599                         else:
600                                 self.IMDBPoster("kein Poster")
601                         extrainfos = self.extrainfomask.search(self.inhtml)
602
603                         if extrainfos:
604                                 Extratext = "Extra Info\n"
605
606                                 for category in ("tagline","outline","synopsis","keywords","awards","runtime","language","color","aspect","sound","cert","locations","company","trivia","goofs","quotes","connections"):
607                                         if extrainfos.group('g_'+category):
608                                                 Extratext += extrainfos.group('g_'+category) + ": " + self.htmltags.sub('',extrainfos.group(category).replace("\n",'').replace("<br>", '\n').replace("<br />",'\n')) + "\n"
609                                 if extrainfos.group("g_comments"):
610                                         stripmask = re.compile('\s{2,}', re.DOTALL)
611                                         Extratext += extrainfos.group("g_comments") + " [" + stripmask.sub(' ', self.htmltags.sub('',extrainfos.group("commenter"))) + "]: " + self.htmltags.sub('',extrainfos.group("comment").replace("\n",' ')) + "\n"
612
613                                 self["extralabel"].setText(Extratext)
614                                 self["extralabel"].hide()
615                                 self["key_blue"].setText(_("Extra Info"))
616
617                 self["detailslabel"].setText(Detailstext)
618                 self.callbackData = Detailstext
619
620         def IMDBPoster(self,string):
621                 self["statusbar"].setText(_("IMDb Details parsed"))
622                 if not string:
623                         filename = "/tmp/poster.jpg"
624                 else:
625                         filename = resolveFilename(SCOPE_PLUGINS, "Extensions/IMDb/no_poster.png")
626                 sc = AVSwitch().getFramebufferScale()
627                 self.picload.setPara((self["poster"].instance.size().width(), self["poster"].instance.size().height(), sc[0], sc[1], False, 1, "#00000000"))
628                 self.picload.startDecode(filename)
629
630         def paintPosterPixmapCB(self, picInfo=None):
631                 ptr = self.picload.getData()
632                 if ptr != None:
633                         self["poster"].instance.setPixmap(ptr.__deref__())
634                         self["poster"].show()
635
636         def createSummary(self):
637                 return IMDbLCDScreen
638
639 class IMDbLCDScreen(Screen):
640         skin = """
641         <screen position="0,0" size="132,64" title="IMDB Plugin">
642                 <widget name="headline" position="4,0" size="128,22" font="Regular;20"/>
643                 <widget source="parent.title" render="Label" position="6,26" size="120,34" font="Regular;14"/>
644         </screen>"""
645
646         def __init__(self, session, parent):
647                 Screen.__init__(self, session, parent)
648                 self["headline"] = Label(_("IMDb Plugin"))
649
650 def eventinfo(session, servicelist, **kwargs):
651         ref = session.nav.getCurrentlyPlayingServiceReference()
652         session.open(IMDBEPGSelection, ref)
653
654 def main(session, eventName="", **kwargs):
655         session.open(IMDB, eventName)
656
657 def Plugins(**kwargs):
658         return [PluginDescriptor(name="IMDb Details",
659                         description=_("Query details from the Internet Movie Database"),
660                         icon="imdb.png",
661                         where=PluginDescriptor.WHERE_PLUGINMENU,
662                         fnc=main,
663                         needsRestart=False,
664                         ),
665                         PluginDescriptor(name="IMDb Details",
666                         description=_("Query details from the Internet Movie Database"),
667                         where=PluginDescriptor.WHERE_EVENTINFO,
668                         fnc=eventinfo,
669                         needsRestart=False,
670                         ),
671                 ]