imdb: fix for titles without rating on new english site
[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 class="rating-rating">(?P<rating>.*?)<span>/10', re.DOTALL)
219                         self.castmask = re.compile('<td class="name">.*?>(.*?)</a>.*?<td class="character">.*?<div>(.*?)</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>)?</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                 htmlentitynumbermask = re.compile('(&#(\d{1,5}?);)')
389                 htmlentityhexmask = re.compile('(&#x([0-9A-Fa-f]{2,2}?);)')
390                 htmlentitynamemask = re.compile('(&([^#]\D{1,5}?);)')
391                 entitydict = {}
392                 entityhexdict = {}
393                 entities = htmlentitynamemask.finditer(in_html)
394
395                 for x in entities:
396                         entitydict[x.group(1)] = x.group(2)
397                 for key, name in entitydict.items():
398                         entitydict[key] = htmlentitydefs.name2codepoint[name]
399                 entities = htmlentityhexmask.finditer(in_html)
400
401                 for x in entities:
402                         entityhexdict[x.group(1)] = x.group(2)
403
404                 for key, name in entityhexdict.items():
405                         entitydict[key] = "%d" % int(key[3:5], 16)
406                         print "key:", key, "before:", name, "after:", entitydict[key]
407                 
408                 entities = htmlentitynumbermask.finditer(in_html)
409                 for x in entities:
410                         entitydict[x.group(1)] = x.group(2)
411                 for key, codepoint in entitydict.items():
412                         in_html = in_html.replace(key, (unichr(int(codepoint)).encode('latin-1', 'ignore')))
413                 self.inhtml = in_html.decode('latin-1').encode('utf8')
414
415         def IMDBquery(self,string):
416                 print "[IMDBquery]"
417                 self["statusbar"].setText(_("IMDb Download completed"))
418
419                 self.html2utf8(open("/tmp/imdbquery.html", "r").read())
420
421                 self.generalinfos = self.generalinfomask.search(self.inhtml)
422
423                 if self.generalinfos:
424                         self.IMDBparse()
425                 else:
426                         if re.search("<title>(?:IMDb.{0,9}Search|IMDb Titelsuche)</title>", self.inhtml):
427                                 searchresultmask = re.compile("<tr> <td.*?img src.*?>.*?<a href=\".*?/title/(tt\d{7,7})/\".*?>(.*?)</td>", re.DOTALL)
428                                 searchresults = searchresultmask.finditer(self.inhtml)
429                                 self.resultlist = [(self.htmltags.sub('',x.group(2)), x.group(1)) for x in searchresults]
430                                 self["menu"].l.setList(self.resultlist)
431                                 if len(self.resultlist) > 1:
432                                         self.Page = 1
433                                         self.showMenu()
434                                 else:
435                                         self["detailslabel"].setText(_("No IMDb match."))
436                                         self["statusbar"].setText(_("No IMDb match."))
437                         else:
438                                 splitpos = self.eventName.find('(')
439                                 if splitpos > 0 and self.eventName.endswith(')'):
440                                         self.eventName = self.eventName[splitpos+1:-1]
441                                         self["statusbar"].setText(_("Re-Query IMDb: %s...") % (self.eventName))
442                                         event_quoted = urllib.quote(self.eventName.decode('utf8').encode('latin-1','ignore'))
443                                         localfile = "/tmp/imdbquery.html"
444                                         fetchurl = "http://" + self.IMDBlanguage + "imdb.com/find?q=" + event_quoted + "&s=tt&site=aka"
445                                         print "[IMDB] Downloading Query " + fetchurl + " to " + localfile
446                                         downloadPage(fetchurl,localfile).addCallback(self.IMDBquery).addErrback(self.fetchFailed)
447                                 else:
448                                         self["detailslabel"].setText(_("IMDb query failed!"))
449
450         def IMDBquery2(self,string):
451                 self["statusbar"].setText(_("IMDb Re-Download completed"))
452                 self.html2utf8(open("/tmp/imdbquery2.html", "r").read())
453                 self.generalinfos = self.generalinfomask.search(self.inhtml)
454                 self.IMDBparse()
455
456         def IMDBparse(self):
457                 print "[IMDBparse]"
458                 self.Page = 1
459                 Detailstext = _("No details found.")
460                 if self.generalinfos:
461                         self["key_yellow"].setText(_("Details"))
462                         self["statusbar"].setText(_("IMDb Details parsed"))
463                         Titeltext = self.generalinfos.group("title")
464                         if len(Titeltext) > 57:
465                                 Titeltext = Titeltext[0:54] + "..."
466                         self["titellabel"].setText(Titeltext)
467
468                         Detailstext = ""
469
470                         genreblock = self.genreblockmask.findall(self.inhtml)
471                         if genreblock:
472                                 genres = self.htmltags.sub('', genreblock[0])
473                                 if genres:
474                                         Detailstext += "Genre: "
475                                         Detailstext += genres
476                                         self.callbackGenre = genres
477
478                         for category in ("director", "creator", "writer", "seasons"):
479                                 if self.generalinfos.group(category):
480                                         Detailstext += "\n" + self.generalinfos.group('g_'+category) + ": " + self.generalinfos.group(category)
481
482                         for category in ("premiere", "country", "alternativ"):
483                                 if self.generalinfos.group(category):
484                                         Detailstext += "\n" + self.generalinfos.group('g_'+category) + ": " + self.htmltags.sub('', self.generalinfos.group(category).replace('\n',' ').replace("<br>", '\n').replace("<br />",'\n').replace("  ",' '))
485
486                         rating = self.ratingmask.search(self.inhtml)
487                         Ratingtext = _("no user rating yet")
488                         if rating:
489                                 rating = rating.group("rating")
490                                 if rating != "-":
491                                         Ratingtext = _("User Rating") + ": " + rating + " / 10"
492                                         self.ratingstars = int(10*round(float(rating.replace(',','.')),1))
493                                         self["stars"].show()
494                                         self["stars"].setValue(self.ratingstars)
495                                         self["starsbg"].show()
496                         self["ratinglabel"].setText(Ratingtext)
497
498                         castresult = self.castmask.finditer(self.inhtml)
499                         if castresult:
500                                 Casttext = ""
501                                 for x in castresult:
502                                         Casttext += "\n" + self.htmltags.sub('', x.group(1))
503                                         if x.group(2):
504                                                 Casttext += _(" as ") + self.htmltags.sub('', x.group(2).replace('/ ...','')).replace('\n', ' ').replace("  ", ' ').strip()
505                                 if Casttext is not "":
506                                         Casttext = _("Cast: ") + Casttext
507                                 else:
508                                         Casttext = _("No cast list found in the database.")
509                                 self["castlabel"].setText(Casttext)
510
511                         posterurl = self.postermask.search(self.inhtml)
512                         if posterurl and posterurl.group(1).find("jpg") > 0:
513                                 posterurl = posterurl.group(1)
514                                 self["statusbar"].setText(_("Downloading Movie Poster: %s...") % (posterurl))
515                                 localfile = "/tmp/poster.jpg"
516                                 print "[IMDB] downloading poster " + posterurl + " to " + localfile
517                                 downloadPage(posterurl,localfile).addCallback(self.IMDBPoster).addErrback(self.fetchFailed)
518                         else:
519                                 self.IMDBPoster("kein Poster")
520                         extrainfos = self.extrainfomask.search(self.inhtml)
521
522                         if extrainfos:
523                                 Extratext = "Extra Info\n"
524
525                                 for category in ("tagline","outline","synopsis","keywords","awards","runtime","language","color","aspect","sound","cert","locations","company","trivia","goofs","quotes","connections"):
526                                         if extrainfos.group('g_'+category):
527                                                 Extratext += extrainfos.group('g_'+category) + ": " + self.htmltags.sub('',extrainfos.group(category).replace("\n",'').replace("<br>", '\n').replace("<br />",'\n')) + "\n"
528                                 if extrainfos.group("g_comments"):
529                                         stripmask = re.compile('\s{2,}', re.DOTALL)
530                                         Extratext += extrainfos.group("g_comments") + " [" + stripmask.sub(' ', self.htmltags.sub('',extrainfos.group("commenter"))) + "]: " + self.htmltags.sub('',extrainfos.group("comment").replace("\n",' ')) + "\n"
531
532                                 self["extralabel"].setText(Extratext)
533                                 self["extralabel"].hide()
534                                 self["key_blue"].setText(_("Extra Info"))
535
536                 self["detailslabel"].setText(Detailstext)
537                 self.callbackData = Detailstext
538
539         def IMDBPoster(self,string):
540                 self["statusbar"].setText(_("IMDb Details parsed"))
541                 if not string:
542                         filename = "/tmp/poster.jpg"
543                 else:
544                         filename = resolveFilename(SCOPE_PLUGINS, "Extensions/IMDb/no_poster.png")
545                 sc = AVSwitch().getFramebufferScale()
546                 self.picload.setPara((self["poster"].instance.size().width(), self["poster"].instance.size().height(), sc[0], sc[1], False, 1, "#00000000"))
547                 self.picload.startDecode(filename)
548
549         def paintPosterPixmapCB(self, picInfo=None):
550                 ptr = self.picload.getData()
551                 if ptr != None:
552                         self["poster"].instance.setPixmap(ptr.__deref__())
553                         self["poster"].show()
554
555         def createSummary(self):
556                 return IMDbLCDScreen
557
558 class IMDbLCDScreen(Screen):
559         skin = """
560         <screen position="0,0" size="132,64" title="IMDB Plugin">
561                 <widget name="headline" position="4,0" size="128,22" font="Regular;20"/>
562                 <widget source="session.Event_Now" render="Label" position="6,26" size="120,34" font="Regular;14" >
563                         <convert type="EventName">Name</convert>
564                 </widget>
565         </screen>"""
566
567         def __init__(self, session, parent):
568                 Screen.__init__(self, session)
569                 self["headline"] = Label(_("IMDb Plugin"))
570
571 def eventinfo(session, servicelist, **kwargs):
572         ref = session.nav.getCurrentlyPlayingServiceReference()
573         session.open(IMDBEPGSelection, ref)
574
575 def main(session, eventName="", **kwargs):
576         session.open(IMDB, eventName)
577
578 def Plugins(**kwargs):
579         try:
580                 return [PluginDescriptor(name="IMDb Details",
581                                 description=_("Query details from the Internet Movie Database"),
582                                 icon="imdb.png",
583                                 where = PluginDescriptor.WHERE_PLUGINMENU,
584                                 fnc = main),
585                                 PluginDescriptor(name="IMDb Details",
586                                 description=_("Query details from the Internet Movie Database"),
587                                 where = PluginDescriptor.WHERE_EVENTINFO,
588                                 fnc = eventinfo)
589                                 ]
590         except AttributeError:
591                 wherelist = [PluginDescriptor.WHERE_EXTENSIONSMENU, PluginDescriptor.WHERE_PLUGINMENU]
592                 return PluginDescriptor(name="IMDb Details",
593                                 description=_("Query details from the Internet Movie Database"),
594                                 icon="imdb.png",
595                                 where = wherelist,
596                                 fnc=main)