updated IMDB Plugin
[enigma2-plugins.git] / imdb / src / plugin.py
1 # -*- coding: UTF-8 -*-
2 # for localized messages
3 from . import _
4
5 from Plugins.Plugin import PluginDescriptor
6 from Tools.Downloader import downloadWithProgress
7 from enigma import ePicLoad, eServiceReference
8 from Screens.Screen import Screen
9 from Screens.EpgSelection import EPGSelection
10 from Screens.ChannelSelection import SimpleChannelSelection
11 from Screens.ChoiceBox import ChoiceBox
12 from Screens.VirtualKeyBoard import VirtualKeyBoard
13 from Components.ActionMap import ActionMap
14 from Components.Pixmap import Pixmap
15 from Components.Label import Label
16 from Components.ScrollLabel import ScrollLabel
17 from Components.Button import Button
18 from Components.AVSwitch import AVSwitch
19 from Components.MenuList import MenuList
20 from Components.Language import language
21 from Components.ProgressBar import ProgressBar
22 from Components.Sources.StaticText import StaticText
23 from Components.Sources.Boolean import Boolean
24 from Tools.Directories import fileExists, resolveFilename, SCOPE_PLUGINS, SCOPE_SKIN_IMAGE
25 import os, re
26 try:
27         import htmlentitydefs
28         from urllib import quote_plus
29         iteritems = lambda d: d.iteritems()
30 except ImportError as ie:
31         from html import entities as htmlentitydefs
32         from urllib.parse import quote_plus
33         iteritems = lambda d: d.items()
34         unichr = chr
35 import os, gettext
36
37 # Configuration
38 from Components.config import config, getConfigListEntry, ConfigSubsection, ConfigYesNo, ConfigText
39 from Components.ConfigList import ConfigListScreen
40 from Components.PluginComponent import plugins
41 from Tools.Directories import resolveFilename, SCOPE_PLUGINS
42
43 from HTMLParser import HTMLParser
44
45 def transHTML(text):
46         h = HTMLParser()
47         return h.unescape(text)
48
49 config.plugins.imdb = ConfigSubsection()
50 config.plugins.imdb.showinplugins = ConfigYesNo(default = True)
51 config.plugins.imdb.force_english = ConfigYesNo(default=False)
52 config.plugins.imdb.ignore_tags = ConfigText(visible_width = 50, fixed_size = False)
53
54 def quoteEventName(eventName, safe="/()" + ''.join(map(chr,range(192,255)))):
55         # BBC uses '\x86' markers in program names, remove them
56         text = eventName.decode('utf8').replace(u'\x86', u'').replace(u'\x87', u'').encode('utf8')
57         # IMDb doesn't seem to like urlencoded characters at all, hence the big "safe" list
58         return quote_plus(text, safe=safe)
59
60 class IMDBChannelSelection(SimpleChannelSelection):
61         def __init__(self, session):
62                 SimpleChannelSelection.__init__(self, session, _("Channel Selection"))
63                 self.skinName = "SimpleChannelSelection"
64
65                 self["ChannelSelectEPGActions"] = ActionMap(["ChannelSelectEPGActions"],
66                         {
67                                 "showEPGList": self.channelSelected
68                         }
69                 )
70
71         def channelSelected(self):
72                 ref = self.getCurrentSelection()
73                 if (ref.flags & 7) == 7:
74                         self.enterPath(ref)
75                 elif not (ref.flags & eServiceReference.isMarker):
76                         self.session.openWithCallback(
77                                 self.epgClosed,
78                                 IMDBEPGSelection,
79                                 ref,
80                                 openPlugin = False
81                         )
82
83         def epgClosed(self, ret = None):
84                 if ret:
85                         self.close(ret)
86
87 class IMDBEPGSelection(EPGSelection):
88         def __init__(self, session, ref, openPlugin = True):
89                 EPGSelection.__init__(self, session, ref)
90                 self.skinName = "EPGSelection"
91                 self["key_green"].setText(_("Lookup"))
92                 self.openPlugin = openPlugin
93
94         def infoKeyPressed(self):
95                 self.timerAdd()
96
97         def timerAdd(self):
98                 cur = self["list"].getCurrent()
99                 evt = cur[0]
100                 sref = cur[1]
101                 if not evt:
102                         return
103
104                 if self.openPlugin:
105                         self.session.open(
106                                 IMDB,
107                                 evt.getEventName()
108                         )
109                 else:
110                         self.close(evt.getEventName())
111
112         def onSelectionChanged(self):
113                 pass
114
115 class IMDB(Screen):
116         skin = """
117                 <screen name="IMDB" position="center,center" size="600,420" title="Internet Movie Database Details Plugin" >
118                         <ePixmap pixmap="skin_default/buttons/red.png" position="0,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
119                         <ePixmap pixmap="skin_default/buttons/green.png" position="140,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
120                         <ePixmap pixmap="skin_default/buttons/yellow.png" position="280,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
121                         <ePixmap pixmap="skin_default/buttons/blue.png" position="420,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
122                         <ePixmap pixmap="skin_default/buttons/key_menu.png" position="565,5" zPosition="0" size="35,25" alphatest="on" />
123                         <widget name="key_red" position="0,0" zPosition="1" size="140,40" font="Regular;20" valign="center" halign="center" backgroundColor="#9f1313" transparent="1" />
124                         <widget name="key_green" position="140,0" zPosition="1" size="140,40" font="Regular;20" valign="center" halign="center" backgroundColor="#1f771f" transparent="1" />
125                         <widget name="key_yellow" position="280,0" zPosition="1" size="140,40" font="Regular;20" valign="center" halign="center" backgroundColor="#a08500" transparent="1" />
126                         <widget name="key_blue" position="420,0" zPosition="1" size="140,40" font="Regular;20" valign="center" halign="center" backgroundColor="#18188b" transparent="1" />
127                         <widget source="title" render="Label" position="10,40" size="330,45" valign="center" font="Regular;22"/>
128                         <widget name="detailslabel" position="105,90" size="485,140" font="Regular;18" />
129                         <widget name="castlabel" position="10,235" size="580,155" font="Regular;18" />
130                         <widget name="extralabel" position="10,40" size="580,350" font="Regular;18" />
131                         <widget name="ratinglabel" position="340,62" size="250,20" halign="center" font="Regular;18" foregroundColor="#f0b400"/>
132                         <widget name="statusbar" position="10,404" size="580,16" font="Regular;16" foregroundColor="#cccccc" />
133                         <widget name="poster" position="4,90" size="96,140" alphatest="on" />
134                         <widget name="menu" position="10,115" size="580,275" zPosition="3" scrollbarMode="showOnDemand" />
135                         <widget name="starsbg" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/IMDb/starsbar_empty.png" position="340,40" zPosition="0" size="210,21" transparent="1" alphatest="on" />
136                         <widget name="stars" position="340,40" size="210,21" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/IMDb/starsbar_filled.png" transparent="1" />
137                 </screen>"""
138
139         def __init__(self, session, eventName, callbackNeeded=False, save=False, savepath=None, localpath=None):
140                 Screen.__init__(self, session)
141
142                 for tag in config.plugins.imdb.ignore_tags.getValue().split(','):
143                         eventName = eventName.replace(tag,'')
144
145                 self.eventName = eventName
146
147                 self.callbackNeeded = callbackNeeded
148                 self.callbackData = ""
149                 self.callbackGenre = ""
150
151                 self.saving = save
152                 self.savingpath = savepath
153                 self.localpath = localpath
154                 self.fetchurl = None
155
156                 self.dictionary_init()
157
158                 self["poster"] = Pixmap()
159                 self.picload = ePicLoad()
160                 self.picload_conn = self.picload.PictureData.connect(self.paintPosterPixmapCB)
161
162                 self["stars"] = ProgressBar()
163                 self["starsbg"] = Pixmap()
164                 self["stars"].hide()
165                 self["starsbg"].hide()
166                 self.ratingstars = -1
167
168                 self["title"] = StaticText(_("The Internet Movie Database"))
169                 # map new source -> old component
170                 def setText(txt):
171                         StaticText.setText(self["title"], txt)
172                         self["titellabel"].setText(txt)
173                 self["title"].setText = setText
174                 self["titellabel"] = Label()
175                 self["detailslabel"] = ScrollLabel("")
176                 self["castlabel"] = ScrollLabel("")
177                 self["extralabel"] = ScrollLabel("")
178                 self["statusbar"] = Label("")
179                 self["ratinglabel"] = Label("")
180                 self.resultlist = []
181                 self["menu"] = MenuList(self.resultlist)
182                 self["menu"].hide()
183
184                 self["key_red"] = Button(_("Exit"))
185                 self["key_green"] = Button("")
186                 self["key_yellow"] = Button("")
187                 self["key_blue"] = Button("")
188
189                 # 0 = multiple query selection menu page
190                 # 1 = movie info page
191                 # 2 = extra infos page
192                 self.Page = 0
193
194                 self["actions"] = ActionMap(["OkCancelActions", "ColorActions", "MovieSelectionActions", "DirectionActions"],
195                 {
196                         "ok": self.showDetails,
197                         "cancel": self.exit,
198                         "down": self.pageDown,
199                         "up": self.pageUp,
200                         "red": self.exit,
201                         "green": self.showMenu,
202                         "yellow": self.showDetails,
203                         "blue": self.showExtras,
204                         "contextMenu": self.contextMenuPressed,
205                         "showEventInfo": self.showDetails
206                 }, -1)
207
208                 self.getIMDB()
209
210                 if self.localpath is not None:                                # otherwise the stars are not correctly shown if we call details directly
211                         self.onLayoutFinish.append(self._layoutFinished)
212
213         def _layoutFinished(self):
214                 self["menu"].hide()
215                 self["extralabel"].hide()
216                 self["stars"].setValue(self.ratingstars)
217
218         def exit(self):
219                 if fileExists("/tmp/poster.jpg"):
220                         os.remove("/tmp/poster.jpg")
221                 if fileExists("/tmp/imdbquery.html"):
222                         os.remove("/tmp/imdbquery.html")
223                 if fileExists("/tmp/imdbquery2.html"):
224                         os.remove("/tmp/imdbquery2.html")
225                 if self.callbackNeeded:
226                         self.close([self.callbackData, self.callbackGenre])
227                 else:
228                         self.close()
229
230         def dictionary_init(self):
231                 syslang = language.getLanguage()
232                 if 1: #"de" not in syslang or config.plugins.imdb.force_english.value is True:
233                         self.generalinfomask = re.compile(
234                         #'<h1 class="header".*?>(?P<title>.*?)<.*?</h1>.*?'
235                         '<h1 itemprop="name" class="".*?>(?P<title>.*?)<.*?/h1>*'
236                         '(?:.*?<h4 class="inline">\s*(?P<g_director>Regisseur|Directors?):\s*</h4>.*?<a.*?>(?P<director>.*?)</a>)*'
237                         '(?:.*?<h4 class="inline">\s*(?P<g_creator>Sch\S*?pfer|Creators?):\s*</h4>.*?<a.*?>(?P<creator>.*?)</a>)*'
238                         '(?:.*?<h4 class="inline">\s*(?P<g_seasons>Seasons?):\s*</h4>.*?<a.*?>(?P<seasons>(?:\d+|unknown)?)</a>)*'
239                         '(?:.*?<h4 class="inline">\s*(?P<g_writer>Drehbuch|Writer).*?</h4>.*?<a.*?>(?P<writer>.*?)</a>)*'
240                         '(?:.*?<h4 class="inline">\s*(?P<g_country>Land|Country):\s*</h4>.*?<a.*?>(?P<country>.*?)</a>)*'
241                         '(?:.*?<h4 class="inline">\s*(?P<g_premiere>Premiere|Release Date).*?</h4>\s+(?P<premiere>.*?)\s*<span)*'
242                         '(?:.*?<h4 class="inline">\s*(?P<g_alternativ>Auch bekannt als|Also Known As):\s*</h4>\s*(?P<alternativ>.*?)\s*<span)*'
243                         , re.DOTALL)
244
245                         self.extrainfomask = re.compile(
246                         '(?:.*?<h4 class="inline">(?P<g_outline>Kurzbeschreibung|Plot Outline):</h4>(?P<outline>.+?)<)*'
247                         '(?:.*?<h2>(?P<g_synopsis>Storyline)</h2>.*?<p>(?P<synopsis>.+?)\s*</p>)*'
248                         '(?:.*?<h4 class="inline">(?P<g_keywords>Plot Keywords):</h4>(?P<keywords>.+?)(?:Mehr|See more</a>|</div>))*'
249                         '(?:.*?<h4 class="inline">(?P<g_tagline>Werbezeile|Tagline?):</h4>\s*(?P<tagline>.+?)<)*'
250                         '(?:.*?<h4 class="inline">(?P<g_awards>Filmpreise|Awards):</h4>\s*(?P<awards>.+?)(?:Mehr|See more</a>|</div>))*'
251                         '(?:.*?<h4 class="inline">(?P<g_language>Sprache|Language):</h4>\s*(?P<language>.+?)</div>)*'
252                         '(?:.*?<h4 class="inline">(?P<g_locations>Drehorte|Filming Locations):</h4>.*?<a.*?>(?P<locations>.+?)</a>)*'
253                         '(?:.*?<h4 class="inline">(?P<g_runtime>L\S*?nge|Runtime):</h4>\s*(?P<runtime>.+?)</div>)*'
254                         '(?:.*?<h4 class="inline">(?P<g_sound>Tonverfahren|Sound Mix):</h4>\s*(?P<sound>.+?)</div>)*'
255                         '(?:.*?<h4 class="inline">(?P<g_color>Farbe|Color):</h4>\s*(?P<color>.+?)</div>)*'
256                         '(?:.*?<h4 class="inline">(?P<g_aspect>Seitenverh\S*?ltnis|Aspect Ratio):</h4>\s*(?P<aspect>.+?)(?:Mehr|See more</a>|</div>))*'
257                         '(?:.*?<h4 class="inline">(?P<g_cert>Altersfreigabe|Certification):</h4>\s*(?P<cert>.+?)</div>)*'
258                         '(?:.*?<h4 class="inline">(?P<g_company>Firma|Company):</h4>\s*(?P<company>.+?)(?:Mehr|See more</a>|</div>))*'
259                         '(?:.*?<h4>(?P<g_trivia>Dies und das|Trivia)</h4>\s*(?P<trivia>.+?)(?:<span))*'
260                         '(?:.*?<h4>(?P<g_goofs>Pannen|Goofs)</h4>\s*(?P<goofs>.+?)(?:<span))*'
261                         '(?:.*?<h4>(?P<g_quotes>Dialogzitate|Quotes)</h4>\s*(?P<quotes>.+?)(?:<span))*'
262                         '(?:.*?<h4>(?P<g_connections>Bez\S*?ge zu anderen Titeln|Movie Connections)</h4>\s*(?P<connections>.+?)(?:<span))*'
263                         '(?:.*?<h2>(?P<g_comments>Nutzerkommentare|User Reviews)</h2>.*?<a href="/user/ur\d{7,7}/comments">(?P<commenter>.+?)</a>.*?<p>(?P<comment>.+?)</p>)*'
264                         , re.DOTALL)
265
266                         self.genreblockmask = re.compile('<h4 class="inline">Genre:</h4>\s<div class="info-content">\s+?(.*?)\s+?(?:Mehr|See more|</p|<a class|</div>)', re.DOTALL)
267                         self.ratingmask = re.compile('<span itemprop="ratingValue">(?P<rating>.*?)</', re.DOTALL)
268                         self.castmask = re.compile('itemprop=.url.> <span class="itemprop" itemprop="name">(?P<actor>.*?)</span>.*?<a href="/character/.*?" >(?P<character>.*?)</a>', re.DOTALL)
269                         #self.postermask = re.compile('<td .*?id="img_primary">.*?<img .*?src=\"(http.*?)\"', re.DOTALL)
270                         self.postermask = re.compile('<div class="poster">.*?<img .*?src=\"(http.*?)\"', re.DOTALL)
271
272                 self.htmltags = re.compile('<.*?>')
273
274         def resetLabels(self):
275                 self["detailslabel"].setText("")
276                 self["ratinglabel"].setText("")
277                 self["title"].setText("")
278                 self["castlabel"].setText("")
279                 self["titellabel"].setText("")
280                 self["extralabel"].setText("")
281                 self.ratingstars = -1
282
283         def pageUp(self):
284                 if self.Page == 0:
285                         self["menu"].instance.moveSelection(self["menu"].instance.moveUp)
286                 if self.Page == 1:
287                         self["castlabel"].pageUp()
288                         self["detailslabel"].pageUp()
289                 if self.Page == 2:
290                         self["extralabel"].pageUp()
291
292         def pageDown(self):
293                 if self.Page == 0:
294                         self["menu"].instance.moveSelection(self["menu"].instance.moveDown)
295                 if self.Page == 1:
296                         self["castlabel"].pageDown()
297                         self["detailslabel"].pageDown()
298                 if self.Page == 2:
299                         self["extralabel"].pageDown()
300
301         def showMenu(self):
302                 if ( self.Page is 1 or self.Page is 2 ) and self.resultlist:
303                         self["menu"].show()
304                         self["stars"].hide()
305                         self["starsbg"].hide()
306                         self["ratinglabel"].hide()
307                         self["castlabel"].hide()
308                         self["poster"].hide()
309                         self["extralabel"].hide()
310                         self["title"].setText(_("Ambiguous results"))
311                         self["detailslabel"].setText(_("Please select the matching entry"))
312                         self["detailslabel"].show()
313                         self["key_blue"].setText("")
314                         self["key_green"].setText(_("Title Menu"))
315                         self["key_yellow"].setText(_("Details"))
316                         self.Page = 0
317
318         def getLocalDetails(self):
319                 localfile = self.localpath
320                 self.html2utf8(open(localfile, "r").read())
321                 self.generalinfos = self.generalinfomask.search(self.inhtml)
322                 self.IMDBparse()
323                 if self.ratingstars > 0:
324                         self["starsbg"].show()
325                         self["stars"].show()
326                 self.Page = 1
327
328         def showDetails(self):
329                 self["ratinglabel"].show()
330                 self["castlabel"].show()
331                 self["detailslabel"].show()
332
333                 if self.resultlist and self.Page == 0:
334                         link = self["menu"].getCurrent()[1]
335                         title = self["menu"].getCurrent()[0]
336                         self["statusbar"].setText(_("Re-Query IMDb: %s...") % (title))
337                         localfile = "/tmp/imdbquery2.html"
338                         fetchurl = "http://imdb.com/title/" + link
339                         print("[IMDB] showDetails() downloading query " + fetchurl + " to " + localfile)
340                         download = downloadWithProgress(fetchurl,localfile)
341                         download.start().addCallback(self.IMDBquery2).addErrback(self.http_failed)
342                         self.fetchurl = fetchurl
343                         self["menu"].hide()
344                         self.resetLabels()
345                         self.Page = 1
346
347                 if self.Page == 2:
348                         self["extralabel"].hide()
349                         self["poster"].show()
350                         if self.ratingstars > 0:
351                                 self["starsbg"].show()
352                                 self["stars"].show()
353                                 self["stars"].setValue(self.ratingstars)
354
355                         self.Page = 1
356
357         def showExtras(self):
358                 if self.Page == 1:
359                         self["extralabel"].show()
360                         self["detailslabel"].hide()
361                         self["castlabel"].hide()
362                         self["poster"].hide()
363                         self["stars"].hide()
364                         self["starsbg"].hide()
365                         self["ratinglabel"].hide()
366                         self.Page = 2
367
368         def contextMenuPressed(self):
369                 list = [
370                         (_("Enter search"), self.openVirtualKeyBoard),
371                         (_("Select from EPG"), self.openChannelSelection),
372                         (_("Setup"), self.setup),
373                 ]
374
375                 if self.saving:
376                         if self.savingpath is not None:
377                                 # TODO: save Poster also as option for .html
378                                 list.extend((
379                                         (_("Save current Details as .html for offline using"), self.saveHtmlDetails),
380                                         (_("Save current Details as .txt"), self.saveTxtDetails),
381                                         (_("Save current Poster and Details as .txt"), self.savePosterTxtDetails),
382                                 ))
383
384                 if fileExists(resolveFilename(SCOPE_PLUGINS, "Extensions/YTTrailer/plugin.py")):
385                         list.extend((
386                                 (_("Play Trailer"), self.openYttrailer),
387                                 (_("Search Trailer"), self.searchYttrailer),
388                         ))
389
390                 self.session.openWithCallback(
391                         self.menuCallback,
392                         ChoiceBox,
393                         title=_("IMDb Menu"),
394                         list = list,
395                 )
396
397         def menuCallback(self, ret = None):
398                 ret and ret[1]()
399
400         def saveHtmlDetails(self):
401                 try:
402                         if self.savingpath is not None:
403                                 isave = self.savingpath + ".imdbquery2.html"
404                                 if self.fetchurl is not None:
405                                         download = downloadWithProgress(self.fetchurl,isave)
406                                         download.start().addCallback(self.IMDBsave).addErrback(self.http_failed)
407                 except Exception, e:
408                         print('[IMDb] saveHtmlDetails exception failure: ', str(e))
409
410         def saveTxtDetails(self):
411                 try:
412                         if self.savingpath is not None:
413                                 getTXT = self.IMDBsavetxt()
414                                 if getTXT is not None:
415                                         file(self.savingpath + ".txt",'w').write(getTXT)
416                                 else:
417                                         from Screens.MessageBox import MessageBox
418                                         self.session.open(MessageBox, (_('IMDb can not get Movie Information, to\n write .txt-file!')), MessageBox.TYPE_INFO, 10)
419                 except Exception, e:
420                         print('[IMDb] saveTxtDetails exception failure: ', str(e))
421
422         def savePosterTxtDetails(self):
423                 try:
424                         if self.savingpath is not None:
425                                 getTXT = self.IMDBsavetxt(True)
426                                 if getTXT is not None:
427                                         file(self.savingpath + ".txt",'w').write(getTXT)
428                                 else:
429                                         from Screens.MessageBox import MessageBox
430                                         self.session.open(MessageBox, (_('IMDb can not get Movie Information, to\n write .jpg and .txt-file!')), MessageBox.TYPE_INFO, 10)
431                 except Exception, e:
432                         print('[IMDb] savePosterTxtDetails exception failure: ', str(e))
433
434         def IMDBsave(self,string):
435                 self["statusbar"].setText(_("IMDb Save-Download completed"))
436                 self.html2utf8(open("/tmp/imdbquery2.html", "r").read())
437                 self.generalinfos = self.generalinfomask.search(self.inhtml)
438                 self.IMDBparse()
439
440         def IMDBsavetxt(self, poster=False):
441                 overview = ""
442                 runtime = ""
443                 genre = ""
444                 country = ""
445                 release = ""
446                 rating = ""
447
448                 if self.generalinfos:
449                         extrainfos = self.extrainfomask.search(self.inhtml)
450                         if extrainfos:
451                                 # get entry 1 = Overview(details)
452                                 try:
453                                         text = self.htmltags.sub('',extrainfos.group("synopsis").replace("\n",'').replace("<br>", '\n').replace("<br />",'\n').replace('&view=simple&sort=alpha&ref_=tt_stry_pl" >',' '))
454                                         overview = (_("Content:") + " " + text.encode('utf-8'))
455                                 except Exception, e:
456                                         print('[IMDb] IMDBsavetxt exception failure in get overview: ', str(e))
457                                         overview = (_("Content:"))
458 #                               print'[IMDb] IMDBsavetxt overview: ', overview
459
460                                 # get entry 2 = Runtime
461                                 try:
462                                         time = self.htmltags.sub('',extrainfos.group("runtime").replace("\n",'').replace("<br>", '\n').replace("<br />",'\n').replace('&view=simple&sort=alpha&ref_=tt_stry_pl" >',' '))
463                                         runtime = (_("Runtime:") + " " + time.encode('utf-8'))
464                                 except Exception, e:
465                                         print('[IMDb] IMDBsavetxt exception failure in get runtime: ', str(e))
466                                         runtime = (_("Runtime:"))
467 #                               print'[IMDb] IMDBsavetxt runtime: ', runtime
468
469                         # get entry 3 = Genre
470                         genreblock = self.genreblockmask.findall(self.inhtml)
471                         if genreblock:
472                                 genres = self.htmltags.sub('', genreblock[0])
473                                 if genres:
474                                         genre = (_("Genre:") + " " + genres.encode('utf-8'))
475                         else:
476                                 genre = (_("Genre:"))
477 #                       print'[IMDb] IMDBsavetxt genre: ', genre
478
479                         # get entry 4 = Country
480                         try:
481                                 land = self.htmltags.sub('', self.generalinfos.group("country").replace('\n',' ').replace("<br>", '\n').replace("<br />",'\n'))
482                                 country = (_("Production Countries:") + " " + land.encode('utf-8'))
483                         except Exception, e:
484                                 print('[IMDb] IMDBsavetxt exception failure in get country: ', str(e))
485                                 country = (_("Production Countries:"))
486 #                       print'[IMDb] IMDBsavetxt country: ', country
487
488                         # get entry 5 = ReleaseDate
489                         try:
490                                 date = self.htmltags.sub('', self.generalinfos.group("premiere").replace('\n',' ').replace("<br>", '\n').replace("<br />",'\n'))
491                                 release = (_("Release Date:") + " " + date.encode('utf-8'))
492                         except Exception, e:
493                                 print('[IMDb] IMDBsavetxt exception failure in get release: ', str(e))
494                                 release = (_("Release Date:"))
495 #                       print'[IMDb] IMDBsavetxt release: ', release
496
497                         # get entry 5 = Vote
498                         ratingtext = self.ratingmask.search(self.inhtml)
499                         if ratingtext:
500                                 ratingtext = ratingtext.group("rating")
501                                 if ratingtext != '<span id="voteuser"></span>':
502                                         text = ratingtext                                # + " / 10"
503                                         rating = (_("User Rating") + ": " + text.encode('utf-8'))
504                         else:
505                                 rating = (_("User Rating") + ": ")
506 #                       print'[IMDb] IMDBsavetxt rating: ', rating
507
508                         # get the poster.jpg
509                         if poster:
510                                 try:
511                                         posterurl = self.postermask.search(self.inhtml)
512                                         if posterurl and posterurl.group(1).find("jpg") > 0:
513                                                 posterurl = posterurl.group(1)
514                                                 postersave = self.savingpath + ".poster.jpg"
515                                                 print("[IMDB] downloading poster " + posterurl + " to " + postersave)
516                                                 download = downloadWithProgress(posterurl,postersave)
517                                                 download.start().addErrback(self.http_failed)
518                                 except Exception, e:
519                                         print('[IMDb] IMDBsavetxt exception failure in get poster: ', str(e))
520
521                 return overview + "\n\n" + runtime + "\n" + genre + "\n" + country + "\n" + release + "\n" + rating + "\n"
522
523         def openYttrailer(self):
524                 try:
525                         from Plugins.Extensions.YTTrailer.plugin import YTTrailer, baseEPGSelection__init__
526                 except ImportError as ie:
527                         pass
528                 if baseEPGSelection__init__ is None:
529                         return
530
531                 ytTrailer = YTTrailer(self.session)
532                 ytTrailer.showTrailer(self.eventName)
533
534         def searchYttrailer(self):
535                 try:
536                         from Plugins.Extensions.YTTrailer.plugin import YTTrailerList, baseEPGSelection__init__
537                 except ImportError as ie:
538                         pass
539                 if baseEPGSelection__init__ is None:
540                         return
541
542                 self.session.open(YTTrailerList, self.eventName)
543
544         def openVirtualKeyBoard(self):
545                 self.session.openWithCallback(
546                         self.gotSearchString,
547                         VirtualKeyBoard,
548                         title = _("Enter text to search for"),
549                         text = self.eventName
550                 )
551
552         def openChannelSelection(self):
553                 self.session.openWithCallback(
554                         self.gotSearchString,
555                         IMDBChannelSelection
556                 )
557
558         def gotSearchString(self, ret = None):
559                 if ret:
560                         self.eventName = ret
561                         self.Page = 0
562                         self.resultlist = []
563                         self["menu"].hide()
564                         self["ratinglabel"].show()
565                         self["castlabel"].show()
566                         self["detailslabel"].show()
567                         self["poster"].hide()
568                         self["stars"].hide()
569                         self["starsbg"].hide()
570                         self.getIMDB(search=True)
571
572         def getIMDB(self, search=False):
573                 self.resetLabels()
574                 if not self.eventName:
575                         s = self.session.nav.getCurrentService()
576                         info = s and s.info()
577                         event = info and info.getEvent(0) # 0 = now, 1 = next
578                         if event:
579                                 self.eventName = event.getEventName()
580                         else:
581                                 self.eventName = self.session.nav.getCurrentlyPlayingServiceReference().toString()
582                                 self.eventName = self.eventName.split('/')
583                                 self.eventName = self.eventName[-1]
584                                 self.eventName = self.eventName.replace('.',' ')
585                                 self.eventName = self.eventName.split('-')
586                                 self.eventName = self.eventName[0]
587                                 if self.eventName.endswith(' '):
588                                         self.eventName = self.eventName[:-1]
589
590                 if self.localpath is not None and not search:
591                         if os.path.exists(self.localpath):
592                                 self.getLocalDetails()
593
594                 else:
595                         if self.eventName:
596                                 self["statusbar"].setText(_("Query IMDb: %s") % (self.eventName))
597                                 localfile = "/tmp/imdbquery.html"
598                                 fetchurl = "http://imdb.com/find?q=" + quoteEventName(self.eventName) + "&s=tt&site=aka"
599                                 print("[IMDB] getIMDB() Downloading Query " + fetchurl + " to " + localfile)
600                                 download = downloadWithProgress(fetchurl,localfile)
601                                 download.start().addCallback(self.IMDBquery).addErrback(self.http_failed)
602
603                         else:
604                                 self["statusbar"].setText(_("Could't get Eventname"))
605
606         def html2utf8(self,in_html):
607                 in_html = (re.subn(r'<(script).*?</\1>(?s)', '', in_html)[0])
608                 in_html = (re.subn(r'<(style).*?</\1>(?s)', '', in_html)[0])
609                 entitydict = {}
610
611                 entities = re.finditer('&([^#][A-Za-z]{1,5}?);', in_html)
612                 for x in entities:
613                         key = x.group(0)
614                         if key not in entitydict:
615                                 entitydict[key] = htmlentitydefs.name2codepoint[x.group(1)]
616
617                 entities = re.finditer('&#x([0-9A-Fa-f]{2,2}?);', in_html)
618                 for x in entities:
619                         key = x.group(0)
620                         if key not in entitydict:
621                                 entitydict[key] = "%d" % int(key[3:5], 16)
622
623                 entities = re.finditer('&#(\d{1,5}?);', in_html)
624                 for x in entities:
625                         key = x.group(0)
626                         if key not in entitydict:
627                                 entitydict[key] = x.group(1)
628
629                 if re.search("charset=utf-8", in_html):
630                         for key, codepoint in iteritems(entitydict):
631                                 in_html = in_html.replace(key, unichr(int(codepoint)))
632                         self.inhtml = in_html.encode('utf8')
633                         return
634
635                 for key, codepoint in iteritems(entitydict):
636                         in_html = in_html.replace(key, unichr(int(codepoint)).encode('latin-1', 'ignore'))
637                 self.inhtml = in_html.decode('latin-1').encode('utf8')
638
639         def IMDBquery(self,string):
640                 self["statusbar"].setText(_("IMDb Download completed"))
641
642                 self.html2utf8(open("/tmp/imdbquery.html", "r").read())
643
644                 self.generalinfos = self.generalinfomask.search(self.inhtml)
645
646                 if self.generalinfos:
647                         self.IMDBparse()
648                 else:
649                         if re.search("<title>Find - IMDb</title>", self.inhtml):
650                                 pos = self.inhtml.find("<table class=\"findList\">")
651                                 pos2 = self.inhtml.find("</table>",pos)
652                                 findlist = self.inhtml[pos:pos2]
653                                 searchresultmask = re.compile('<tr class=\"findResult (?:odd|even)\">.*?<td class=\"result_text\"> <a href=\"/title/(tt\d{7,7})/.*?\"\s?>(.*?)</a>.*?</td>', re.DOTALL)
654                                 searchresults = searchresultmask.finditer(findlist)
655                                 self.resultlist = [(self.htmltags.sub('',x.group(2)), x.group(1)) for x in searchresults]
656                                 Len = len(self.resultlist)
657                                 self["menu"].l.setList(self.resultlist)
658                                 if Len == 1:
659                                         self["statusbar"].setText(_("Re-Query IMDb: %s...") % (self.resultlist[0][0],))
660                                         self.eventName = self.resultlist[0][1]
661                                         localfile = "/tmp/imdbquery.html"
662                                         fetchurl = "http://imdb.com/find?q=" + quoteEventName(self.eventName) + "&s=tt&site=aka"
663                                         download = downloadWithProgress(fetchurl,localfile)
664                                         download.start().addCallback(self.IMDBquery).addErrback(self.http_failed)
665                                 elif Len > 1:
666                                         self.Page = 1
667                                         self.showMenu()
668                                 else:
669                                         self["detailslabel"].setText(_("No IMDb match."))
670                                         self["statusbar"].setText(_("No IMDb match.") + ' ' + self.eventName)
671                         else:
672                                 splitpos = self.eventName.find('(')
673                                 if splitpos > 0 and self.eventName.endswith(')'):
674                                         self.eventName = self.eventName[splitpos+1:-1]
675                                         self["statusbar"].setText(_("Re-Query IMDb: %s...") % (self.eventName))
676                                         # event_quoted = quoteEventName(self.eventName)
677                                         localfile = "/tmp/imdbquery.html"
678                                         fetchurl = "http://imdb.com/find?q=" + quoteEventName(self.eventName) + "&s=tt&site=aka"
679                                         download = downloadWithProgress(fetchurl,localfile)
680                                         download.start().addCallback(self.IMDBquery).addErrback(self.http_failed)
681                                 else:
682                                         self["detailslabel"].setText(_("IMDb query failed!"))
683
684         def http_failed(self, failure_instance=None, error_message=""):
685                 text = _("IMDb Download failed")
686                 if error_message == "" and failure_instance is not None:
687                         error_message = failure_instance.getErrorMessage()
688                         text += ": " + error_message
689                 print("[IMDB] ",text)
690                 self["statusbar"].setText(text)
691
692         def IMDBquery2(self,string):
693                 self["statusbar"].setText(_("IMDb Re-Download completed"))
694                 self.html2utf8(open("/tmp/imdbquery2.html", "r").read())
695                 self.generalinfos = self.generalinfomask.search(self.inhtml)
696                 self.IMDBparse()
697
698         def IMDBparse(self):
699                 self.Page = 1
700                 Detailstext = _("No details found.")
701                 if self.generalinfos:
702                         self["key_yellow"].setText(_("Details"))
703                         self["statusbar"].setText(_("IMDb Details parsed"))
704                         Titeltext = self.generalinfos.group("title")
705                         if len(Titeltext) > 57:
706                                 Titeltext = Titeltext[0:54] + "..."
707                         self["title"].setText(Titeltext)
708
709                         Detailstext = ""
710
711                         genreblock = self.genreblockmask.findall(self.inhtml)
712                         if genreblock:
713                                 genres = self.htmltags.sub('', genreblock[0])
714                                 if genres:
715                                         Detailstext += "Genre: "
716                                         Detailstext += genres
717                                         self.callbackGenre = genres
718
719                         for category in ("director", "creator", "writer", "seasons"):
720                                 if self.generalinfos.group(category):
721                                         Detailstext += "\n" + self.generalinfos.group('g_'+category) + ": " + self.generalinfos.group(category).replace('<span class="itemprop" itemprop="name">','').replace('</span>','')
722
723                         for category in ("premiere", "country", "alternativ"):
724                                 if self.generalinfos.group(category):
725                                         Detailstext += "\n" + self.generalinfos.group('g_'+category) + ": " + self.htmltags.sub('', self.generalinfos.group(category).replace('\n',' ').replace("<br>", '\n').replace("<br />",'\n'))
726
727                         rating = self.ratingmask.search(self.inhtml)
728                         Ratingtext = _("no user rating yet")
729                         if rating:
730                                 rating = rating.group("rating")
731                                 if rating != '<span id="voteuser"></span>':
732                                         Ratingtext = _("User Rating") + ": " + rating + " / 10"
733                                         self.ratingstars = int(10*round(float(rating.replace(',','.')),1))
734                                         self["stars"].show()
735                                         self["stars"].setValue(self.ratingstars)
736                                         self["starsbg"].show()
737                         self["ratinglabel"].setText(Ratingtext)
738
739                         castresult = self.castmask.finditer(self.inhtml)
740                         if castresult:
741                                 Casttext = ""
742                                 for x in castresult:
743                                         Casttext += "\n" + self.htmltags.sub('', x.group('actor'))
744                                         if x.group('character'):
745                                                 Casttext += _(" as ") + self.htmltags.sub('', x.group('character').replace('/ ...','')).replace('\n', ' ')
746                                                 #if x.group('additional'):
747                                                 #       Casttext += ' ' + x.group('additional')
748                                 if Casttext:
749                                         Casttext = _("Cast: ") + Casttext
750                                 else:
751                                         Casttext = _("No cast list found in the database.")
752                                 self["castlabel"].setText(Casttext)
753
754                         posterurl = self.postermask.search(self.inhtml)
755                         if posterurl and posterurl.group(1).find("jpg") > 0:
756                                 posterurl = posterurl.group(1)
757                                 self["statusbar"].setText(_("Downloading Movie Poster: %s...") % (posterurl))
758                                 localfile = "/tmp/poster.jpg"
759                                 print("[IMDB] downloading poster " + posterurl + " to " + localfile)
760                                 download = downloadWithProgress(posterurl,localfile)
761                                 download.start().addCallback(self.IMDBPoster).addErrback(self.http_failed)
762                         else:
763                                 self.IMDBPoster("kein Poster")
764                         extrainfos = self.extrainfomask.search(self.inhtml)
765
766                         if extrainfos:
767                                 Extratext = "Extra Info\n"
768
769                                 for category in ("tagline","outline","synopsis","keywords","awards","runtime","language","color","aspect","sound","cert","locations","company","trivia","goofs","quotes","connections"):
770                                         if extrainfos.group('g_'+category):
771                                                 Extratext += extrainfos.group('g_'+category) + ": " + self.htmltags.sub('',extrainfos.group(category).replace("\n",'').replace("<br>", '\n').replace("<br />",'\n').replace('&view=simple&sort=alpha&ref_=tt_stry_pl" >',' ')) + "\n"
772                                 if extrainfos.group("g_comments"):
773                                         stripmask = re.compile('\s{2,}', re.DOTALL)
774                                         Extratext += extrainfos.group("g_comments") + " [" + stripmask.sub(' ', self.htmltags.sub('',extrainfos.group("commenter"))) + "]: " + self.htmltags.sub('',extrainfos.group("comment").replace("\n",' ')) + "\n"
775
776                                 Extratext = transHTML(Extratext)
777                                 self["extralabel"].setText(Extratext)
778                                 self["extralabel"].hide()
779                                 self["key_blue"].setText(_("Extra Info"))
780
781                 self["detailslabel"].setText(Detailstext)
782                 self.callbackData = Detailstext
783
784         def IMDBPoster(self,string):
785                 self["statusbar"].setText(_("IMDb Details parsed"))
786                 if not string:
787                         filename = "/tmp/poster.jpg"
788                 else:
789                         filename = resolveFilename(SCOPE_PLUGINS, "Extensions/IMDb/no_poster.png")
790                 sc = AVSwitch().getFramebufferScale()
791                 self.picload.setPara((self["poster"].instance.size().width(), self["poster"].instance.size().height(), sc[0], sc[1], False, 1, "#00000000"))
792                 self.picload.startDecode(filename)
793
794         def paintPosterPixmapCB(self, picInfo=None):
795                 ptr = self.picload.getData()
796                 if ptr != None:
797                         self["poster"].instance.setPixmap(ptr)
798                         self["poster"].show()
799
800         def setup(self):
801                 self.session.open(IMDbSetup)
802
803         def createSummary(self):
804                 return IMDbLCDScreen
805
806 class IMDbLCDScreen(Screen):
807         skin = """
808         <screen position="0,0" size="132,64" title="IMDB Plugin">
809                 <widget name="headline" position="4,0" size="128,22" font="Regular;20"/>
810                 <widget source="parent.title" render="Label" position="6,26" size="120,34" font="Regular;14"/>
811         </screen>"""
812
813         def __init__(self, session, parent):
814                 Screen.__init__(self, session, parent)
815                 self["headline"] = Label(_("IMDb Plugin"))
816
817 class IMDbSetup(Screen, ConfigListScreen):
818         skin = """<screen name="EPGSearchSetup" position="center,center" size="565,370">
819                 <ePixmap pixmap="skin_default/buttons/red.png" position="0,0" size="140,40" alphatest="on" />
820                 <ePixmap pixmap="skin_default/buttons/green.png" position="140,0" size="140,40" alphatest="on" />
821                 <widget source="key_red" render="Label" position="0,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#9f1313" transparent="1" />
822                 <widget source="key_green" render="Label" position="140,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#1f771f" transparent="1" />
823                 <widget name="config" position="5,50" size="555,250" scrollbarMode="showOnDemand" />
824                 <ePixmap pixmap="skin_default/div-h.png" position="0,301" zPosition="1" size="565,2" />
825                 <widget source="help" render="Label" position="5,305" size="555,63" font="Regular;21" />
826         </screen>"""
827
828         def __init__(self, session):
829                 Screen.__init__(self, session)
830                 self.skinName = ["Setup" ]
831
832                 self['footnote'] = Label(_("* = Restart Required"))
833                 self["HelpWindow"] = Pixmap()
834                 self["HelpWindow"].hide()
835                 self["VKeyIcon"] = Boolean(False)
836
837                 # Summary
838                 self.setup_title = _("IMDb Setup")
839                 self.onChangedEntry = []
840
841                 # Initialize widgets
842                 self["key_green"] = StaticText(_("OK"))
843                 self["key_red"] = StaticText(_("Cancel"))
844                 self["description"] = Label("")
845
846                 # Define Actions
847                 self["actions"] = ActionMap(["SetupActions"],
848                         {
849                                 "cancel": self.keyCancel,
850                                 "save": self.keySave,
851                         }, -2)
852
853                 self["VirtualKB"] = ActionMap(["VirtualKeyboardActions"],
854                 {
855                         "showVirtualKeyboard": self.KeyText,
856                 }, -2)
857                 self["VirtualKB"].setEnabled(False)
858
859                 self.list = []
860                 ConfigListScreen.__init__(self, self.list, session = self.session, on_change = self.changedEntry)
861                 self.createSetup()
862                 if not self.handleInputHelpers in self["config"].onSelectionChanged:
863                         self["config"].onSelectionChanged.append(self.handleInputHelpers)
864                 self.changedEntry()
865                 self.onLayoutFinish.append(self.layoutFinished)
866
867         def createSetup(self):
868                 self.list = []
869                 self.list.append(getConfigListEntry(_("Show in plugin browser"), config.plugins.imdb.showinplugins, _("Enable this to be able to access the IMDb from within the plugin browser.")))
870                 self.list.append(getConfigListEntry(_("Words / phrases to ignore "), config.plugins.imdb.ignore_tags, _("This option allows you add words/phrases for IMDb to ignore when searching. please seperaate with a comma")))
871                 self["config"].list = self.list
872                 self["config"].l.setList(self.list)
873
874         def handleInputHelpers(self):
875                 if self["config"].getCurrent() is not None:
876                         try:
877                                 if isinstance(self["config"].getCurrent()[1], ConfigText) or isinstance(self["config"].getCurrent()[1], ConfigPassword):
878                                         if self.has_key("VKeyIcon"):
879                                                 self["VirtualKB"].setEnabled(True)
880                                                 self["VKeyIcon"].boolean = True
881                                         if self.has_key("HelpWindow"):
882                                                 if self["config"].getCurrent()[1].help_window.instance is not None:
883                                                         helpwindowpos = self["HelpWindow"].getPosition()
884                                                         from enigma import ePoint
885                                                         self["config"].getCurrent()[1].help_window.instance.move(ePoint(helpwindowpos[0],helpwindowpos[1]))
886                                 else:
887                                         if self.has_key("VKeyIcon"):
888                                                 self["VirtualKB"].setEnabled(False)
889                                                 self["VKeyIcon"].boolean = False
890                         except:
891                                 if self.has_key("VKeyIcon"):
892                                         self["VirtualKB"].setEnabled(False)
893                                         self["VKeyIcon"].boolean = False
894                 else:
895                         if self.has_key("VKeyIcon"):
896                                 self["VirtualKB"].setEnabled(False)
897                                 self["VKeyIcon"].boolean = False
898
899         def HideHelp(self):
900                 try:
901                         if isinstance(self["config"].getCurrent()[1], ConfigText):
902                                 if self["config"].getCurrent()[1].help_window.instance is not None:
903                                         self["config"].getCurrent()[1].help_window.hide()
904                 except:
905                         pass
906
907         def KeyText(self):
908                 if isinstance(self["config"].getCurrent()[1], ConfigText):
909                         if self["config"].getCurrent()[1].help_window.instance is not None:
910                                 self["config"].getCurrent()[1].help_window.hide()
911                 self.session.openWithCallback(self.VirtualKeyBoardCallback, VirtualKeyBoard, title = self["config"].getCurrent()[0], text = self["config"].getCurrent()[1].getValue())
912
913         def VirtualKeyBoardCallback(self, callback = None):
914                 if callback is not None and len(callback):
915                         self["config"].getCurrent()[1].setValue(callback)
916                         self["config"].invalidate(self["config"].getCurrent())
917
918         def layoutFinished(self):
919                 self.setTitle(_(self.setup_title))
920
921         # for summary:
922         def changedEntry(self):
923                 self.item = self["config"].getCurrent()
924                 for x in self.onChangedEntry:
925                         x()
926                 try:
927                         if isinstance(self["config"].getCurrent()[1], ConfigYesNo) or isinstance(self["config"].getCurrent()[1], ConfigSelection):
928                                 self.createSetup()
929                 except:
930                         pass
931
932         def getCurrentEntry(self):
933                 return self["config"].getCurrent() and self["config"].getCurrent()[0] or ""
934
935         def getCurrentValue(self):
936                 return self["config"].getCurrent() and str(self["config"].getCurrent()[1].getText()) or ""
937
938         def getCurrentDescription(self):
939                 return self["config"].getCurrent() and len(self["config"].getCurrent()) > 2 and self["config"].getCurrent()[2] or ""
940
941         def createSummary(self):
942                 from Screens.Setup import SetupSummary
943                 return SetupSummary
944
945         def keySave(self):
946                 self.saveAll()
947                 if not config.plugins.imdb.showinplugins.value:
948                         for plugin in plugins.getPlugins(PluginDescriptor.WHERE_PLUGINMENU):
949                                 if plugin.name == _("IMDb Details"):
950                                         plugins.removePlugin(plugin)
951
952                 plugins.readPluginList(resolveFilename(SCOPE_PLUGINS))
953                 self.close()
954
955         def createSummary(self):
956                 from Screens.Setup import SetupSummary
957                 return SetupSummary
958
959 def eventinfo(session, eventName="", **kwargs):
960         if not eventName:
961                 s = session.nav.getCurrentService()
962                 if s:
963                         info = s.info()
964                         event = info.getEvent(0) # 0 = now, 1 = next
965                         eventName = event and event.getEventName() or ''
966         session.open(IMDB, eventName)
967
968 def main(session, eventName="", **kwargs):
969         session.open(IMDB, eventName)
970
971 pluginlist = PluginDescriptor(name=_("IMDb Details"), description=_("Query details from the Internet Movie Database"), icon="imdb.png", where=PluginDescriptor.WHERE_PLUGINMENU, fnc=main, needsRestart=False)
972
973 def Plugins(**kwargs):
974         l = [PluginDescriptor(name=_("IMDb Details") + "...",
975                         description=_("Query details from the Internet Movie Database"),
976                         where=PluginDescriptor.WHERE_EVENTINFO,
977                         fnc=eventinfo,
978                         needsRestart=False,
979                         ),
980                 ]
981
982         if config.plugins.imdb.showinplugins.value:
983                 l.append(pluginlist)
984
985         return l