1 # -*- coding: UTF-8 -*-
2 # for localized messages
5 from Plugins.Plugin import PluginDescriptor
6 from Tools.Downloader import downloadWithProgress
7 from enigma import ePicLoad, eServiceReference
8 from Screens.Screen import Screen
9 from Screens.EpgSelection import EPGSelection
10 from Screens.ChannelSelection import SimpleChannelSelection
11 from Screens.ChoiceBox import ChoiceBox
12 from Screens.VirtualKeyBoard import VirtualKeyBoard
13 from Components.ActionMap import ActionMap
14 from Components.Pixmap import Pixmap
15 from Components.Label import Label
16 from Components.ScrollLabel import ScrollLabel
17 from Components.Button import Button
18 from Components.AVSwitch import AVSwitch
19 from Components.MenuList import MenuList
20 from Components.Language import language
21 from Components.ProgressBar import ProgressBar
22 from Components.Sources.StaticText import StaticText
23 from Components.Sources.Boolean import Boolean
24 from Tools.Directories import fileExists, resolveFilename, SCOPE_PLUGINS, SCOPE_SKIN_IMAGE
28 from urllib import quote_plus
29 iteritems = lambda d: d.iteritems()
30 except ImportError as ie:
31 from html import entities as htmlentitydefs
32 from urllib.parse import quote_plus
33 iteritems = lambda d: d.items()
38 from Components.config import config, getConfigListEntry, ConfigSubsection, ConfigYesNo, ConfigText
39 from Components.ConfigList import ConfigListScreen
40 from Components.PluginComponent import plugins
41 from Tools.Directories import resolveFilename, SCOPE_PLUGINS
43 from HTMLParser import HTMLParser
47 return h.unescape(text)
49 config.plugins.imdb = ConfigSubsection()
50 config.plugins.imdb.showinplugins = ConfigYesNo(default = True)
51 config.plugins.imdb.force_english = ConfigYesNo(default=False)
52 config.plugins.imdb.ignore_tags = ConfigText(visible_width = 50, fixed_size = False)
54 def quoteEventName(eventName, safe="/()" + ''.join(map(chr,range(192,255)))):
55 # BBC uses '\x86' markers in program names, remove them
56 text = eventName.decode('utf8').replace(u'\x86', u'').replace(u'\x87', u'').encode('utf8')
57 # IMDb doesn't seem to like urlencoded characters at all, hence the big "safe" list
58 return quote_plus(text, safe=safe)
60 class IMDBChannelSelection(SimpleChannelSelection):
61 def __init__(self, session):
62 SimpleChannelSelection.__init__(self, session, _("Channel Selection"))
63 self.skinName = "SimpleChannelSelection"
65 self["ChannelSelectEPGActions"] = ActionMap(["ChannelSelectEPGActions"],
67 "showEPGList": self.channelSelected
71 def channelSelected(self):
72 ref = self.getCurrentSelection()
73 if (ref.flags & 7) == 7:
75 elif not (ref.flags & eServiceReference.isMarker):
76 self.session.openWithCallback(
83 def epgClosed(self, ret = None):
87 class IMDBEPGSelection(EPGSelection):
88 def __init__(self, session, ref, openPlugin = True):
89 EPGSelection.__init__(self, session, ref)
90 self.skinName = "EPGSelection"
91 self["key_green"].setText(_("Lookup"))
92 self.openPlugin = openPlugin
94 def infoKeyPressed(self):
98 cur = self["list"].getCurrent()
110 self.close(evt.getEventName())
112 def onSelectionChanged(self):
117 <screen name="IMDB" position="center,center" size="600,420" title="Internet Movie Database Details Plugin" >
118 <ePixmap pixmap="skin_default/buttons/red.png" position="0,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
119 <ePixmap pixmap="skin_default/buttons/green.png" position="140,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
120 <ePixmap pixmap="skin_default/buttons/yellow.png" position="280,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
121 <ePixmap pixmap="skin_default/buttons/blue.png" position="420,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
122 <ePixmap pixmap="skin_default/buttons/key_menu.png" position="565,5" zPosition="0" size="35,25" alphatest="on" />
123 <widget name="key_red" position="0,0" zPosition="1" size="140,40" font="Regular;20" valign="center" halign="center" backgroundColor="#9f1313" transparent="1" />
124 <widget name="key_green" position="140,0" zPosition="1" size="140,40" font="Regular;20" valign="center" halign="center" backgroundColor="#1f771f" transparent="1" />
125 <widget name="key_yellow" position="280,0" zPosition="1" size="140,40" font="Regular;20" valign="center" halign="center" backgroundColor="#a08500" transparent="1" />
126 <widget name="key_blue" position="420,0" zPosition="1" size="140,40" font="Regular;20" valign="center" halign="center" backgroundColor="#18188b" transparent="1" />
127 <widget source="title" render="Label" position="10,40" size="330,45" valign="center" font="Regular;22"/>
128 <widget name="detailslabel" position="105,90" size="485,140" font="Regular;18" />
129 <widget name="castlabel" position="10,235" size="580,155" font="Regular;18" />
130 <widget name="extralabel" position="10,40" size="580,350" font="Regular;18" />
131 <widget name="ratinglabel" position="340,62" size="250,20" halign="center" font="Regular;18" foregroundColor="#f0b400"/>
132 <widget name="statusbar" position="10,404" size="580,16" font="Regular;16" foregroundColor="#cccccc" />
133 <widget name="poster" position="4,90" size="96,140" alphatest="on" />
134 <widget name="menu" position="10,115" size="580,275" zPosition="3" scrollbarMode="showOnDemand" />
135 <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" />
136 <widget name="stars" position="340,40" size="210,21" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/IMDb/starsbar_filled.png" transparent="1" />
139 def __init__(self, session, eventName, callbackNeeded=False, save=False, savepath=None, localpath=None):
140 Screen.__init__(self, session)
142 for tag in config.plugins.imdb.ignore_tags.getValue().split(','):
143 eventName = eventName.replace(tag,'')
145 self.eventName = eventName
147 self.callbackNeeded = callbackNeeded
148 self.callbackData = ""
149 self.callbackGenre = ""
152 self.savingpath = savepath
153 self.localpath = localpath
156 self.dictionary_init()
158 self["poster"] = Pixmap()
159 self.picload = ePicLoad()
160 self.picload_conn = self.picload.PictureData.connect(self.paintPosterPixmapCB)
162 self["stars"] = ProgressBar()
163 self["starsbg"] = Pixmap()
165 self["starsbg"].hide()
166 self.ratingstars = -1
168 self["title"] = StaticText(_("The Internet Movie Database"))
169 # map new source -> old component
171 StaticText.setText(self["title"], txt)
172 self["titellabel"].setText(txt)
173 self["title"].setText = setText
174 self["titellabel"] = Label()
175 self["detailslabel"] = ScrollLabel("")
176 self["castlabel"] = ScrollLabel("")
177 self["extralabel"] = ScrollLabel("")
178 self["statusbar"] = Label("")
179 self["ratinglabel"] = Label("")
181 self["menu"] = MenuList(self.resultlist)
184 self["key_red"] = Button(_("Exit"))
185 self["key_green"] = Button("")
186 self["key_yellow"] = Button("")
187 self["key_blue"] = Button("")
189 # 0 = multiple query selection menu page
190 # 1 = movie info page
191 # 2 = extra infos page
194 self["actions"] = ActionMap(["OkCancelActions", "ColorActions", "MovieSelectionActions", "DirectionActions"],
196 "ok": self.showDetails,
198 "down": self.pageDown,
201 "green": self.showMenu,
202 "yellow": self.showDetails,
203 "blue": self.showExtras,
204 "contextMenu": self.contextMenuPressed,
205 "showEventInfo": self.showDetails
210 if self.localpath is not None: # otherwise the stars are not correctly shown if we call details directly
211 self.onLayoutFinish.append(self._layoutFinished)
213 def _layoutFinished(self):
215 self["extralabel"].hide()
216 self["stars"].setValue(self.ratingstars)
219 if fileExists("/tmp/poster.jpg"):
220 os.remove("/tmp/poster.jpg")
221 if fileExists("/tmp/imdbquery.html"):
222 os.remove("/tmp/imdbquery.html")
223 if fileExists("/tmp/imdbquery2.html"):
224 os.remove("/tmp/imdbquery2.html")
225 if self.callbackNeeded:
226 self.close([self.callbackData, self.callbackGenre])
230 def dictionary_init(self):
231 syslang = language.getLanguage()
232 if 1: #"de" not in syslang or config.plugins.imdb.force_english.value is True:
233 self.generalinfomask = re.compile(
234 #'<h1 class="header".*?>(?P<title>.*?)<.*?</h1>.*?'
235 '<h1 itemprop="name" class="".*?>(?P<title>.*?)<.*?/h1>*'
236 '(?:.*?<h4 class="inline">\s*(?P<g_director>Regisseur|Directors?):\s*</h4>.*?<a.*?>(?P<director>.*?)</a>)*'
237 '(?:.*?<h4 class="inline">\s*(?P<g_creator>Sch\S*?pfer|Creators?):\s*</h4>.*?<a.*?>(?P<creator>.*?)</a>)*'
238 '(?:.*?<h4 class="inline">\s*(?P<g_seasons>Seasons?):\s*</h4>.*?<a.*?>(?P<seasons>(?:\d+|unknown)?)</a>)*'
239 '(?:.*?<h4 class="inline">\s*(?P<g_writer>Drehbuch|Writer).*?</h4>.*?<a.*?>(?P<writer>.*?)</a>)*'
240 '(?:.*?<h4 class="inline">\s*(?P<g_country>Land|Country):\s*</h4>.*?<a.*?>(?P<country>.*?)</a>)*'
241 '(?:.*?<h4 class="inline">\s*(?P<g_premiere>Premiere|Release Date).*?</h4>\s+(?P<premiere>.*?)\s*<span)*'
242 '(?:.*?<h4 class="inline">\s*(?P<g_alternativ>Auch bekannt als|Also Known As):\s*</h4>\s*(?P<alternativ>.*?)\s*<span)*'
245 self.extrainfomask = re.compile(
246 '(?:.*?<h4 class="inline">(?P<g_outline>Kurzbeschreibung|Plot Outline):</h4>(?P<outline>.+?)<)*'
247 '(?:.*?<h2>(?P<g_synopsis>Storyline)</h2>.*?<p>(?P<synopsis>.+?)\s*</p>)*'
248 '(?:.*?<h4 class="inline">(?P<g_keywords>Plot Keywords):</h4>(?P<keywords>.+?)(?:Mehr|See more</a>|</div>))*'
249 '(?:.*?<h4 class="inline">(?P<g_tagline>Werbezeile|Tagline?):</h4>\s*(?P<tagline>.+?)<)*'
250 '(?:.*?<h4 class="inline">(?P<g_awards>Filmpreise|Awards):</h4>\s*(?P<awards>.+?)(?:Mehr|See more</a>|</div>))*'
251 '(?:.*?<h4 class="inline">(?P<g_language>Sprache|Language):</h4>\s*(?P<language>.+?)</div>)*'
252 '(?:.*?<h4 class="inline">(?P<g_locations>Drehorte|Filming Locations):</h4>.*?<a.*?>(?P<locations>.+?)</a>)*'
253 '(?:.*?<h4 class="inline">(?P<g_runtime>L\S*?nge|Runtime):</h4>\s*(?P<runtime>.+?)</div>)*'
254 '(?:.*?<h4 class="inline">(?P<g_sound>Tonverfahren|Sound Mix):</h4>\s*(?P<sound>.+?)</div>)*'
255 '(?:.*?<h4 class="inline">(?P<g_color>Farbe|Color):</h4>\s*(?P<color>.+?)</div>)*'
256 '(?:.*?<h4 class="inline">(?P<g_aspect>Seitenverh\S*?ltnis|Aspect Ratio):</h4>\s*(?P<aspect>.+?)(?:Mehr|See more</a>|</div>))*'
257 '(?:.*?<h4 class="inline">(?P<g_cert>Altersfreigabe|Certification):</h4>\s*(?P<cert>.+?)</div>)*'
258 '(?:.*?<h4 class="inline">(?P<g_company>Firma|Company):</h4>\s*(?P<company>.+?)(?:Mehr|See more</a>|</div>))*'
259 '(?:.*?<h4>(?P<g_trivia>Dies und das|Trivia)</h4>\s*(?P<trivia>.+?)(?:<span))*'
260 '(?:.*?<h4>(?P<g_goofs>Pannen|Goofs)</h4>\s*(?P<goofs>.+?)(?:<span))*'
261 '(?:.*?<h4>(?P<g_quotes>Dialogzitate|Quotes)</h4>\s*(?P<quotes>.+?)(?:<span))*'
262 '(?:.*?<h4>(?P<g_connections>Bez\S*?ge zu anderen Titeln|Movie Connections)</h4>\s*(?P<connections>.+?)(?:<span))*'
263 '(?:.*?<h2>(?P<g_comments>Nutzerkommentare|User Reviews)</h2>.*?<a href="/user/ur\d{7,7}/comments">(?P<commenter>.+?)</a>.*?<p>(?P<comment>.+?)</p>)*'
266 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)
267 self.ratingmask = re.compile('<span itemprop="ratingValue">(?P<rating>.*?)</', re.DOTALL)
268 self.castmask = re.compile('itemprop=.url.> <span class="itemprop" itemprop="name">(?P<actor>.*?)</span>.*?<a href="/character/.*?" >(?P<character>.*?)</a>', re.DOTALL)
269 #self.postermask = re.compile('<td .*?id="img_primary">.*?<img .*?src=\"(http.*?)\"', re.DOTALL)
270 self.postermask = re.compile('<div class="poster">.*?<img .*?src=\"(http.*?)\"', re.DOTALL)
272 self.htmltags = re.compile('<.*?>')
274 def resetLabels(self):
275 self["detailslabel"].setText("")
276 self["ratinglabel"].setText("")
277 self["title"].setText("")
278 self["castlabel"].setText("")
279 self["titellabel"].setText("")
280 self["extralabel"].setText("")
281 self.ratingstars = -1
285 self["menu"].instance.moveSelection(self["menu"].instance.moveUp)
287 self["castlabel"].pageUp()
288 self["detailslabel"].pageUp()
290 self["extralabel"].pageUp()
294 self["menu"].instance.moveSelection(self["menu"].instance.moveDown)
296 self["castlabel"].pageDown()
297 self["detailslabel"].pageDown()
299 self["extralabel"].pageDown()
302 if ( self.Page is 1 or self.Page is 2 ) and self.resultlist:
305 self["starsbg"].hide()
306 self["ratinglabel"].hide()
307 self["castlabel"].hide()
308 self["poster"].hide()
309 self["extralabel"].hide()
310 self["title"].setText(_("Ambiguous results"))
311 self["detailslabel"].setText(_("Please select the matching entry"))
312 self["detailslabel"].show()
313 self["key_blue"].setText("")
314 self["key_green"].setText(_("Title Menu"))
315 self["key_yellow"].setText(_("Details"))
318 def getLocalDetails(self):
319 localfile = self.localpath
320 self.html2utf8(open(localfile, "r").read())
321 self.generalinfos = self.generalinfomask.search(self.inhtml)
323 if self.ratingstars > 0:
324 self["starsbg"].show()
328 def showDetails(self):
329 self["ratinglabel"].show()
330 self["castlabel"].show()
331 self["detailslabel"].show()
333 if self.resultlist and self.Page == 0:
334 link = self["menu"].getCurrent()[1]
335 title = self["menu"].getCurrent()[0]
336 self["statusbar"].setText(_("Re-Query IMDb: %s...") % (title))
337 localfile = "/tmp/imdbquery2.html"
338 fetchurl = "http://imdb.com/title/" + link
339 print("[IMDB] showDetails() downloading query " + fetchurl + " to " + localfile)
340 download = downloadWithProgress(fetchurl,localfile)
341 download.start().addCallback(self.IMDBquery2).addErrback(self.http_failed)
342 self.fetchurl = fetchurl
348 self["extralabel"].hide()
349 self["poster"].show()
350 if self.ratingstars > 0:
351 self["starsbg"].show()
353 self["stars"].setValue(self.ratingstars)
357 def showExtras(self):
359 self["extralabel"].show()
360 self["detailslabel"].hide()
361 self["castlabel"].hide()
362 self["poster"].hide()
364 self["starsbg"].hide()
365 self["ratinglabel"].hide()
368 def contextMenuPressed(self):
370 (_("Enter search"), self.openVirtualKeyBoard),
371 (_("Select from EPG"), self.openChannelSelection),
372 (_("Setup"), self.setup),
376 if self.savingpath is not None:
377 # TODO: save Poster also as option for .html
379 (_("Save current Details as .html for offline using"), self.saveHtmlDetails),
380 (_("Save current Details as .txt"), self.saveTxtDetails),
381 (_("Save current Poster and Details as .txt"), self.savePosterTxtDetails),
384 if fileExists(resolveFilename(SCOPE_PLUGINS, "Extensions/YTTrailer/plugin.py")):
386 (_("Play Trailer"), self.openYttrailer),
387 (_("Search Trailer"), self.searchYttrailer),
390 self.session.openWithCallback(
393 title=_("IMDb Menu"),
397 def menuCallback(self, ret = None):
400 def saveHtmlDetails(self):
402 if self.savingpath is not None:
403 isave = self.savingpath + ".imdbquery2.html"
404 if self.fetchurl is not None:
405 download = downloadWithProgress(self.fetchurl,isave)
406 download.start().addCallback(self.IMDBsave).addErrback(self.http_failed)
408 print('[IMDb] saveHtmlDetails exception failure: ', str(e))
410 def saveTxtDetails(self):
412 if self.savingpath is not None:
413 getTXT = self.IMDBsavetxt()
414 if getTXT is not None:
415 file(self.savingpath + ".txt",'w').write(getTXT)
417 from Screens.MessageBox import MessageBox
418 self.session.open(MessageBox, (_('IMDb can not get Movie Information, to\n write .txt-file!')), MessageBox.TYPE_INFO, 10)
420 print('[IMDb] saveTxtDetails exception failure: ', str(e))
422 def savePosterTxtDetails(self):
424 if self.savingpath is not None:
425 getTXT = self.IMDBsavetxt(True)
426 if getTXT is not None:
427 file(self.savingpath + ".txt",'w').write(getTXT)
429 from Screens.MessageBox import MessageBox
430 self.session.open(MessageBox, (_('IMDb can not get Movie Information, to\n write .jpg and .txt-file!')), MessageBox.TYPE_INFO, 10)
432 print('[IMDb] savePosterTxtDetails exception failure: ', str(e))
434 def IMDBsave(self,string):
435 self["statusbar"].setText(_("IMDb Save-Download completed"))
436 self.html2utf8(open("/tmp/imdbquery2.html", "r").read())
437 self.generalinfos = self.generalinfomask.search(self.inhtml)
440 def IMDBsavetxt(self, poster=False):
448 if self.generalinfos:
449 extrainfos = self.extrainfomask.search(self.inhtml)
451 # get entry 1 = Overview(details)
453 text = self.htmltags.sub('',extrainfos.group("synopsis").replace("\n",'').replace("<br>", '\n').replace("<br />",'\n').replace('&view=simple&sort=alpha&ref_=tt_stry_pl" >',' '))
454 overview = (_("Content:") + " " + text.encode('utf-8'))
456 print('[IMDb] IMDBsavetxt exception failure in get overview: ', str(e))
457 overview = (_("Content:"))
458 # print'[IMDb] IMDBsavetxt overview: ', overview
460 # get entry 2 = Runtime
462 time = self.htmltags.sub('',extrainfos.group("runtime").replace("\n",'').replace("<br>", '\n').replace("<br />",'\n').replace('&view=simple&sort=alpha&ref_=tt_stry_pl" >',' '))
463 runtime = (_("Runtime:") + " " + time.encode('utf-8'))
465 print('[IMDb] IMDBsavetxt exception failure in get runtime: ', str(e))
466 runtime = (_("Runtime:"))
467 # print'[IMDb] IMDBsavetxt runtime: ', runtime
469 # get entry 3 = Genre
470 genreblock = self.genreblockmask.findall(self.inhtml)
472 genres = self.htmltags.sub('', genreblock[0])
474 genre = (_("Genre:") + " " + genres.encode('utf-8'))
476 genre = (_("Genre:"))
477 # print'[IMDb] IMDBsavetxt genre: ', genre
479 # get entry 4 = Country
481 land = self.htmltags.sub('', self.generalinfos.group("country").replace('\n',' ').replace("<br>", '\n').replace("<br />",'\n'))
482 country = (_("Production Countries:") + " " + land.encode('utf-8'))
484 print('[IMDb] IMDBsavetxt exception failure in get country: ', str(e))
485 country = (_("Production Countries:"))
486 # print'[IMDb] IMDBsavetxt country: ', country
488 # get entry 5 = ReleaseDate
490 date = self.htmltags.sub('', self.generalinfos.group("premiere").replace('\n',' ').replace("<br>", '\n').replace("<br />",'\n'))
491 release = (_("Release Date:") + " " + date.encode('utf-8'))
493 print('[IMDb] IMDBsavetxt exception failure in get release: ', str(e))
494 release = (_("Release Date:"))
495 # print'[IMDb] IMDBsavetxt release: ', release
498 ratingtext = self.ratingmask.search(self.inhtml)
500 ratingtext = ratingtext.group("rating")
501 if ratingtext != '<span id="voteuser"></span>':
502 text = ratingtext # + " / 10"
503 rating = (_("User Rating") + ": " + text.encode('utf-8'))
505 rating = (_("User Rating") + ": ")
506 # print'[IMDb] IMDBsavetxt rating: ', rating
511 posterurl = self.postermask.search(self.inhtml)
512 if posterurl and posterurl.group(1).find("jpg") > 0:
513 posterurl = posterurl.group(1)
514 postersave = self.savingpath + ".poster.jpg"
515 print("[IMDB] downloading poster " + posterurl + " to " + postersave)
516 download = downloadWithProgress(posterurl,postersave)
517 download.start().addErrback(self.http_failed)
519 print('[IMDb] IMDBsavetxt exception failure in get poster: ', str(e))
521 return overview + "\n\n" + runtime + "\n" + genre + "\n" + country + "\n" + release + "\n" + rating + "\n"
523 def openYttrailer(self):
525 from Plugins.Extensions.YTTrailer.plugin import YTTrailer, baseEPGSelection__init__
526 except ImportError as ie:
528 if baseEPGSelection__init__ is None:
531 ytTrailer = YTTrailer(self.session)
532 ytTrailer.showTrailer(self.eventName)
534 def searchYttrailer(self):
536 from Plugins.Extensions.YTTrailer.plugin import YTTrailerList, baseEPGSelection__init__
537 except ImportError as ie:
539 if baseEPGSelection__init__ is None:
542 self.session.open(YTTrailerList, self.eventName)
544 def openVirtualKeyBoard(self):
545 self.session.openWithCallback(
546 self.gotSearchString,
548 title = _("Enter text to search for"),
549 text = self.eventName
552 def openChannelSelection(self):
553 self.session.openWithCallback(
554 self.gotSearchString,
558 def gotSearchString(self, ret = None):
564 self["ratinglabel"].show()
565 self["castlabel"].show()
566 self["detailslabel"].show()
567 self["poster"].hide()
569 self["starsbg"].hide()
570 self.getIMDB(search=True)
572 def getIMDB(self, search=False):
574 if not self.eventName:
575 s = self.session.nav.getCurrentService()
576 info = s and s.info()
577 event = info and info.getEvent(0) # 0 = now, 1 = next
579 self.eventName = event.getEventName()
581 self.eventName = self.session.nav.getCurrentlyPlayingServiceReference().toString()
582 self.eventName = self.eventName.split('/')
583 self.eventName = self.eventName[-1]
584 self.eventName = self.eventName.replace('.',' ')
585 self.eventName = self.eventName.split('-')
586 self.eventName = self.eventName[0]
587 if self.eventName.endswith(' '):
588 self.eventName = self.eventName[:-1]
590 if self.localpath is not None and not search:
591 if os.path.exists(self.localpath):
592 self.getLocalDetails()
596 self["statusbar"].setText(_("Query IMDb: %s") % (self.eventName))
597 localfile = "/tmp/imdbquery.html"
598 fetchurl = "http://imdb.com/find?q=" + quoteEventName(self.eventName) + "&s=tt&site=aka"
599 print("[IMDB] getIMDB() Downloading Query " + fetchurl + " to " + localfile)
600 download = downloadWithProgress(fetchurl,localfile)
601 download.start().addCallback(self.IMDBquery).addErrback(self.http_failed)
604 self["statusbar"].setText(_("Could't get Eventname"))
606 def html2utf8(self,in_html):
607 in_html = (re.subn(r'<(script).*?</\1>(?s)', '', in_html)[0])
608 in_html = (re.subn(r'<(style).*?</\1>(?s)', '', in_html)[0])
611 entities = re.finditer('&([^#][A-Za-z]{1,5}?);', in_html)
614 if key not in entitydict:
615 entitydict[key] = htmlentitydefs.name2codepoint[x.group(1)]
617 entities = re.finditer('&#x([0-9A-Fa-f]{2,2}?);', in_html)
620 if key not in entitydict:
621 entitydict[key] = "%d" % int(key[3:5], 16)
623 entities = re.finditer('&#(\d{1,5}?);', in_html)
626 if key not in entitydict:
627 entitydict[key] = x.group(1)
629 if re.search("charset=utf-8", in_html):
630 for key, codepoint in iteritems(entitydict):
631 in_html = in_html.replace(key, unichr(int(codepoint)))
632 self.inhtml = in_html.encode('utf8')
635 for key, codepoint in iteritems(entitydict):
636 in_html = in_html.replace(key, unichr(int(codepoint)).encode('latin-1', 'ignore'))
637 self.inhtml = in_html.decode('latin-1').encode('utf8')
639 def IMDBquery(self,string):
640 self["statusbar"].setText(_("IMDb Download completed"))
642 self.html2utf8(open("/tmp/imdbquery.html", "r").read())
644 self.generalinfos = self.generalinfomask.search(self.inhtml)
646 if self.generalinfos:
649 if re.search("<title>Find - IMDb</title>", self.inhtml):
650 pos = self.inhtml.find("<table class=\"findList\">")
651 pos2 = self.inhtml.find("</table>",pos)
652 findlist = self.inhtml[pos:pos2]
653 searchresultmask = re.compile('<tr class=\"findResult (?:odd|even)\">.*?<td class=\"result_text\"> <a href=\"/title/(tt\d{7,7})/.*?\"\s?>(.*?)</a>.*?</td>', re.DOTALL)
654 searchresults = searchresultmask.finditer(findlist)
655 self.resultlist = [(self.htmltags.sub('',x.group(2)), x.group(1)) for x in searchresults]
656 Len = len(self.resultlist)
657 self["menu"].l.setList(self.resultlist)
659 self["statusbar"].setText(_("Re-Query IMDb: %s...") % (self.resultlist[0][0],))
660 self.eventName = self.resultlist[0][1]
661 localfile = "/tmp/imdbquery.html"
662 fetchurl = "http://imdb.com/find?q=" + quoteEventName(self.eventName) + "&s=tt&site=aka"
663 download = downloadWithProgress(fetchurl,localfile)
664 download.start().addCallback(self.IMDBquery).addErrback(self.http_failed)
669 self["detailslabel"].setText(_("No IMDb match."))
670 self["statusbar"].setText(_("No IMDb match.") + ' ' + self.eventName)
672 splitpos = self.eventName.find('(')
673 if splitpos > 0 and self.eventName.endswith(')'):
674 self.eventName = self.eventName[splitpos+1:-1]
675 self["statusbar"].setText(_("Re-Query IMDb: %s...") % (self.eventName))
676 # event_quoted = quoteEventName(self.eventName)
677 localfile = "/tmp/imdbquery.html"
678 fetchurl = "http://imdb.com/find?q=" + quoteEventName(self.eventName) + "&s=tt&site=aka"
679 download = downloadWithProgress(fetchurl,localfile)
680 download.start().addCallback(self.IMDBquery).addErrback(self.http_failed)
682 self["detailslabel"].setText(_("IMDb query failed!"))
684 def http_failed(self, failure_instance=None, error_message=""):
685 text = _("IMDb Download failed")
686 if error_message == "" and failure_instance is not None:
687 error_message = failure_instance.getErrorMessage()
688 text += ": " + error_message
689 print("[IMDB] ",text)
690 self["statusbar"].setText(text)
692 def IMDBquery2(self,string):
693 self["statusbar"].setText(_("IMDb Re-Download completed"))
694 self.html2utf8(open("/tmp/imdbquery2.html", "r").read())
695 self.generalinfos = self.generalinfomask.search(self.inhtml)
700 Detailstext = _("No details found.")
701 if self.generalinfos:
702 self["key_yellow"].setText(_("Details"))
703 self["statusbar"].setText(_("IMDb Details parsed"))
704 Titeltext = self.generalinfos.group("title")
705 if len(Titeltext) > 57:
706 Titeltext = Titeltext[0:54] + "..."
707 self["title"].setText(Titeltext)
711 genreblock = self.genreblockmask.findall(self.inhtml)
713 genres = self.htmltags.sub('', genreblock[0])
715 Detailstext += "Genre: "
716 Detailstext += genres
717 self.callbackGenre = genres
719 for category in ("director", "creator", "writer", "seasons"):
720 if self.generalinfos.group(category):
721 Detailstext += "\n" + self.generalinfos.group('g_'+category) + ": " + self.generalinfos.group(category).replace('<span class="itemprop" itemprop="name">','').replace('</span>','')
723 for category in ("premiere", "country", "alternativ"):
724 if self.generalinfos.group(category):
725 Detailstext += "\n" + self.generalinfos.group('g_'+category) + ": " + self.htmltags.sub('', self.generalinfos.group(category).replace('\n',' ').replace("<br>", '\n').replace("<br />",'\n'))
727 rating = self.ratingmask.search(self.inhtml)
728 Ratingtext = _("no user rating yet")
730 rating = rating.group("rating")
731 if rating != '<span id="voteuser"></span>':
732 Ratingtext = _("User Rating") + ": " + rating + " / 10"
733 self.ratingstars = int(10*round(float(rating.replace(',','.')),1))
735 self["stars"].setValue(self.ratingstars)
736 self["starsbg"].show()
737 self["ratinglabel"].setText(Ratingtext)
739 castresult = self.castmask.finditer(self.inhtml)
743 Casttext += "\n" + self.htmltags.sub('', x.group('actor'))
744 if x.group('character'):
745 Casttext += _(" as ") + self.htmltags.sub('', x.group('character').replace('/ ...','')).replace('\n', ' ')
746 #if x.group('additional'):
747 # Casttext += ' ' + x.group('additional')
749 Casttext = _("Cast: ") + Casttext
751 Casttext = _("No cast list found in the database.")
752 self["castlabel"].setText(Casttext)
754 posterurl = self.postermask.search(self.inhtml)
755 if posterurl and posterurl.group(1).find("jpg") > 0:
756 posterurl = posterurl.group(1)
757 self["statusbar"].setText(_("Downloading Movie Poster: %s...") % (posterurl))
758 localfile = "/tmp/poster.jpg"
759 print("[IMDB] downloading poster " + posterurl + " to " + localfile)
760 download = downloadWithProgress(posterurl,localfile)
761 download.start().addCallback(self.IMDBPoster).addErrback(self.http_failed)
763 self.IMDBPoster("kein Poster")
764 extrainfos = self.extrainfomask.search(self.inhtml)
767 Extratext = "Extra Info\n"
769 for category in ("tagline","outline","synopsis","keywords","awards","runtime","language","color","aspect","sound","cert","locations","company","trivia","goofs","quotes","connections"):
770 if extrainfos.group('g_'+category):
771 Extratext += extrainfos.group('g_'+category) + ": " + self.htmltags.sub('',extrainfos.group(category).replace("\n",'').replace("<br>", '\n').replace("<br />",'\n').replace('&view=simple&sort=alpha&ref_=tt_stry_pl" >',' ')) + "\n"
772 if extrainfos.group("g_comments"):
773 stripmask = re.compile('\s{2,}', re.DOTALL)
774 Extratext += extrainfos.group("g_comments") + " [" + stripmask.sub(' ', self.htmltags.sub('',extrainfos.group("commenter"))) + "]: " + self.htmltags.sub('',extrainfos.group("comment").replace("\n",' ')) + "\n"
776 Extratext = transHTML(Extratext)
777 self["extralabel"].setText(Extratext)
778 self["extralabel"].hide()
779 self["key_blue"].setText(_("Extra Info"))
781 self["detailslabel"].setText(Detailstext)
782 self.callbackData = Detailstext
784 def IMDBPoster(self,string):
785 self["statusbar"].setText(_("IMDb Details parsed"))
787 filename = "/tmp/poster.jpg"
789 filename = resolveFilename(SCOPE_PLUGINS, "Extensions/IMDb/no_poster.png")
790 sc = AVSwitch().getFramebufferScale()
791 self.picload.setPara((self["poster"].instance.size().width(), self["poster"].instance.size().height(), sc[0], sc[1], False, 1, "#00000000"))
792 self.picload.startDecode(filename)
794 def paintPosterPixmapCB(self, picInfo=None):
795 ptr = self.picload.getData()
797 self["poster"].instance.setPixmap(ptr)
798 self["poster"].show()
801 self.session.open(IMDbSetup)
803 def createSummary(self):
806 class IMDbLCDScreen(Screen):
808 <screen position="0,0" size="132,64" title="IMDB Plugin">
809 <widget name="headline" position="4,0" size="128,22" font="Regular;20"/>
810 <widget source="parent.title" render="Label" position="6,26" size="120,34" font="Regular;14"/>
813 def __init__(self, session, parent):
814 Screen.__init__(self, session, parent)
815 self["headline"] = Label(_("IMDb Plugin"))
817 class IMDbSetup(Screen, ConfigListScreen):
818 skin = """<screen name="EPGSearchSetup" position="center,center" size="565,370">
819 <ePixmap pixmap="skin_default/buttons/red.png" position="0,0" size="140,40" alphatest="on" />
820 <ePixmap pixmap="skin_default/buttons/green.png" position="140,0" size="140,40" alphatest="on" />
821 <widget source="key_red" render="Label" position="0,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#9f1313" transparent="1" />
822 <widget source="key_green" render="Label" position="140,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#1f771f" transparent="1" />
823 <widget name="config" position="5,50" size="555,250" scrollbarMode="showOnDemand" />
824 <ePixmap pixmap="skin_default/div-h.png" position="0,301" zPosition="1" size="565,2" />
825 <widget source="help" render="Label" position="5,305" size="555,63" font="Regular;21" />
828 def __init__(self, session):
829 Screen.__init__(self, session)
830 self.skinName = ["Setup" ]
832 self['footnote'] = Label(_("* = Restart Required"))
833 self["HelpWindow"] = Pixmap()
834 self["HelpWindow"].hide()
835 self["VKeyIcon"] = Boolean(False)
838 self.setup_title = _("IMDb Setup")
839 self.onChangedEntry = []
842 self["key_green"] = StaticText(_("OK"))
843 self["key_red"] = StaticText(_("Cancel"))
844 self["description"] = Label("")
847 self["actions"] = ActionMap(["SetupActions"],
849 "cancel": self.keyCancel,
850 "save": self.keySave,
853 self["VirtualKB"] = ActionMap(["VirtualKeyboardActions"],
855 "showVirtualKeyboard": self.KeyText,
857 self["VirtualKB"].setEnabled(False)
860 ConfigListScreen.__init__(self, self.list, session = self.session, on_change = self.changedEntry)
862 if not self.handleInputHelpers in self["config"].onSelectionChanged:
863 self["config"].onSelectionChanged.append(self.handleInputHelpers)
865 self.onLayoutFinish.append(self.layoutFinished)
867 def createSetup(self):
869 self.list.append(getConfigListEntry(_("Show in plugin browser"), config.plugins.imdb.showinplugins, _("Enable this to be able to access the IMDb from within the plugin browser.")))
870 self.list.append(getConfigListEntry(_("Words / phrases to ignore "), config.plugins.imdb.ignore_tags, _("This option allows you add words/phrases for IMDb to ignore when searching. please seperaate with a comma")))
871 self["config"].list = self.list
872 self["config"].l.setList(self.list)
874 def handleInputHelpers(self):
875 if self["config"].getCurrent() is not None:
877 if isinstance(self["config"].getCurrent()[1], ConfigText) or isinstance(self["config"].getCurrent()[1], ConfigPassword):
878 if self.has_key("VKeyIcon"):
879 self["VirtualKB"].setEnabled(True)
880 self["VKeyIcon"].boolean = True
881 if self.has_key("HelpWindow"):
882 if self["config"].getCurrent()[1].help_window.instance is not None:
883 helpwindowpos = self["HelpWindow"].getPosition()
884 from enigma import ePoint
885 self["config"].getCurrent()[1].help_window.instance.move(ePoint(helpwindowpos[0],helpwindowpos[1]))
887 if self.has_key("VKeyIcon"):
888 self["VirtualKB"].setEnabled(False)
889 self["VKeyIcon"].boolean = False
891 if self.has_key("VKeyIcon"):
892 self["VirtualKB"].setEnabled(False)
893 self["VKeyIcon"].boolean = False
895 if self.has_key("VKeyIcon"):
896 self["VirtualKB"].setEnabled(False)
897 self["VKeyIcon"].boolean = False
901 if isinstance(self["config"].getCurrent()[1], ConfigText):
902 if self["config"].getCurrent()[1].help_window.instance is not None:
903 self["config"].getCurrent()[1].help_window.hide()
908 if isinstance(self["config"].getCurrent()[1], ConfigText):
909 if self["config"].getCurrent()[1].help_window.instance is not None:
910 self["config"].getCurrent()[1].help_window.hide()
911 self.session.openWithCallback(self.VirtualKeyBoardCallback, VirtualKeyBoard, title = self["config"].getCurrent()[0], text = self["config"].getCurrent()[1].getValue())
913 def VirtualKeyBoardCallback(self, callback = None):
914 if callback is not None and len(callback):
915 self["config"].getCurrent()[1].setValue(callback)
916 self["config"].invalidate(self["config"].getCurrent())
918 def layoutFinished(self):
919 self.setTitle(_(self.setup_title))
922 def changedEntry(self):
923 self.item = self["config"].getCurrent()
924 for x in self.onChangedEntry:
927 if isinstance(self["config"].getCurrent()[1], ConfigYesNo) or isinstance(self["config"].getCurrent()[1], ConfigSelection):
932 def getCurrentEntry(self):
933 return self["config"].getCurrent() and self["config"].getCurrent()[0] or ""
935 def getCurrentValue(self):
936 return self["config"].getCurrent() and str(self["config"].getCurrent()[1].getText()) or ""
938 def getCurrentDescription(self):
939 return self["config"].getCurrent() and len(self["config"].getCurrent()) > 2 and self["config"].getCurrent()[2] or ""
941 def createSummary(self):
942 from Screens.Setup import SetupSummary
947 if not config.plugins.imdb.showinplugins.value:
948 for plugin in plugins.getPlugins(PluginDescriptor.WHERE_PLUGINMENU):
949 if plugin.name == _("IMDb Details"):
950 plugins.removePlugin(plugin)
952 plugins.readPluginList(resolveFilename(SCOPE_PLUGINS))
955 def createSummary(self):
956 from Screens.Setup import SetupSummary
959 def eventinfo(session, eventName="", **kwargs):
961 s = session.nav.getCurrentService()
964 event = info.getEvent(0) # 0 = now, 1 = next
965 eventName = event and event.getEventName() or ''
966 session.open(IMDB, eventName)
968 def main(session, eventName="", **kwargs):
969 session.open(IMDB, eventName)
971 pluginlist = PluginDescriptor(name=_("IMDb Details"), description=_("Query details from the Internet Movie Database"), icon="imdb.png", where=PluginDescriptor.WHERE_PLUGINMENU, fnc=main, needsRestart=False)
973 def Plugins(**kwargs):
974 l = [PluginDescriptor(name=_("IMDb Details") + "...",
975 description=_("Query details from the Internet Movie Database"),
976 where=PluginDescriptor.WHERE_EVENTINFO,
982 if config.plugins.imdb.showinplugins.value: