IMDb fixed cover download
[enigma2-plugins.git] / imdb / src / plugin.py
1 # -*- coding: UTF-8 -*-
2 from Plugins.Plugin import PluginDescriptor
3 from Tools.Downloader import downloadWithProgress
4 from enigma import ePicLoad, eServiceReference, getDesktop
5 from Screens.Screen import Screen
6 from Screens.EpgSelection import EPGSelection
7 from Screens.ChannelSelection import SimpleChannelSelection
8 from Screens.ChoiceBox import ChoiceBox
9 from Screens.VirtualKeyBoard import VirtualKeyBoard
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.ProgressBar import ProgressBar
18 from Components.Sources.StaticText import StaticText
19 from Components.Sources.Boolean import Boolean
20 from Tools.Directories import fileExists, resolveFilename, SCOPE_PLUGINS, SCOPE_SKIN_IMAGE
21 import os, re
22 try:
23         from urllib import quote_plus
24 except ImportError as ie:
25         from urllib.parse import quote_plus
26
27 from Components.config import config, getConfigListEntry, ConfigSubsection, ConfigYesNo, ConfigText, ConfigSelection
28 from Components.ConfigList import ConfigListScreen
29 from Components.PluginComponent import plugins
30 from Tools.Directories import resolveFilename, SCOPE_PLUGINS
31
32 from HTMLParser import HTMLParser
33
34 sz_w = getDesktop(0).size().width()
35
36 def transHTML(text):
37         h = HTMLParser()
38         return h.unescape(text)
39
40 config.plugins.imdb = ConfigSubsection()
41 config.plugins.imdb.showinplugins = ConfigYesNo(default = True)
42 config.plugins.imdb.ignore_tags = ConfigText(visible_width = 50, fixed_size = False)
43 config.plugins.imdb.language = ConfigSelection(default = None, choices = [(None, _("Default")),("en-us", _("English")),("fr-fr", _("French")),("de-de", _("German")),("it-it", _("Italian")),("es-es", _("Spanish"))])
44
45 imdb_headers = {}
46
47 def quoteEventName(eventName):
48         eventName = eventName.replace(' ','+')
49         return quote_plus(eventName, safe='+')
50
51 class IMDBChannelSelection(SimpleChannelSelection):
52         def __init__(self, session):
53                 SimpleChannelSelection.__init__(self, session, _("Channel Selection"))
54                 self.skinName = "SimpleChannelSelection"
55
56                 self["ChannelSelectEPGActions"] = ActionMap(["ChannelSelectEPGActions"],
57                         {
58                                 "showEPGList": self.channelSelected
59                         }
60                 )
61
62         def channelSelected(self):
63                 ref = self.getCurrentSelection()
64                 if (ref.flags & 7) == 7:
65                         self.enterPath(ref)
66                 elif not (ref.flags & eServiceReference.isMarker):
67                         self.session.openWithCallback(
68                                 self.epgClosed,
69                                 IMDBEPGSelection,
70                                 ref,
71                                 openPlugin = False
72                         )
73
74         def epgClosed(self, ret = None):
75                 if ret:
76                         self.close(ret)
77
78 class IMDBEPGSelection(EPGSelection):
79         def __init__(self, session, ref, openPlugin = True):
80                 EPGSelection.__init__(self, session, ref)
81                 self.skinName = "EPGSelection"
82                 self["key_green"].setText(_("Lookup"))
83                 self.openPlugin = openPlugin
84
85         def infoKeyPressed(self):
86                 self.timerAdd()
87
88         def timerAdd(self):
89                 cur = self["list"].getCurrent()
90                 evt = cur[0]
91                 sref = cur[1]
92                 if not evt:
93                         return
94
95                 if self.openPlugin:
96                         self.session.open(
97                                 IMDB,
98                                 evt.getEventName()
99                         )
100                 else:
101                         self.close(evt.getEventName())
102
103         def onSelectionChanged(self):
104                 pass
105
106 class IMDB(Screen):
107         if sz_w == 1920:
108                 skin = """
109                 <screen name="IMDBv2" position="center,110" size="1800,930" title="IMDb - Internet Movie Database">
110                 <eLabel backgroundColor="grey" position="10,80" size="1780,1" />
111                 <ePixmap pixmap="Default-FHD/skin_default/buttons/red.svg" position="10,5" size="300,70" />
112                 <ePixmap pixmap="Default-FHD/skin_default/buttons/green.svg" position="310,5" size="300,70" />
113                 <ePixmap pixmap="Default-FHD/skin_default/buttons/yellow.svg" position="610,5" size="300,70" />
114                 <ePixmap pixmap="Default-FHD/skin_default/buttons/blue.svg" position="910,5" size="300,70" />
115                 <widget backgroundColor="#9f1313" font="Regular;30" halign="center" name="key_red" position="10,5" foregroundColor="white" shadowColor="black" shadowOffset="-2,-2" size="300,70" transparent="1" valign="center" zPosition="1" />
116                 <widget backgroundColor="#1f771f" font="Regular;30" halign="center" name="key_green" position="310,5" foregroundColor="white" shadowColor="black" shadowOffset="-2,-2" size="300,70" transparent="1" valign="center" zPosition="1" />
117                 <widget backgroundColor="#a08500" font="Regular;30" halign="center" name="key_yellow" position="610,5" foregroundColor="white" shadowColor="black" shadowOffset="-2,-2" size="300,70" transparent="1" valign="center" zPosition="1" />
118                 <widget backgroundColor="#18188b" font="Regular;30" halign="center" name="key_blue" position="910,5" foregroundColor="white" shadowColor="black" shadowOffset="-2,-2" size="300,70" transparent="1" valign="center" zPosition="1" />
119                 <widget font="Regular;34" halign="right" position="1650,25" render="Label" size="120,40" source="global.CurrentTime">
120                         <convert type="ClockToText">Default</convert>
121                 </widget>
122                 <widget font="Regular;34" halign="right" position="1240,25" render="Label" size="400,40" source="global.CurrentTime" >
123                         <convert type="ClockToText">Date</convert>
124                 </widget>
125                 <widget font="Regular;38" name="titlelabel" position="20,90" size="1780,45" foregroundColor="yellow"/>
126                 <widget enableWrapAround="1" name="menu" position="20,150" scrollbarMode="showOnDemand" size="1760,720" />
127                 <widget font="Regular;28" name="extralabel" position="20,180" size="1760,750" />
128                 <widget font="Regular;30" halign="left" name="ratinglabel" position="370,150" size="1000,40" foregroundColor="yellow"/>
129                 <widget name="starsbg" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/IMDb/starsbar_empty.svg" position="30,150" size="300,30" />
130                 <widget name="stars" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/IMDb/starsbar_filled.svg" position="30,150" size="300,30" transparent="1" />
131                 <widget name="poster" position="30,200" size="300,449" />
132                 <widget font="Regular;30" name="detailslabel" position="370,210" size="750,400" />
133                 <widget font="Regular;30" name="castlabel" position="1180,210" size="600,400" />
134                 <widget font="Regular;30" name="storylinelabel" position="20,660" size="1760,210" />
135                 <widget name="statusbar" halign="right" position="370,885" size="1400,35" font="Regular;30" foregroundColor="#b3b3b9" />
136                 </screen>"""
137         else:
138                 skin = """
139                 <screen name="IMDBv2" position="center,80" size="1200,610" title="IMDb - Internet Movie Database">
140                 <ePixmap pixmap="skin_default/buttons/red.png" position="10,5" size="200,40"/>
141                 <ePixmap pixmap="skin_default/buttons/green.png" position="210,5" size="200,40"/>
142                 <ePixmap pixmap="skin_default/buttons/yellow.png" position="410,5" size="200,40"/>
143                 <ePixmap pixmap="skin_default/buttons/blue.png" position="610,5" size="200,40"/>
144                 <widget name="key_red" position="10,5" size="200,40" zPosition="1" font="Regular;20" halign="center" valign="center" backgroundColor="#9f1313" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-2,-2"/>
145                 <widget name="key_green" position="210,5" size="200,40" zPosition="1" font="Regular;20" halign="center" valign="center" backgroundColor="#1f771f" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-2,-2"/>
146                 <widget name="key_yellow" position="410,5" size="200,40" zPosition="1" font="Regular;20" halign="center" valign="center" backgroundColor="#a08500" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-2,-2"/>
147                 <widget name="key_blue" position="610,5" size="200,40" zPosition="1" font="Regular;20" halign="center" valign="center" backgroundColor="#18188b" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-2,-2"/>
148                 <widget source="global.CurrentTime" render="Label" position="1130,12" size="60,25" font="Regular;22" halign="right">
149                         <convert type="ClockToText">Default</convert>
150                 </widget>
151                 <widget source="global.CurrentTime" render="Label" position="820,12" size="300,25" font="Regular;22" halign="right">
152                         <convert type="ClockToText">Format:%A %d. %B</convert>
153                 </widget>
154                 <eLabel position="10,50" size="1180,1" backgroundColor="grey"/>
155                 <widget name="titlelabel" position="10,55" size="1180,30" valign="center" font="Regular;24" foregroundColor="yellow"/>
156                 <widget name="menu" position="10,100" size="1180,480" zPosition="3" scrollbarMode="showOnDemand" enableWrapAround="1" />
157                 <widget name="extralabel" position="10,100" size="1180,500" font="Regular;22" />
158                 <widget name="ratinglabel" position="230,92" size="800,23" font="Regular;19" foregroundColor="yellow"/>
159                 <widget name="starsbg" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/IMDb/starsbar_empty.svg" position="10,90" size="210,21"/>
160                 <widget name="stars" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/IMDb/starsbar_filled.svg" position="10,90" size="210,21" transparent="1" />
161                 <widget name="poster" position="22,120" size="185,278"/>
162                 <widget name="detailslabel" position="240,130" size="450,270" font="Regular;20" />
163                 <widget name="castlabel" position="720,130" size="470,270" font="Regular;20" />
164                 <widget name="storylinelabel" position="20,410" size="1160,170" font="Regular;20" />
165                 <widget name="statusbar" position="10,580" size="1180,25" halign="right" font="Regular;19" foregroundColor="#b3b3b9" />
166                 </screen>"""
167         def __init__(self, session, eventName, callbackNeeded=False):
168                 Screen.__init__(self, session)
169                 self.skinName = "IMDBv2"
170
171                 for tag in config.plugins.imdb.ignore_tags.getValue().split(','):
172                         eventName = eventName.replace(tag,'')
173
174                 self.eventName = eventName
175
176                 self.callbackNeeded = callbackNeeded
177                 self.callbackData = ""
178                 self.callbackGenre = ""
179
180                 self.fetchurl = None
181
182                 self.dictionary_init()
183
184                 self["poster"] = Pixmap()
185                 self.picload = ePicLoad()
186                 self.picload_conn = self.picload.PictureData.connect(self.paintPosterPixmapCB)
187
188                 self["stars"] = ProgressBar()
189                 self["starsbg"] = Pixmap()
190                 self["stars"].hide()
191                 self["starsbg"].hide()
192                 self.ratingstars = -1
193
194                 self.setTitle(_("IMDb - Internet Movie Database"))
195                 self["titlelabel"] = Label("")
196                 self["titlelcd"] = StaticText("")
197                 self["detailslabel"] = ScrollLabel("")
198                 self["castlabel"] = ScrollLabel("")
199                 self["storylinelabel"] = ScrollLabel("")
200                 self["extralabel"] = ScrollLabel("")
201                 self["statusbar"] = Label("")
202                 self["ratinglabel"] = Label("")
203                 self.resultlist = []
204                 self["menu"] = MenuList(self.resultlist)
205                 self["menu"].hide()
206
207                 self["key_red"] = Button(_("Exit"))
208                 self["key_green"] = Button("")
209                 self["key_yellow"] = Button("")
210                 self["key_blue"] = Button("")
211
212                 # 0 = multiple query selection menu page
213                 # 1 = movie info page
214                 # 2 = extra infos page
215                 self.Page = 0
216
217                 self["actions"] = ActionMap(["OkCancelActions", "ColorActions", "MovieSelectionActions", "DirectionActions"],
218                 {
219                         "ok": self.showDetails,
220                         "cancel": self.exit,
221                         "down": self.pageDown,
222                         "up": self.pageUp,
223                         "right": self.keyRight,
224                         "left": self.keyLeft,
225                         "red": self.exit,
226                         "green": self.showMenu,
227                         "yellow": self.showDetails,
228                         "blue": self.showExtras,
229                         "contextMenu": self.contextMenuPressed,
230                         "showEventInfo": self.showDetails
231                 }, -1)
232
233                 self.getIMDB()
234
235         def title_setText(self, txt):
236                 StaticText.setText(self["titlelcd"], txt)
237                 self["titlelabel"].setText(txt)
238
239         def exit(self):
240                 if fileExists("/tmp/poster.jpg"):
241                         os.remove("/tmp/poster.jpg")
242                 if fileExists("/tmp/imdbquery.html"):
243                         os.remove("/tmp/imdbquery.html")
244                 if fileExists("/tmp/imdbquery2.html"):
245                         os.remove("/tmp/imdbquery2.html")
246                 if self.callbackNeeded:
247                         self.close([self.callbackData, self.callbackGenre])
248                 else:
249                         self.close()
250
251         def dictionary_init(self):
252                 self.generalinfomask = re.compile(
253                 '<h1 itemprop="name" class=".*?>(?P<title>.*?)<.*?/h1>*'
254                 '(?:.*?<div class="originalTitle">(?P<originaltitle>.*?)\s*\((?P<g_originaltitle>original title))*'
255                 '(?:.*?<h4 class="inline">\s*(?P<g_director>Directors?):\s*</h4>.*?<a.*?>(?P<director>.*?)(?:\d+ more|</div>))*'
256                 '(?:.*?<h4 class="inline">\s*(?P<g_creator>Creators?):\s*</h4>.*?<a.*?>(?P<creator>.*?)(?:\d+ more|</div>))*'
257                 '(?:.*?<h4 class="inline">\s*(?P<g_writer>Writers?):\s*</h4>.*?<a.*?>(?P<writer>.*?)(?:\d+ more|</div>))*'
258                 '(?:.*?<h4 class="float-left">(?P<g_seasons>Seasons?)\s*</h4>.*?<a.*?>(?P<seasons>(?:\d+|unknown)?)</a>)*'
259                 '(?:.*?<h4 class="inline">\s*(?P<g_country>Country):\s*</h4>.*?<a.*?>(?P<country>.*?)</a>)*'
260                 '(?:.*?<h4 class="inline">\s*(?P<g_premiere>Release Date).*?</h4>\s+(?P<premiere>.*?)\D+\s+<span)*'
261                 '(?:.*?<h4 class="inline">(?P<g_runtime>Runtime):</h4>\s*(?P<runtime>.*?)</div>)*'
262                 , re.S)
263
264                 self.extrainfomask = re.compile(
265                 '(?:.*?<h4 class="inline">(?P<g_keywords>Plot Keywords):</h4>(?P<keywords>.*?)(?:See All|</div>))*'
266                 '(?:.*?<h4 class="inline">(?P<g_tagline>Taglines):</h4>\s*(?P<tagline>.*?)<)*'
267                 '(?:.*?<h4 class="inline">(?P<g_cert>Certificate):.*?>(?P<cert>\d{1,2})<.*?(?:certification))*'
268                 '(?:.*?<h4>(?P<g_mpaa>Motion Picture Rating).*?</h4>*(?P<mpaa>.*?)</span.*?(?:certification))*'
269                 '(?:.*?<h4 class="inline">(?P<g_language>Language):</h4>\s*(?P<language>.*?)</div>)*'
270                 '(?:.*?<h4 class="inline">(?P<g_locations>Filming Locations):</h4>.*?<a.*?>(?P<locations>.*?)</a>)*'
271                 '(?:.*?<h4 class="inline">(?P<g_company>Production Co):</h4>\s*(?P<company>.*?)(?:See more|</div>))*'
272                 '(?:.*?<h4 class="inline">(?P<g_sound>Sound Mix):</h4>\s*(?P<sound>.*?)</div>)*'
273                 '(?:.*?<h4 class="inline">(?P<g_color>Color):</h4>\s*(?P<color>.*?)</div>)*'
274                 '(?:.*?<h4 class="inline">(?P<g_aspect>Aspect Ratio):</h4>\s*(?P<aspect>.*?)(?:See more</a>|</div>))*'
275                 '(?:.*?<h4>(?P<g_trivia>Trivia)</h4>\s*(?P<trivia>.*?)(?:See more|<span))*'
276                 '(?:.*?<h4>(?P<g_goofs>Goofs)</h4>\s*(?P<goofs>.*?)(?:See more|<span))*'
277                 '(?:.*?<h4>(?P<g_quotes>Quotes)</h4>.*?class="character">(?P<quotes>.*?)(?:See more))*'
278                 '(?:.*?<h4>(?P<g_crazy>Crazy Credits)</h4>\s*(?P<crazy>.*?)(?:See more))*'
279                 '(?:.*?<h4>(?P<g_connections>Connections)</h4>\s*(?P<connections>.*?)(?:See more|<span))*'
280                 '(?:.*?<h2>(?P<g_comments>User Reviews)</h2>.*?href="/user/ur\d+/\?ref_=tt_urv".{0,1}>(?:<span itemprop="author">)(?P<commenter>.*?)</span>.*?<p\sitemprop="reviewBody">(?P<comment>.*?)</p>)*'
281                 , re.S)
282
283                 self.genreblockmask = re.compile('href="/genre/.*?\?ref_=tt_stry_gnr.*?\n.*?\s(?P<genre>.*?)</a>', re.S)
284                 self.ratingmask = re.compile('<span itemprop="ratingValue">(?P<rating>.*?)</.*?itemprop="ratingCount">(?P<ratingcount>.*?)</', re.S)
285                 self.castmask = re.compile('itemprop="actor".*?class="itemprop"\sitemprop="name">(?P<actor>.*?)</span>.*?class="character">(?P<character>.*?)(?:</a>|</div>|\(|<a href="#")', re.S)
286                 self.postermask = re.compile('<div class="poster">.*?<img .*?src=\"(http.*?)\"', re.S)
287                 self.storylinemask = re.compile('(?:.*?<h2>(?P<g_storyline>Storyline)</h2>.*?(?P<storyline>.*?)(?:see-more inline|</p>|Written))*', re.S)
288
289                 self.htmltags = re.compile('<.*?>')
290
291         def resetLabels(self):
292                 self["detailslabel"].setText("")
293                 self["ratinglabel"].setText("")
294                 self["castlabel"].setText("")
295                 self["storylinelabel"].setText("")
296                 self.title_setText("")
297                 self["extralabel"].setText("")
298                 self.ratingstars = -1
299
300         def pageUp(self):
301                 if self.Page == 0:
302                         self["menu"].instance.moveSelection(self["menu"].instance.moveUp)
303                 if self.Page == 1:
304                         self["castlabel"].pageUp()
305                         self["detailslabel"].pageUp()
306                         self["storylinelabel"].pageUp()
307                 if self.Page == 2:
308                         self["extralabel"].pageUp()
309
310         def pageDown(self):
311                 if self.Page == 0:
312                         self["menu"].instance.moveSelection(self["menu"].instance.moveDown)
313                 if self.Page == 1:
314                         self["castlabel"].pageDown()
315                         self["detailslabel"].pageDown()
316                         self["storylinelabel"].pageDown()
317                 if self.Page == 2:
318                         self["extralabel"].pageDown()
319
320         def keyLeft(self):
321                 if self.Page == 0:
322                         self["menu"].pageUp()
323                 if self.Page == 1:
324                         self["castlabel"].pageUp()
325                         self["detailslabel"].pageUp()
326                         self["storylinelabel"].pageUp()
327                 if self.Page == 2:
328                         self["extralabel"].pageUp()
329
330         def keyRight(self):
331                 if self.Page == 0:
332                         self["menu"].pageDown()
333                 if self.Page == 1:
334                         self["castlabel"].pageDown()
335                         self["detailslabel"].pageDown()
336                         self["storylinelabel"].pageDown()
337                 if self.Page == 2:
338                         self["extralabel"].pageDown()
339
340         def showMenu(self):
341                 if ( self.Page is 1 or self.Page is 2 ) and self.resultlist:
342                         self["menu"].show()
343                         self["stars"].hide()
344                         self["starsbg"].hide()
345                         self["ratinglabel"].hide()
346                         self["castlabel"].hide()
347                         self["storylinelabel"].hide()
348                         self["poster"].hide()
349                         self["extralabel"].hide()
350                         self["detailslabel"].hide()
351                         self.title_setText(_("Please select the matching entry"))
352                         self["key_blue"].setText("")
353                         self["key_green"].setText(_("Title Menu"))
354                         self["key_yellow"].setText(_("Details"))
355                         self.Page = 0
356
357         def showDetails(self):
358                 self["ratinglabel"].show()
359                 self["castlabel"].show()
360                 self["detailslabel"].show()
361                 self["storylinelabel"].show()
362
363                 if self.resultlist and self.Page == 0:
364                         link = self["menu"].getCurrent()[1]
365                         title = self["menu"].getCurrent()[0]
366                         self["statusbar"].setText(_("Re-Query IMDb: %s...") % (title))
367                         localfile = "/tmp/imdbquery2.html"
368                         fetchurl = "http://www.imdb.com/title/" + link
369                         print("[IMDB] showDetails() downloading query " + fetchurl + " to " + localfile)
370                         download = downloadWithProgress(fetchurl,localfile,headers=imdb_headers)
371                         download.start().addCallback(self.IMDBquery2).addErrback(self.http_failed)
372                         self.fetchurl = fetchurl
373                         self["menu"].hide()
374                         self.resetLabels()
375                         self.Page = 1
376
377                 if self.Page == 2:
378                         self["extralabel"].hide()
379                         self["poster"].show()
380                         if self.ratingstars > 0:
381                                 self["starsbg"].show()
382                                 self["stars"].show()
383                                 self["stars"].setValue(self.ratingstars)
384
385                         self.Page = 1
386
387         def showExtras(self):
388                 if self.Page == 1:
389                         self["extralabel"].show()
390                         self["detailslabel"].hide()
391                         self["castlabel"].hide()
392                         self["storylinelabel"].hide()
393                         self["poster"].hide()
394                         self["stars"].hide()
395                         self["starsbg"].hide()
396                         self["ratinglabel"].hide()
397                         self.Page = 2
398
399         def contextMenuPressed(self):
400                 list = [
401                         (_("Enter search"), self.openVirtualKeyBoard),
402                         (_("Select from EPG"), self.openChannelSelection),
403                         (_("Setup"), self.setup),
404                 ]
405
406                 if fileExists(resolveFilename(SCOPE_PLUGINS, "Extensions/YTTrailer/plugin.py")):
407                         list.extend((
408                                 (_("Play Trailer"), self.openYttrailer),
409                                 (_("Search Trailer"), self.searchYttrailer),
410                         ))
411
412                 self.session.openWithCallback(
413                         self.menuCallback,
414                         ChoiceBox,
415                         title=_("IMDb Menu"),
416                         list = list,
417                 )
418
419         def menuCallback(self, ret = None):
420                 ret and ret[1]()
421
422         def openYttrailer(self):
423                 try:
424                         from Plugins.Extensions.YTTrailer.plugin import YTTrailer, baseEPGSelection__init__
425                 except ImportError as ie:
426                         pass
427                 if baseEPGSelection__init__ is None:
428                         return
429
430                 ytTrailer = YTTrailer(self.session)
431                 ytTrailer.showTrailer(self.eventName)
432
433         def searchYttrailer(self):
434                 try:
435                         from Plugins.Extensions.YTTrailer.plugin import YTTrailerList, baseEPGSelection__init__
436                 except ImportError as ie:
437                         pass
438                 if baseEPGSelection__init__ is None:
439                         return
440
441                 self.session.open(YTTrailerList, self.eventName)
442
443         def openVirtualKeyBoard(self):
444                 self.session.openWithCallback(
445                         self.gotSearchString,
446                         VirtualKeyBoard,
447                         title = _("Enter text to search for"),
448                         text = self.eventName
449                 )
450
451         def openChannelSelection(self):
452                 self.session.openWithCallback(
453                         self.gotSearchString,
454                         IMDBChannelSelection
455                 )
456
457         def gotSearchString(self, ret = None):
458                 if ret:
459                         self.eventName = ret
460                         self.Page = 0
461                         self.resultlist = []
462                         self["menu"].hide()
463                         self["ratinglabel"].show()
464                         self["castlabel"].show()
465                         self["storylinelabel"].show()
466                         self["detailslabel"].show()
467                         self["poster"].hide()
468                         self["stars"].hide()
469                         self["starsbg"].hide()
470                         self.getIMDB(search=True)
471
472         def getIMDB(self, search=False):
473                 global imdb_headers
474                 if config.plugins.imdb.language.value:
475                         imdb_headers = {'Accept-Language': config.plugins.imdb.language.value}
476                 else:
477                         imdb_headers = {}
478                 self.resetLabels()
479                 if not self.eventName:
480                         s = self.session.nav.getCurrentService()
481                         info = s and s.info()
482                         event = info and info.getEvent(0) # 0 = now, 1 = next
483                         if event:
484                                 self.eventName = event.getEventName()
485                         else:
486                                 self.eventName = self.session.nav.getCurrentlyPlayingServiceReference().toString()
487                                 self.eventName = self.eventName.split('/')
488                                 self.eventName = self.eventName[-1]
489                                 self.eventName = self.eventName.replace('.',' ')
490                                 self.eventName = self.eventName.split('-')
491                                 self.eventName = self.eventName[0]
492                                 if self.eventName.endswith(' '):
493                                         self.eventName = self.eventName[:-1]
494
495                 if self.eventName:
496                         self["statusbar"].setText(_("Query IMDb: %s") % (self.eventName))
497                         localfile = "/tmp/imdbquery.html"
498                         fetchurl = "http://www.imdb.com/find?ref_=nv_sr_fn&q=" + quoteEventName(self.eventName) + "&s=all"
499                         print("[IMDB] getIMDB() Downloading Query " + fetchurl + " to " + localfile)
500                         download = downloadWithProgress(fetchurl,localfile,headers=imdb_headers)
501                         download.start().addCallback(self.IMDBquery).addErrback(self.http_failed)
502
503                 else:
504                         self["statusbar"].setText(_("Couldn't get Eventname"))
505
506         def cleanhtml(self, in_html):
507                 in_html = (re.subn(r'<(script).*?</\1>(?s)', '', in_html)[0])
508                 in_html = (re.subn(r'<(style).*?</\1>(?s)', '', in_html)[0])
509                 return in_html
510
511         def IMDBquery(self,string):
512                 self["statusbar"].setText(_("IMDb Download completed"))
513
514                 self.inhtml = open("/tmp/imdbquery.html", "r").read()
515                 self.inhtml = self.cleanhtml(self.inhtml)
516
517                 self.generalinfos = self.generalinfomask.search(self.inhtml)
518
519                 if self.generalinfos:
520                         self.IMDBparse()
521                 else:
522                         if not re.search('class="findHeader">No results found for', self.inhtml):
523                                 pos = self.inhtml.find("<table class=\"findList\">")
524                                 pos2 = self.inhtml.find("</table>",pos)
525                                 findlist = self.inhtml[pos:pos2]
526                                 searchresultmask = re.compile('<tr class=\"findResult (?:odd|even)\">.*?<td class=\"result_text\"> <a href=\"/title/(tt\d{7,7})/.*?\"\s?>(.*?)</a>.*?</td>', re.S)
527                                 searchresults = searchresultmask.finditer(findlist)
528                                 self.resultlist = [(self.htmltags.sub('',x.group(2)), x.group(1)) for x in searchresults]
529                                 Len = len(self.resultlist)
530                                 self["menu"].l.setList(self.resultlist)
531                                 if Len == 1:
532                                         self["key_green"].setText("")
533                                         self["statusbar"].setText(_("Re-Query IMDb: %s...") % (self.resultlist[0][0],))
534                                         self.eventName = self.resultlist[0][1]
535                                         localfile = "/tmp/imdbquery.html"
536                                         fetchurl = "http://www.imdb.com/find?ref_=nv_sr_fn&q=" + quoteEventName(self.eventName) + "&s=all"
537                                         download = downloadWithProgress(fetchurl,localfile,headers=imdb_headers)
538                                         download.start().addCallback(self.IMDBquery).addErrback(self.http_failed)
539                                 elif Len > 1:
540                                         self.Page = 1
541                                         self.showMenu()
542                                 else:
543                                         self["statusbar"].setText(_("No IMDb match.") + ' ' + self.eventName)
544                         else:
545                                 splitpos = self.eventName.find('(')
546                                 if splitpos > 0:
547                                         self.eventName = self.eventName[:splitpos-1]
548                                         self["statusbar"].setText(_("Re-Query IMDb: %s...") % (self.eventName))
549                                         localfile = "/tmp/imdbquery.html"
550                                         fetchurl = "http://www.imdb.com/find?ref_=nv_sr_fn&q=" + quoteEventName(self.eventName) + "&s=all"
551                                         download = downloadWithProgress(fetchurl,localfile,headers=imdb_headers)
552                                         download.start().addCallback(self.IMDBquery).addErrback(self.http_failed)
553                                 else:
554                                         splitpos = self.eventName.find('-')
555                                         if splitpos > 0:
556                                                 self.eventName = self.eventName[:splitpos-1].strip()
557                                                 self["statusbar"].setText(_("Re-Query IMDb: %s...") % (self.eventName))
558                                                 localfile = "/tmp/imdbquery.html"
559                                                 fetchurl = "http://www.imdb.com/find?ref_=nv_sr_fn&q=" + quoteEventName(self.eventName) + "&s=all"
560                                                 download = downloadWithProgress(fetchurl,localfile,headers=imdb_headers)
561                                                 download.start().addCallback(self.IMDBquery).addErrback(self.http_failed)
562                                         else:
563                                                 self["statusbar"].setText(_("IMDb query failed!"))
564
565         def http_failed(self, failure_instance=None, error_message=""):
566                 text = _("IMDb Download failed")
567                 if error_message == "" and failure_instance is not None:
568                         error_message = failure_instance.getErrorMessage()
569                         text += ": " + error_message
570                 print("[IMDB] ",text)
571                 self["statusbar"].setText(text)
572
573         def IMDBquery2(self,string):
574                 self["statusbar"].setText(_("IMDb Re-Download completed"))
575                 self.inhtml = open("/tmp/imdbquery2.html", "r").read()
576                 self.inhtml = self.cleanhtml(self.inhtml)
577                 self.generalinfos = self.generalinfomask.search(self.inhtml)
578                 self.IMDBparse()
579
580         def IMDBparse(self):
581                 self.Page = 1
582                 Detailstext = _("No details found.")
583                 if self.generalinfos:
584                         self["key_yellow"].setText(_("Details"))
585                         self["statusbar"].setText(_("IMDb Details parsed"))
586                         Titeltext = self.generalinfos.group("title").replace('&nbsp;',' ').strip()
587                         if len(Titeltext) > 57:
588                                 Titeltext = Titeltext[0:54] + "..."
589                         self.title_setText(Titeltext)
590
591                         Detailstext = ""
592                         stripmask = re.compile('\s{2,}', re.S)
593
594                         for category in ("originaltitle", "premiere", "country", "runtime", "seasons"):
595                                 if self.generalinfos.group(category):
596                                         Detailstext += self.generalinfos.group('g_'+category).title() + ": " + stripmask.sub(' ', self.htmltags.sub('', self.generalinfos.group(category).replace('\n',' ').replace('<br>', '\n').replace('<br />','\n'))).replace(' | ',', ').strip() + "\n"
597
598                         genreblock = self.genreblockmask.findall(self.inhtml)
599                         if genreblock:
600                                 s = ''
601                                 if len(genreblock) > 1: s = 's'
602                                 Detailstext += "Genre%s: " % s
603                                 for x in genreblock:
604                                         genres = self.htmltags.sub('', x)
605                                         Detailstext += genres + ", "
606                                 if Detailstext[-2:] == ", ":
607                                         Detailstext = Detailstext[:-2]
608                                 Detailstext += "\n"
609
610                         for category in ("director", "creator", "writer"):
611                                 if self.generalinfos.group(category):
612                                         striplink1 = re.compile('(<a href="/name/nm\d+.{0,1}\?ref_=tt_ov_..".{0,1}itemprop=\'url\'>)', re.S)
613                                         striplink2 = re.compile('(<a href="fullcredits\?ref_=tt_ov_..#.*?">)', re.S)
614                                         Detailstext += self.generalinfos.group('g_'+category) + ": " + striplink2.sub('', striplink1.sub('', stripmask.sub(' ', self.htmltags.sub('', self.generalinfos.group(category)).replace('\n','').replace('|','')))) + "\n"
615
616                         rating = self.ratingmask.search(self.inhtml)
617                         Ratingtext = _("no user rating yet")
618                         if rating:
619                                 ratingval = rating.group("rating")
620                                 if ratingval != '<span id="voteuser"></span>':
621                                         count = ''
622                                         if rating.group("ratingcount"): count = ' (' + rating.group("ratingcount").replace(',','.') + ' ' + _("votes") +')'
623                                         Ratingtext = _("User Rating") + ": " + ratingval + "/10" + count
624                                         self.ratingstars = int(10*round(float(ratingval.replace(',','.')),1))
625                                         self["stars"].show()
626                                         self["stars"].setValue(self.ratingstars)
627                                         self["starsbg"].show()
628                         self["ratinglabel"].setText(str(Ratingtext))
629
630                         castresult = self.castmask.finditer(self.inhtml)
631                         if castresult:
632                                 Casttext = ""
633                                 for x in castresult:
634                                         Casttext += "\n" + self.htmltags.sub('', x.group('actor'))
635                                         if x.group('character'):
636                                                 Casttext += " as " + stripmask.sub(' ', self.htmltags.sub('', x.group('character').replace('/ ...','').replace('\n','').replace('&nbsp;',''))).strip()
637                                 if Casttext:
638                                         Casttext = "Cast: " + Casttext
639                                 else:
640                                         Casttext = _("No cast list found in the database.")
641                                 self["castlabel"].setText(str(Casttext))
642
643                         storylineresult = self.storylinemask.search(self.inhtml)
644                         if storylineresult:
645                                 Storyline = ""
646                                 if storylineresult.group("g_storyline"):
647                                         Storyline = storylineresult.group('g_storyline') + ":\n" + re.sub('\s{5,30}',', ', self.htmltags.sub('', storylineresult.group('storyline').replace('\n','').replace('<br>', '\n').replace('<br />','\n').replace('&nbsp;','').replace('&quot;','"').replace('<span class="','')).strip())
648
649                                 if Storyline == storylineresult.group('g_storyline') + ":\n":
650                                         self["storylinelabel"].hide()
651                                 else:
652                                         self["storylinelabel"].setText(str(Storyline))
653
654                         posterurl = self.postermask.search(self.inhtml)
655                         if posterurl and posterurl.group(1).find("jpg") > 0:
656                                 posterurl = posterurl.group(1)
657                                 self["statusbar"].setText(_("Downloading Movie Poster: %s...") % (posterurl))
658                                 localfile = "/tmp/poster.jpg"
659                                 print("[IMDB] downloading poster " + posterurl + " to " + localfile)
660                                 download = downloadWithProgress(posterurl,localfile,headers=imdb_headers)
661                                 download.start().addCallback(self.IMDBPoster).addErrback(self.http_failed)
662                         else:
663                                 self.IMDBPoster("kein Poster")
664
665                         extrainfos = self.extrainfomask.search(self.inhtml)
666                         if extrainfos:
667                                 Extratext = ""
668                                 for category in ("tagline","keywords","cert","mpaa","language","color","aspect","company","sound","locations","trivia","goofs","quotes","crazy","connections"):
669                                         if extrainfos.group('g_'+category):
670                                                 Extratext += extrainfos.group('g_'+category) + ":\n" + re.sub('\s{5,30}',', ', self.htmltags.sub('', extrainfos.group(category).replace('\n','').replace('<br>', '\n').replace('<br />','\n').replace('&view=simple&sort=alpha&ref_=tt_stry_pl" >',' ').replace('&nbsp;','').replace('&quot;','"').replace('|','').replace(' : ',':').replace(',        ',' / ')).strip()) + "\n\n"
671                                 if extrainfos.group("g_comments"):
672                                         stripmask = re.compile('\s{2,}', re.S)
673                                         Extratext += extrainfos.group("g_comments") + " [" + stripmask.sub(' ', self.htmltags.sub('', extrainfos.group("commenter"))) + "]:\n" + transHTML(self.htmltags.sub('',extrainfos.group("comment").replace("\n",' '))).strip()
674
675                                 self["extralabel"].setText(str(Extratext))
676                                 self["extralabel"].hide()
677                                 self["key_blue"].setText(_("Extra Info"))
678                         else:
679                                 self["key_blue"].setText("")
680
681                 self["detailslabel"].setText(str(Detailstext))
682
683         def IMDBPoster(self,string):
684                 self["statusbar"].setText(_("IMDb Details parsed"))
685                 if not string:
686                         filename = "/tmp/poster.jpg"
687                 else:
688                         filename = resolveFilename(SCOPE_PLUGINS, "Extensions/IMDb/no_poster.png")
689                 sc = AVSwitch().getFramebufferScale()
690                 self.picload.setPara((self["poster"].instance.size().width(), self["poster"].instance.size().height(), sc[0], sc[1], False, 1, "#00000000"))
691                 self.picload.startDecode(filename)
692
693         def paintPosterPixmapCB(self, picInfo=None):
694                 ptr = self.picload.getData()
695                 if ptr != None:
696                         self["poster"].instance.setPixmap(ptr)
697                         self["poster"].show()
698
699         def setup(self):
700                 self.session.open(IMDbSetup)
701
702         def createSummary(self):
703                 return IMDbLCDScreenV2
704
705 class IMDbLCDScreenV2(Screen):
706         skin = (
707         """<screen title="IMDB Plugin" position="0,0" size="132,64" id="1">
708                 <widget name="headline" font="Display;22" halign="center" position="1,3" size="130,22"/>
709                 <widget source="parent.titlelcd" render="Label" font="Display;16" halign="center" valign="center" position="1,28" size="130,34"/>
710         </screen>""",
711         """<screen title="IMDB Plugin" position="0,0" size="96,64" id="2">
712                 <widget name="headline" font="Display;19" halign="center" position="0,2" size="96,20"/>
713                 <widget source="parent.titlelcd" render="Label" font="Display;15" halign="center" valign="center" position="0,28" size="96,34"/>
714         </screen>""",
715         """<screen title="IMDB Plugin" position="0,0" size="400,240" id="3">
716                 <ePixmap position="0,0" size="400,240" pixmap="skin_default/display_bg.png" zPosition="-1"/>
717                 <widget name="headline" font="Display;60" halign="center" position="0,5" size="400,60" transparent="1"/>
718                 <eLabel backgroundColor="yellow" position="0,75" size="400,2" />
719                 <widget source="parent.titlelcd" render="Label" font="Display;45" halign="center" valign="center" position="0,85" size="400,135" transparent="1"/>
720         </screen>""")
721
722         def __init__(self, session, parent):
723                 Screen.__init__(self, session, parent)
724                 self["headline"] = Label(_("IMDb Plugin"))
725
726 class IMDbSetup(Screen, ConfigListScreen):
727         def __init__(self, session):
728                 Screen.__init__(self, session)
729                 self.skinName = "Setup"
730
731                 self.setup_title = _("IMDb Setup")
732                 self.onChangedEntry = []
733
734                 self["key_green"] = StaticText(_("OK"))
735                 self["key_red"] = StaticText(_("Cancel"))
736                 self["description"] = Label("")
737
738                 self["actions"] = ActionMap(["SetupActions"],
739                         {
740                                 "cancel": self.keyCancel,
741                                 "save": self.keySave,
742                         }, -2)
743
744                 self.list = []
745                 ConfigListScreen.__init__(self, self.list, session = self.session, on_change = self.changedEntry)
746                 self.createSetup()
747                 self.changedEntry()
748                 self.onLayoutFinish.append(self.layoutFinished)
749
750         def createSetup(self):
751                 self.list = []
752                 self.list.append(getConfigListEntry(_("IMDb query language"), config.plugins.imdb.language))
753                 self.list.append(getConfigListEntry(_("Show in plugin browser"), config.plugins.imdb.showinplugins))
754                 self.list.append(getConfigListEntry(_("Words / phrases to ignore (comma separated)"), config.plugins.imdb.ignore_tags))
755                 self["config"].list = self.list
756                 self["config"].l.setList(self.list)
757
758         def layoutFinished(self):
759                 self.setTitle(_(self.setup_title))
760
761         def changedEntry(self):
762                 self.item = self["config"].getCurrent()
763                 for x in self.onChangedEntry:
764                         x()
765                 try:
766                         if isinstance(self["config"].getCurrent()[1], ConfigYesNo) or isinstance(self["config"].getCurrent()[1], ConfigSelection):
767                                 self.createSetup()
768                 except:
769                         pass
770
771         def getCurrentEntry(self):
772                 return self["config"].getCurrent() and self["config"].getCurrent()[0] or ""
773
774         def getCurrentValue(self):
775                 return self["config"].getCurrent() and str(self["config"].getCurrent()[1].getText()) or ""
776
777         def getCurrentDescription(self):
778                 return self["config"].getCurrent() and len(self["config"].getCurrent()) > 2 and self["config"].getCurrent()[2] or ""
779
780         def createSummary(self):
781                 from Screens.Setup import SetupSummary
782                 return SetupSummary
783
784         def keySave(self):
785                 self.saveAll()
786                 global imdb_headers
787                 if config.plugins.imdb.language.value:
788                         imdb_headers = {'Accept-Language': config.plugins.imdb.language.value}
789                 else:
790                         imdb_headers = {}
791                 if not config.plugins.imdb.showinplugins.value:
792                         for plugin in plugins.getPlugins(PluginDescriptor.WHERE_PLUGINMENU):
793                                 if plugin.name == _("IMDb Details"):
794                                         plugins.removePlugin(plugin)
795
796                 plugins.readPluginList(resolveFilename(SCOPE_PLUGINS))
797                 self.close()
798
799 def eventinfo(session, eventName="", **kwargs):
800         if not eventName:
801                 s = session.nav.getCurrentService()
802                 if s:
803                         info = s.info()
804                         event = info.getEvent(0) # 0 = now, 1 = next
805                         eventName = event and event.getEventName() or ''
806         session.open(IMDB, eventName)
807
808 def main(session, eventName="", **kwargs):
809         session.open(IMDB, eventName)
810
811 pluginlist = PluginDescriptor(name=_("IMDb Details"), description=_("Query details from the Internet Movie Database"), icon="imdb.png", where=PluginDescriptor.WHERE_PLUGINMENU, fnc=main, needsRestart=False)
812
813 def Plugins(**kwargs):
814         l = [PluginDescriptor(name=_("IMDb Details") + "...",
815                         description=_("Query details from the Internet Movie Database"),
816                         where=PluginDescriptor.WHERE_EVENTINFO,
817                         fnc=eventinfo,
818                         needsRestart=False,
819                         ),
820                 ]
821
822         if config.plugins.imdb.showinplugins.value:
823                 l.append(pluginlist)
824
825         return l