imdb: fix stars image width (was set to 250 though image is only 210px)
[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 resolveFilename, SCOPE_PLUGINS, SCOPE_SKIN_IMAGE
22 from os import environ as os_environ
23 from 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\s+href=\".*?\"\s*>(?P<director>.*?)</a>)*'
212                         '(?:.*?<h4 class="inline">\s*(?P<g_creator>Sch\S*?pfer|Creators?):\s*</h4>.*?<a\s+href=\".*?\"\s*>(?P<creator>.*?)</a>)*'
213                         '(?:.*?<h4 class="inline">\s*(?P<g_seasons>Seasons?):\s*</h4>.*?<a\s+href=\".*?\"\s*>(?P<seasons>\d+?)</a>)*'
214                         '(?:.*?<h4 class="inline">\s*(?P<g_writer>Drehbuch|Writer).*?</h4>.*?<a\s+href=\".*?\"\s*>(?P<writer>.*?)</a>)*'
215                         '(?:.*?<h4 class="inline">\s*(?P<g_country>Land|Country):\s*</h4>.*?<a\s+href=\".*?\"\s*>(?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\s+href=\".*?\">(?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>(?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>.+?)<)*'
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                 self.session.openWithCallback(
376                         self.menuCallback,
377                         ChoiceBox,
378                         list = list,
379                 )
380
381         def menuCallback(self, ret = None):
382                 ret and ret[1]()
383
384         def openVirtualKeyBoard(self):
385                 self.session.openWithCallback(
386                         self.gotSearchString,
387                         NTIVirtualKeyBoard,
388                         title = _("Enter text to search for")
389                 )
390
391         def openChannelSelection(self):
392                 self.session.openWithCallback(
393                         self.gotSearchString,
394                         IMDBChannelSelection
395                 )
396
397         def gotSearchString(self, ret = None):
398                 if ret:
399                         self.eventName = ret
400                         self.Page = 0
401                         self.resultlist = []
402                         self["menu"].hide()
403                         self["ratinglabel"].show()
404                         self["castlabel"].show()
405                         self["detailslabel"].show()
406                         self["poster"].hide()
407                         self["stars"].hide()
408                         self["starsbg"].hide()
409                         self.getIMDB()
410
411         def getIMDB(self):
412                 self.resetLabels()
413                 if not self.eventName:
414                         s = self.session.nav.getCurrentService()
415                         info = s and s.info()
416                         event = info and info.getEvent(0) # 0 = now, 1 = next
417                         if event:
418                                 self.eventName = event.getEventName()
419                 if self.eventName:
420                         self["statusbar"].setText(_("Query IMDb: %s...") % (self.eventName))
421                         localfile = "/tmp/imdbquery.html"
422                         if self.IMDBlanguage:
423                                 fetchurl = "http://" + self.IMDBlanguage + "imdb.com/find?q=" + self.event_quoted + "&s=tt&site=aka"
424                         else:
425                                 fetchurl = "http://akas.imdb.com/find?s=tt;mx=20;q=" + self.event_quoted
426                         print("[IMDB] Downloading Query " + fetchurl + " to " + localfile)
427                         downloadPage(fetchurl,localfile).addCallback(self.IMDBquery).addErrback(self.fetchFailed)
428                 else:
429                         self["statusbar"].setText(_("Could't get Eventname"))
430
431         def fetchFailed(self,string):
432                 print("[IMDB] fetch failed", string)
433                 self["statusbar"].setText(_("IMDb Download failed"))
434
435         def html2utf8(self,in_html):
436                 entitydict = {}
437
438                 entities = re.finditer('&([^#][A-Za-z]{1,5}?);', in_html)
439                 for x in entities:
440                         key = x.group(0)
441                         if key not in entitydict:
442                                 entitydict[key] = htmlentitydefs.name2codepoint[x.group(1)]
443
444                 entities = re.finditer('&#x([0-9A-Fa-f]{2,2}?);', in_html)
445                 for x in entities:
446                         key = x.group(0)
447                         if key not in entitydict:
448                                 entitydict[key] = "%d" % int(key[3:5], 16)
449
450                 entities = re.finditer('&#(\d{1,5}?);', in_html)
451                 for x in entities:
452                         key = x.group(0)
453                         if key not in entitydict:
454                                 entitydict[key] = x.group(1)
455
456                 for key, codepoint in iteritems(entitydict):
457                         in_html = in_html.replace(key, unichr(int(codepoint)).encode('latin-1', 'ignore'))
458                 self.inhtml = in_html.decode('latin-1').encode('utf8')
459
460         def IMDBquery(self,string):
461                 print("[IMDBquery]")
462                 self["statusbar"].setText(_("IMDb Download completed"))
463
464                 self.html2utf8(open("/tmp/imdbquery.html", "r").read())
465
466                 self.generalinfos = self.generalinfomask.search(self.inhtml)
467
468                 if self.generalinfos:
469                         self.IMDBparse()
470                 else:
471                         if re.search("<title>(?:IMDb.{0,9}Search|IMDb Titelsuche)</title>", self.inhtml):
472                                 searchresultmask = re.compile("<tr> <td.*?img src.*?>.*?<a href=\".*?/title/(tt\d{7,7})/\".*?>(.*?)</td>", re.DOTALL)
473                                 searchresults = searchresultmask.finditer(self.inhtml)
474                                 self.resultlist = [(self.htmltags.sub('',x.group(2)), x.group(1)) for x in searchresults]
475                                 Len = len(self.resultlist)
476                                 self["menu"].l.setList(self.resultlist)
477                                 if Len == 1:
478                                         self["statusbar"].setText(_("Re-Query IMDb: %s...") % (self.resultlist[0][0],))
479                                         self.eventName = self.resultlist[0][1]
480                                         localfile = "/tmp/imdbquery.html"
481                                         fetchurl = "http://" + self.IMDBlanguage + "imdb.com/find?q=" + self.event_quoted + "&s=tt&site=aka"
482                                         print("[IMDB] Downloading Query " + fetchurl + " to " + localfile)
483                                         downloadPage(fetchurl,localfile).addCallback(self.IMDBquery).addErrback(self.fetchFailed)
484                                 elif Len > 1:
485                                         self.Page = 1
486                                         self.showMenu()
487                                 else:
488                                         self["detailslabel"].setText(_("No IMDb match."))
489                                         self["statusbar"].setText(_("No IMDb match."))
490                         else:
491                                 splitpos = self.eventName.find('(')
492                                 if splitpos > 0 and self.eventName.endswith(')'):
493                                         self.eventName = self.eventName[splitpos+1:-1]
494                                         self["statusbar"].setText(_("Re-Query IMDb: %s...") % (self.eventName))
495                                         localfile = "/tmp/imdbquery.html"
496                                         fetchurl = "http://" + self.IMDBlanguage + "imdb.com/find?q=" + self.event_quoted + "&s=tt&site=aka"
497                                         print("[IMDB] Downloading Query " + fetchurl + " to " + localfile)
498                                         downloadPage(fetchurl,localfile).addCallback(self.IMDBquery).addErrback(self.fetchFailed)
499                                 else:
500                                         self["detailslabel"].setText(_("IMDb query failed!"))
501
502         def IMDBquery2(self,string):
503                 self["statusbar"].setText(_("IMDb Re-Download completed"))
504                 self.html2utf8(open("/tmp/imdbquery2.html", "r").read())
505                 self.generalinfos = self.generalinfomask.search(self.inhtml)
506                 self.IMDBparse()
507
508         def IMDBparse(self):
509                 print("[IMDBparse]")
510                 self.Page = 1
511                 Detailstext = _("No details found.")
512                 if self.generalinfos:
513                         self["key_yellow"].setText(_("Details"))
514                         self["statusbar"].setText(_("IMDb Details parsed"))
515                         Titeltext = self.generalinfos.group("title")
516                         if len(Titeltext) > 57:
517                                 Titeltext = Titeltext[0:54] + "..."
518                         self["title"].setText(Titeltext)
519
520                         Detailstext = ""
521
522                         genreblock = self.genreblockmask.findall(self.inhtml)
523                         if genreblock:
524                                 genres = self.htmltags.sub('', genreblock[0])
525                                 if genres:
526                                         Detailstext += "Genre: "
527                                         Detailstext += genres
528                                         self.callbackGenre = genres
529
530                         for category in ("director", "creator", "writer", "seasons"):
531                                 if self.generalinfos.group(category):
532                                         Detailstext += "\n" + self.generalinfos.group('g_'+category) + ": " + self.generalinfos.group(category)
533
534                         for category in ("premiere", "country", "alternativ"):
535                                 if self.generalinfos.group(category):
536                                         Detailstext += "\n" + self.generalinfos.group('g_'+category) + ": " + self.htmltags.sub('', self.generalinfos.group(category).replace('\n',' ').replace("<br>", '\n').replace("<br />",'\n').replace("  ",' '))
537
538                         rating = self.ratingmask.search(self.inhtml)
539                         Ratingtext = _("no user rating yet")
540                         if rating:
541                                 rating = rating.group("rating")
542                                 if rating != '<span id="voteuser"></span>':
543                                         Ratingtext = _("User Rating") + ": " + rating + " / 10"
544                                         self.ratingstars = int(10*round(float(rating.replace(',','.')),1))
545                                         self["stars"].show()
546                                         self["stars"].setValue(self.ratingstars)
547                                         self["starsbg"].show()
548                         self["ratinglabel"].setText(Ratingtext)
549
550                         castresult = self.castmask.finditer(self.inhtml)
551                         if castresult:
552                                 Casttext = ""
553                                 for x in castresult:
554                                         Casttext += "\n" + self.htmltags.sub('', x.group(1))
555                                         if x.group(2):
556                                                 Casttext += _(" as ") + self.htmltags.sub('', x.group(2).replace('/ ...','')).replace('\n', ' ')
557                                                 if x.group(3):
558                                                         Casttext += x.group(3)
559                                 if Casttext:
560                                         Casttext = _("Cast: ") + Casttext
561                                 else:
562                                         Casttext = _("No cast list found in the database.")
563                                 self["castlabel"].setText(Casttext)
564
565                         posterurl = self.postermask.search(self.inhtml)
566                         if posterurl and posterurl.group(1).find("jpg") > 0:
567                                 posterurl = posterurl.group(1)
568                                 self["statusbar"].setText(_("Downloading Movie Poster: %s...") % (posterurl))
569                                 localfile = "/tmp/poster.jpg"
570                                 print("[IMDB] downloading poster " + posterurl + " to " + localfile)
571                                 downloadPage(posterurl,localfile).addCallback(self.IMDBPoster).addErrback(self.fetchFailed)
572                         else:
573                                 self.IMDBPoster("kein Poster")
574                         extrainfos = self.extrainfomask.search(self.inhtml)
575
576                         if extrainfos:
577                                 Extratext = "Extra Info\n"
578
579                                 for category in ("tagline","outline","synopsis","keywords","awards","runtime","language","color","aspect","sound","cert","locations","company","trivia","goofs","quotes","connections"):
580                                         if extrainfos.group('g_'+category):
581                                                 Extratext += extrainfos.group('g_'+category) + ": " + self.htmltags.sub('',extrainfos.group(category).replace("\n",'').replace("<br>", '\n').replace("<br />",'\n')) + "\n"
582                                 if extrainfos.group("g_comments"):
583                                         stripmask = re.compile('\s{2,}', re.DOTALL)
584                                         Extratext += extrainfos.group("g_comments") + " [" + stripmask.sub(' ', self.htmltags.sub('',extrainfos.group("commenter"))) + "]: " + self.htmltags.sub('',extrainfos.group("comment").replace("\n",' ')) + "\n"
585
586                                 self["extralabel"].setText(Extratext)
587                                 self["extralabel"].hide()
588                                 self["key_blue"].setText(_("Extra Info"))
589
590                 self["detailslabel"].setText(Detailstext)
591                 self.callbackData = Detailstext
592
593         def IMDBPoster(self,string):
594                 self["statusbar"].setText(_("IMDb Details parsed"))
595                 if not string:
596                         filename = "/tmp/poster.jpg"
597                 else:
598                         filename = resolveFilename(SCOPE_PLUGINS, "Extensions/IMDb/no_poster.png")
599                 sc = AVSwitch().getFramebufferScale()
600                 self.picload.setPara((self["poster"].instance.size().width(), self["poster"].instance.size().height(), sc[0], sc[1], False, 1, "#00000000"))
601                 self.picload.startDecode(filename)
602
603         def paintPosterPixmapCB(self, picInfo=None):
604                 ptr = self.picload.getData()
605                 if ptr != None:
606                         self["poster"].instance.setPixmap(ptr.__deref__())
607                         self["poster"].show()
608
609         def createSummary(self):
610                 return IMDbLCDScreen
611
612 class IMDbLCDScreen(Screen):
613         skin = """
614         <screen position="0,0" size="132,64" title="IMDB Plugin">
615                 <widget name="headline" position="4,0" size="128,22" font="Regular;20"/>
616                 <widget source="parent.title" render="Label" position="6,26" size="120,34" font="Regular;14"/>
617         </screen>"""
618
619         def __init__(self, session, parent):
620                 Screen.__init__(self, session, parent)
621                 self["headline"] = Label(_("IMDb Plugin"))
622
623 def eventinfo(session, servicelist, **kwargs):
624         ref = session.nav.getCurrentlyPlayingServiceReference()
625         session.open(IMDBEPGSelection, ref)
626
627 def main(session, eventName="", **kwargs):
628         session.open(IMDB, eventName)
629
630 def Plugins(**kwargs):
631         return [PluginDescriptor(name="IMDb Details",
632                         description=_("Query details from the Internet Movie Database"),
633                         icon="imdb.png",
634                         where=PluginDescriptor.WHERE_PLUGINMENU,
635                         fnc=main,
636                         needsRestart=False,
637                         ),
638                         PluginDescriptor(name="IMDb Details",
639                         description=_("Query details from the Internet Movie Database"),
640                         where=PluginDescriptor.WHERE_EVENTINFO,
641                         fnc=eventinfo,
642                         needsRestart=False,
643                         ),
644                 ]