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