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
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()
36 config.plugins.imdb = ConfigSubsection()
37 config.plugins.imdb.force_english = ConfigYesNo(default=False)
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"))
45 t = gettext.dgettext("IMDb", txt)
47 print("[IMDb] fallback to default translation for", txt)
48 t = gettext.gettext(txt)
52 language.addCallback(localeInit)
54 class IMDBChannelSelection(SimpleChannelSelection):
55 def __init__(self, session):
56 SimpleChannelSelection.__init__(self, session, _("Channel Selection"))
57 self.skinName = "SimpleChannelSelection"
59 self["ChannelSelectEPGActions"] = ActionMap(["ChannelSelectEPGActions"],
61 "showEPGList": self.channelSelected
65 def channelSelected(self):
66 ref = self.getCurrentSelection()
67 if (ref.flags & 7) == 7:
69 elif not (ref.flags & eServiceReference.isMarker):
70 self.session.openWithCallback(
77 def epgClosed(self, ret = None):
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
88 def infoKeyPressed(self):
92 cur = self["list"].getCurrent()
104 self.close(evt.getEventName())
106 def onSelectionChanged(self):
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" />
133 def __init__(self, session, eventName, callbackNeeded=False):
134 Screen.__init__(self, session)
136 self.eventName = eventName
138 self.callbackNeeded = callbackNeeded
139 self.callbackData = ""
140 self.callbackGenre = ""
142 self.dictionary_init()
144 self["poster"] = Pixmap()
145 self.picload = ePicLoad()
146 self.picload.PictureData.get().append(self.paintPosterPixmapCB)
148 self["stars"] = ProgressBar()
149 self["starsbg"] = Pixmap()
151 self["starsbg"].hide()
152 self.ratingstars = -1
154 self["title"] = StaticText(_("The Internet Movie Database"))
155 # map new source -> old component
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("")
167 self["menu"] = MenuList(self.resultlist)
170 self["key_red"] = Button(_("Exit"))
171 self["key_green"] = Button("")
172 self["key_yellow"] = Button("")
173 self["key_blue"] = Button("")
175 # 0 = multiple query selection menu page
176 # 1 = movie info page
177 # 2 = extra infos page
180 self["actions"] = ActionMap(["OkCancelActions", "ColorActions", "MovieSelectionActions", "DirectionActions"],
182 "ok": self.showDetails,
184 "down": self.pageDown,
187 "green": self.showMenu,
188 "yellow": self.showDetails,
189 "blue": self.showExtras,
190 "contextMenu": self.contextMenuPressed,
191 "showEventInfo": self.showDetails
197 if self.callbackNeeded:
198 self.close([self.callbackData, self.callbackGenre])
202 event_quoted = property(lambda self: quote_plus(self.eventName.encode('utf8')))
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
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)*'
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>)*'
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)
246 self.IMDBlanguage = "german." # it's a subdomain, so add a '.' at the end
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>))*'
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>)*'
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)
285 self.htmltags = re.compile('<.*?>')
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
298 self["menu"].instance.moveSelection(self["menu"].instance.moveUp)
300 self["castlabel"].pageUp()
301 self["detailslabel"].pageUp()
303 self["extralabel"].pageUp()
307 self["menu"].instance.moveSelection(self["menu"].instance.moveDown)
309 self["castlabel"].pageDown()
310 self["detailslabel"].pageDown()
312 self["extralabel"].pageDown()
315 if ( self.Page is 1 or self.Page is 2 ) and self.resultlist:
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"))
331 def showDetails(self):
332 self["ratinglabel"].show()
333 self["castlabel"].show()
334 self["detailslabel"].show()
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)
349 self["extralabel"].hide()
350 self["poster"].show()
351 if self.ratingstars > 0:
352 self["starsbg"].show()
354 self["stars"].setValue(self.ratingstars)
358 def showExtras(self):
360 self["extralabel"].show()
361 self["detailslabel"].hide()
362 self["castlabel"].hide()
363 self["poster"].hide()
365 self["starsbg"].hide()
366 self["ratinglabel"].hide()
369 def contextMenuPressed(self):
371 (_("Enter search"), self.openVirtualKeyBoard),
372 (_("Select from EPG"), self.openChannelSelection),
375 self.session.openWithCallback(
381 def menuCallback(self, ret = None):
384 def openVirtualKeyBoard(self):
385 self.session.openWithCallback(
386 self.gotSearchString,
388 title = _("Enter text to search for")
391 def openChannelSelection(self):
392 self.session.openWithCallback(
393 self.gotSearchString,
397 def gotSearchString(self, ret = None):
403 self["ratinglabel"].show()
404 self["castlabel"].show()
405 self["detailslabel"].show()
406 self["poster"].hide()
408 self["starsbg"].hide()
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
418 self.eventName = event.getEventName()
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"
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)
429 self["statusbar"].setText(_("Could't get Eventname"))
431 def fetchFailed(self,string):
432 print("[IMDB] fetch failed", string)
433 self["statusbar"].setText(_("IMDb Download failed"))
435 def html2utf8(self,in_html):
438 entities = re.finditer('&([^#][A-Za-z]{1,5}?);', in_html)
441 if key not in entitydict:
442 entitydict[key] = htmlentitydefs.name2codepoint[x.group(1)]
444 entities = re.finditer('&#x([0-9A-Fa-f]{2,2}?);', in_html)
447 if key not in entitydict:
448 entitydict[key] = "%d" % int(key[3:5], 16)
450 entities = re.finditer('&#(\d{1,5}?);', in_html)
453 if key not in entitydict:
454 entitydict[key] = x.group(1)
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')
460 def IMDBquery(self,string):
462 self["statusbar"].setText(_("IMDb Download completed"))
464 self.html2utf8(open("/tmp/imdbquery.html", "r").read())
466 self.generalinfos = self.generalinfomask.search(self.inhtml)
468 if self.generalinfos:
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)
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)
488 self["detailslabel"].setText(_("No IMDb match."))
489 self["statusbar"].setText(_("No IMDb match."))
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)
500 self["detailslabel"].setText(_("IMDb query failed!"))
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)
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)
522 genreblock = self.genreblockmask.findall(self.inhtml)
524 genres = self.htmltags.sub('', genreblock[0])
526 Detailstext += "Genre: "
527 Detailstext += genres
528 self.callbackGenre = genres
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)
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(" ",' '))
538 rating = self.ratingmask.search(self.inhtml)
539 Ratingtext = _("no user rating yet")
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))
546 self["stars"].setValue(self.ratingstars)
547 self["starsbg"].show()
548 self["ratinglabel"].setText(Ratingtext)
550 castresult = self.castmask.finditer(self.inhtml)
554 Casttext += "\n" + self.htmltags.sub('', x.group(1))
556 Casttext += _(" as ") + self.htmltags.sub('', x.group(2).replace('/ ...','')).replace('\n', ' ')
558 Casttext += x.group(3)
560 Casttext = _("Cast: ") + Casttext
562 Casttext = _("No cast list found in the database.")
563 self["castlabel"].setText(Casttext)
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)
573 self.IMDBPoster("kein Poster")
574 extrainfos = self.extrainfomask.search(self.inhtml)
577 Extratext = "Extra Info\n"
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"
586 self["extralabel"].setText(Extratext)
587 self["extralabel"].hide()
588 self["key_blue"].setText(_("Extra Info"))
590 self["detailslabel"].setText(Detailstext)
591 self.callbackData = Detailstext
593 def IMDBPoster(self,string):
594 self["statusbar"].setText(_("IMDb Details parsed"))
596 filename = "/tmp/poster.jpg"
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)
603 def paintPosterPixmapCB(self, picInfo=None):
604 ptr = self.picload.getData()
606 self["poster"].instance.setPixmap(ptr.__deref__())
607 self["poster"].show()
609 def createSummary(self):
612 class IMDbLCDScreen(Screen):
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"/>
619 def __init__(self, session, parent):
620 Screen.__init__(self, session, parent)
621 self["headline"] = Label(_("IMDb Plugin"))
623 def eventinfo(session, servicelist, **kwargs):
624 ref = session.nav.getCurrentlyPlayingServiceReference()
625 session.open(IMDBEPGSelection, ref)
627 def main(session, eventName="", **kwargs):
628 session.open(IMDB, eventName)
630 def Plugins(**kwargs):
631 return [PluginDescriptor(name="IMDb Details",
632 description=_("Query details from the Internet Movie Database"),
634 where=PluginDescriptor.WHERE_PLUGINMENU,
638 PluginDescriptor(name="IMDb Details",
639 description=_("Query details from the Internet Movie Database"),
640 where=PluginDescriptor.WHERE_EVENTINFO,