imdb/ofdb: allow to select different event than the current one in eventinfo hook
[enigma2-plugins.git] / ofdb / src / plugin.py
1 # -*- coding: utf8 -*-
2 from Plugins.Plugin import PluginDescriptor
3 from twisted.web.client import downloadPage
4 from enigma import ePicLoad
5 from Screens.Screen import Screen
6 from Screens.EpgSelection import EPGSelection
7 from Components.ActionMap import ActionMap
8 from Components.Pixmap import Pixmap
9 from Components.Label import Label
10 from Components.ScrollLabel import ScrollLabel
11 from Components.Button import Button
12 from Components.AVSwitch import AVSwitch
13 from Components.MenuList import MenuList
14 from Components.Language import language
15 from Components.ProgressBar import ProgressBar
16 from Tools.Directories import resolveFilename, SCOPE_PLUGINS, SCOPE_SKIN_IMAGE
17 import re
18 import htmlentitydefs
19 import urllib
20
21 class OFDBEPGSelection(EPGSelection):
22         def __init__(self, *args):
23                 EPGSelection.__init__(self, *args)
24                 self.skinName = "EPGSelection"
25                 self["key_green"].setText(_("Lookup"))
26
27         def infoKeyPressed(self):
28                 self.timerAdd()
29
30         def timerAdd(self):
31                 cur = self["list"].getCurrent()
32                 evt = cur[0]
33                 sref = cur[1]
34                 if not evt: 
35                         return
36
37                 self.session.open(
38                         OFDB,
39                         evt.getEventName()
40                 )
41
42         def onSelectionChanged(self):
43                 pass
44
45 class OFDB(Screen):
46         skin = """
47                 <screen name="OFDb" position="90,95" size="560,420" title="Internet Movie Database Details Plugin" >
48                         <ePixmap pixmap="skin_default/buttons/red.png" position="0,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
49                         <ePixmap pixmap="skin_default/buttons/green.png" position="140,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
50                         <ePixmap pixmap="skin_default/buttons/yellow.png" position="280,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
51                         <ePixmap pixmap="skin_default/buttons/blue.png" position="420,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
52                         <widget name="key_red" position="0,0" zPosition="1" size="140,40" font="Regular;20" valign="center" halign="center" backgroundColor="#9f1313" transparent="1" />
53                         <widget name="key_green" position="140,0" zPosition="1" size="140,40" font="Regular;20" valign="center" halign="center" backgroundColor="#1f771f" transparent="1" />
54                         <widget name="key_yellow" position="280,0" zPosition="1" size="140,40" font="Regular;20" valign="center" halign="center" backgroundColor="#a08500" transparent="1" />
55                         <widget name="key_blue" position="420,0" zPosition="1" size="140,40" font="Regular;20" valign="center" halign="center" backgroundColor="#18188b" transparent="1" />
56                         <widget name="titellabel" position="10,40" size="330,45" valign="center" font="Regular;22"/>
57                         <widget name="detailslabel" position="105,90" size="445,140" font="Regular;18" />
58                         <widget name="castlabel" position="10,235" size="540,155" font="Regular;18" />
59                         <widget name="extralabel" position="10,40" size="540,350" font="Regular;18" />
60                         <widget name="ratinglabel" position="340,62" size="210,20" halign="center" font="Regular;18" foregroundColor="#f0b400"/>
61                         <widget name="statusbar" position="10,404" size="540,16" font="Regular;16" foregroundColor="#cccccc" />
62                         <widget name="poster" position="4,90" size="96,140" alphatest="on" />
63                         <widget name="menu" position="10,115" size="540,275" zPosition="3" scrollbarMode="showOnDemand" />
64                         <widget name="starsbg" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/OFDb/starsbar_empty.png" position="340,40" zPosition="0" size="210,21" transparent="1" alphatest="on" />
65                         <widget name="stars" position="340,40" size="210,21" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/OFDb/starsbar_filled.png" transparent="1" />
66                 </screen>"""
67
68         def __init__(self, session, eventName, args = None):
69                 self.skin = OFDB.skin
70                 Screen.__init__(self, session)
71                 self.eventName = eventName
72                 self.dictionary_init()
73                 self["poster"] = Pixmap()
74                 self.picload = ePicLoad()
75                 self.picload.PictureData.get().append(self.paintPosterPixmapCB)
76
77                 self["stars"] = ProgressBar()
78                 self["starsbg"] = Pixmap()
79                 self["stars"].hide()
80                 self["starsbg"].hide()
81                 self.ratingstars = -1
82                 self["titellabel"] = Label("The Internet Movie Database")
83                 self["detailslabel"] = ScrollLabel("")
84                 self["castlabel"] = ScrollLabel("")
85                 self["extralabel"] = ScrollLabel("")
86                 self["statusbar"] = Label("")
87                 self["ratinglabel"] = Label("")
88                 self.resultlist = []
89                 self["menu"] = MenuList(self.resultlist)
90                 self["menu"].hide()
91                 self["key_red"] = Button(self._("Exit"))
92                 self["key_green"] = Button("")
93                 self["key_yellow"] = Button("")
94                 self["key_blue"] = Button("")
95                 # 0 = multiple query selection menu page
96                 # 1 = movie info page
97                 # 2 = extra infos page
98                 self.Page = 0
99
100                 self["actions"] = ActionMap(["OkCancelActions", "ColorActions", "MovieSelectionActions", "DirectionActions"],
101                 {
102                         "ok": self.showDetails,
103                         "cancel": self.close,
104                         "down": self.pageDown,
105                         "up": self.pageUp,
106                         "red": self.close,
107                         "green": self.showMenu,
108                         "yellow": self.showDetails,
109                         "blue": self.showExtras,
110                         "showEventInfo": self.showDetails
111                 }, -1)
112                 
113                 self.getOFDB()
114
115         def dictionary_init(self):
116                 syslang = language.getLanguage()
117                 if syslang.find("de") is -1:
118                         self.OFDBlanguage = ""  # set to empty ("") for english version
119                 else:
120                         self.OFDBlanguage = "german." # it's a subdomain, so add a '.' at the end
121
122                 self.dict = {}
123                 self.dict["of"]="von"
124                 self.dict[" as "]=" als "
125                 self.dict["Ambiguous results"]="Kein eindeutiger Treffer"
126                 self.dict["Please select the matching entry"]="Bitte passenden Eintrag auswählen"
127                 self.dict["No OFDb match."]="Keine passenden Einträge gefunden."
128                 self.dict["OFDb query failed!"]="OFDb-Query fehlgeschlagen!"
129                 self.dict["No details found."]="Keine Details gefunden."
130                 self.dict["no user rating yet"]="noch keine Nutzerwertung"
131                 self.dict["Cast: "]="Darsteller: "
132                 self.dict["No cast list found in the database."]="Keine Darstellerliste in der Datenbank gefunden."
133                 self.dict["Exit"]="Beenden"
134                 self.dict["Extra Info"]="Zusatzinfos"
135                 self.dict["Title Menu"]="Titelauswahl"
136
137                 self.htmltags = re.compile('<.*?>')
138
139                 self.generalinfomask = re.compile(
140                 '<title>OFDb - (?P<title>.*?)</title>.*?'
141                 '(?P<g_original>Originaltitel):[\s\S]*?class=\"Daten\">(?P<original>.*?)</td>'
142                 '(?:.*?(?P<g_country>Herstellungsland):[\s\S]*?class="Daten">(?P<country>.*?)(?:\.\.\.|</td>))*'
143                 '(?:.*?(?P<g_year>Erscheinungsjahr):[\s\S]*?class="Daten">(?P<year>.*?)</td>)*'
144                 '(?:.*?(?P<g_director>Regie):[\s\S]*?class="Daten">(?P<director>.*?)(?:\.\.\.|</td>))*'
145                 , re.DOTALL)
146
147         def _(self, in_string):
148                 out_string = in_string
149                 if ((self.OFDBlanguage).find("german")) != -1:
150                         out_string = self.dict.get(in_string, in_string)
151                 return out_string
152
153         def resetLabels(self):
154                 self["detailslabel"].setText("")
155                 self["ratinglabel"].setText("")
156                 self["titellabel"].setText("")
157                 self["castlabel"].setText("")
158                 self["titellabel"].setText("")
159                 self["extralabel"].setText("")
160                 self.ratingstars = -1
161
162         def pageUp(self):
163                 if self.Page == 0:
164                         self["menu"].instance.moveSelection(self["menu"].instance.moveUp)
165                 if self.Page == 1:
166                         self["castlabel"].pageUp()
167                         self["detailslabel"].pageUp()
168                 if self.Page == 2:
169                         self["extralabel"].pageUp()
170         
171         def pageDown(self):
172                 if self.Page == 0:
173                         self["menu"].instance.moveSelection(self["menu"].instance.moveDown)
174                 if self.Page == 1:
175                         self["castlabel"].pageDown()
176                         self["detailslabel"].pageDown()
177                 if self.Page == 2:
178                         self["extralabel"].pageDown()
179
180         def showMenu(self):
181                 if ( self.Page is 1 or self.Page is 2 ) and self.resultlist:
182                         self["menu"].show()
183                         self["stars"].hide()
184                         self["starsbg"].hide()
185                         self["ratinglabel"].hide()
186                         self["castlabel"].hide()
187                         self["poster"].hide()
188                         self["extralabel"].hide()
189                         self["titellabel"].setText(self._("Ambiguous results"))
190                         self["detailslabel"].setText(self._("Please select the matching entry"))
191                         self["detailslabel"].show()
192                         self["key_blue"].setText("")
193                         self["key_green"].setText(self._("Title Menu"))
194                         self["key_yellow"].setText(self._("Details"))
195                         self.Page = 0
196
197         def showDetails(self):
198                 self["ratinglabel"].show()
199                 self["castlabel"].show()
200                 self["detailslabel"].show()
201
202                 if self.resultlist and self.Page == 0:
203                         link = self["menu"].getCurrent()[1]
204                         title = self["menu"].getCurrent()[0]
205                         self["statusbar"].setText("Re-Query OFDb: "+title+"...")
206                         localfile = "/tmp/ofdbquery2.html"
207                         fetchurl = "http://www.ofdb.de/film/" + link
208                         print "[OFDb] downloading query " + fetchurl + " to " + localfile
209                         downloadPage(fetchurl,localfile).addCallback(self.OFDBquery2).addErrback(self.fetchFailed)
210                         self["menu"].hide()
211                         self.resetLabels()
212                         self.Page = 1
213
214                 if self.Page == 2:
215                         self["extralabel"].hide()
216                         self["poster"].show()
217                         if self.ratingstars > 0:
218                                 self["starsbg"].show()
219                                 self["stars"].show()
220                                 self["stars"].setValue(self.ratingstars)
221
222                         self.Page = 1
223
224         def showExtras(self):
225                 if self.Page == 1:
226                         self["extralabel"].show()
227                         self["detailslabel"].hide()
228                         self["castlabel"].hide()
229                         self["poster"].hide()
230                         self["stars"].hide()
231                         self["starsbg"].hide()
232                         self["ratinglabel"].hide()
233                         self.Page = 2
234
235         def getOFDB(self):
236                 self.resetLabels()
237                 if self.eventName is "":
238                         s = self.session.nav.getCurrentService()
239                         info = s.info()
240                         event = info.getEvent(0) # 0 = now, 1 = next
241                         if event:
242                                 self.eventName = event.getEventName()
243                         try:
244                                 pos = self.eventName.index("(")
245                                 self.eventName=self.eventName[0:pos]
246                         except ValueError:
247                                 pass
248
249                 if self.eventName is not "":
250                         self["statusbar"].setText("Query OFDb: " + self.eventName + "...")
251                         try:
252                                 self.eventName = urllib.quote(self.eventName)
253                         except:
254                                 self.eventName = urllib.quote(self.eventName.decode('utf8').encode('ascii','ignore'))
255                         localfile = "/tmp/ofdbquery.html"
256                         fetchurl = "http://www.ofdb.de/view.php?page=suchergebnis&Kat=DTitel&SText=" + self.eventName
257                         print "[OFDb] Downloading Query " + fetchurl + " to " + localfile
258                         downloadPage(fetchurl,localfile).addCallback(self.OFDBquery).addErrback(self.fetchFailed)
259                 else:
260                         self["statusbar"].setText("Could't get Eventname -_-")
261
262         def fetchFailed(self,string):
263                 print "[OFDb] fetch failed " + string
264                 self["statusbar"].setText("OFDb Download failed -_-")
265
266         def html2utf8(self,in_html):
267                 htmlentitynumbermask = re.compile('(&#(\d{1,5}?);)')
268                 htmlentitynamemask = re.compile('(&(\D{1,5}?);)')
269                 
270                 entities = htmlentitynamemask.finditer(in_html)
271                 entitydict = {}
272
273                 for x in entities:
274                         entitydict[x.group(1)] = x.group(2)
275
276                 for key, name in entitydict.items():
277                         entitydict[key] = htmlentitydefs.name2codepoint[name]
278
279                 entities = htmlentitynumbermask.finditer(in_html)
280
281                 for x in entities:
282                         entitydict[x.group(1)] = x.group(2)
283
284                 for key, codepoint in entitydict.items():
285                         in_html = in_html.replace(key, (unichr(int(codepoint)).encode('utf8')))
286
287                 self.inhtml = in_html
288
289         def OFDBquery(self,string):
290                 print "[OFDBquery]"
291                 self["statusbar"].setText("OFDb Download completed")
292
293                 self.html2utf8(open("/tmp/ofdbquery.html", "r").read())
294
295                 self.generalinfos = self.generalinfomask.search(self.inhtml)
296
297                 if self.generalinfos:
298                         self.OFDBparse()
299                 else:
300                         if re.search("<title>OFDb - Suchergebnis</title>", self.inhtml):
301                                 searchresultmask = re.compile("<br>(\d{1,3}\.) <a href=\"film/(.*?)\"(?:.*?)\)\">(.*?)</a>", re.DOTALL)
302                                 searchresults = searchresultmask.finditer(self.inhtml)
303                                 self.resultlist = []
304                                 if searchresults:
305                                         for x in searchresults:
306                                                 self.resultlist.append((self.htmltags.sub('',x.group(3)), x.group(2)))
307                                         self["menu"].l.setList(self.resultlist)
308                                 if len(self.resultlist) == 1:
309                                         self.Page = 0
310                                         self["extralabel"].hide()
311                                         self.showDetails()
312                                 elif len(self.resultlist) > 1:
313                                         self.Page = 1
314                                         self.showMenu()
315                                 else:
316                                         self["detailslabel"].setText(self._("No OFDb match."))
317                                         self["statusbar"].setText("No OFDb match")
318                         else:
319                                 self["detailslabel"].setText(self._("OFDb query failed!"))
320
321         def OFDBquery2(self,string):
322                 self["statusbar"].setText("OFDb Re-Download completed")
323                 self.html2utf8(open("/tmp/ofdbquery2.html", "r").read())
324                 self.generalinfos = self.generalinfomask.search(self.inhtml)
325                 self.OFDBparse()
326
327         def OFDBparse(self):
328                 print "[OFDBparse]"
329                 self.Page = 1
330                 Detailstext = self._("No details found.")
331                 if self.generalinfos:
332                         self["key_yellow"].setText(self._("Details"))
333                         self["statusbar"].setText("OFDb Details parsed ^^")
334                         
335                         Titeltext = self.generalinfos.group("title")
336                         if len(Titeltext) > 57:
337                                 Titeltext = Titeltext[0:54] + "..."
338                         self["titellabel"].setText(Titeltext)
339
340                         Detailstext = ""
341
342                         genreblockmask = re.compile('Genre\(s\):(?:[\s\S]*?)class=\"Daten\">(.*?)</tr>', re.DOTALL)
343                         genreblock = genreblockmask.findall(self.inhtml)
344                         genremask = re.compile('\">(.*?)</a')
345                         if genreblock:
346                                 genres = genremask.finditer(genreblock[0])
347                                 if genres:
348                                         Detailstext += "Genre: "
349                                         for x in genres:
350                                                 Detailstext += self.htmltags.sub('', x.group(1)) + " "
351
352                         detailscategories = ["director", "year", "country", "original"]
353
354                         for category in detailscategories:
355                                 if self.generalinfos.group('g_'+category):
356                                         Detailstext += "\n" + self.generalinfos.group('g_'+category) + ": " + self.htmltags.sub('', self.generalinfos.group(category).replace("<br>",' '))
357
358                         self["detailslabel"].setText(Detailstext)
359
360                         #if self.generalinfos.group("alternativ"):
361                                 #Detailstext += "\n" + self.generalinfos.group("g_alternativ") + ": " + self.htmltags.sub('',(self.generalinfos.group("alternativ").replace('\n','').replace("<br>",'\n').replace("  ",' ')))
362
363                         ratingmask = re.compile('<td>[\s\S]*notenskala.*(?P<g_rating>Note: )(?P<rating>\d.\d{2,2})[\s\S]*</td>', re.DOTALL)
364                         rating = ratingmask.search(self.inhtml)
365                         Ratingtext = self._("no user rating yet")
366                         if rating:
367                                 Ratingtext = rating.group("g_rating") + rating.group("rating") + " / 10"
368                                 self.ratingstars = int(10*round(float(rating.group("rating")),1))
369                                 self["stars"].show()
370                                 self["stars"].setValue(self.ratingstars)
371                                 self["starsbg"].show()
372                         self["ratinglabel"].setText(Ratingtext)
373
374                         castblockmask = re.compile('Darsteller:[\s\S]*?class=\"Daten\">(.*?)(?:\.\.\.|\xbb)', re.DOTALL)
375                         castblock = castblockmask.findall(self.inhtml)
376                         castmask = re.compile('\">(.*?)</a')
377                         Casttext = ""
378                         if castblock:
379                                 cast = castmask.finditer(castblock[0])
380                                 if cast:
381                                         for x in cast:
382                                                 Casttext += "\n" + self.htmltags.sub('', x.group(1))
383                                         if Casttext is not "":
384                                                 Casttext = self._("Cast: ") + Casttext
385                                         else:
386                                                 Casttext = self._("No cast list found in the database.")
387                                         self["castlabel"].setText(Casttext)
388
389                         postermask = re.compile('<img src=\"(http://img.ofdb.de/film.*?)\" alt', re.DOTALL)
390                         posterurl = postermask.search(self.inhtml)
391                         if posterurl and posterurl.group(1).find("jpg") > 0:
392                                 posterurl = posterurl.group(1)
393                                 self["statusbar"].setText("Downloading Movie Poster: "+posterurl+"...")
394                                 localfile = "/tmp/poster.jpg"
395                                 print "[OFDb] downloading poster " + posterurl + " to " + localfile
396                                 downloadPage(posterurl,localfile).addCallback(self.OFDBPoster).addErrback(self.fetchFailed)
397                         else:
398                                 print "no jpg poster!"
399                                 self.OFDBPoster("kein Poster")
400
401                 self["detailslabel"].setText(Detailstext)
402                 
403         def OFDBPoster(self,string):
404                 self["statusbar"].setText("OFDb Details parsed ^^")
405                 if not string:
406                         filename = "/tmp/poster.jpg"
407                 else:
408                         filename = resolveFilename(SCOPE_PLUGINS, "Extensions/OFDb/no_poster.png")
409                 sc = AVSwitch().getFramebufferScale()
410                 self.picload.setPara((self["poster"].instance.size().width(), self["poster"].instance.size().height(), sc[0], sc[1], False, 1, "#00000000"))
411                 self.picload.startDecode(filename)
412
413         def paintPosterPixmapCB(self, picInfo=None):
414                 ptr = self.picload.getData()
415                 if ptr != None:
416                         self["poster"].instance.setPixmap(ptr.__deref__())
417                         self["poster"].show()
418
419         def createSummary(self):
420                 return OFDbLCDScreen
421
422 class OFDbLCDScreen(Screen):
423         skin = """
424         <screen position="0,0" size="132,64" title="OFDb Plugin">
425                 <widget name="headline" position="4,0" size="128,22" font="Regular;20"/>
426                 <widget source="session.Event_Now" render="Label" position="6,26" size="120,34" font="Regular;14" >
427                         <convert type="EventName">Name</convert>
428                 </widget>
429         </screen>"""
430
431         def __init__(self, session, parent):
432                 Screen.__init__(self, session)
433                 self["headline"] = Label("OFDb Plugin")
434
435 def eventinfo(session, servicelist, **kwargs):
436         ref = session.nav.getCurrentlyPlayingServiceReference()
437         session.open(OFDBEPGSelection, ref)
438
439 def main(session, eventName="", **kwargs):
440         session.open(OFDB, eventName)
441         
442 def Plugins(**kwargs):
443         try:
444                 return [PluginDescriptor(name = "OFDb Details",
445                                 description = _("Query details from the Online-Filmdatenbank"),
446                                 icon = "ofdb.png",
447                                 where = PluginDescriptor.WHERE_PLUGINMENU,
448                                 fnc = main),
449                                 PluginDescriptor(name = "OFDb Details",
450                                 description = _("Query details from the Online-Filmdatenbank"),
451                                 where = PluginDescriptor.WHERE_EVENTINFO,
452                                 fnc = eventinfo)
453                                 ]
454         except AttributeError:
455                 wherelist = [PluginDescriptor.WHERE_EXTENSIONSMENU, PluginDescriptor.WHERE_PLUGINMENU]
456                 return PluginDescriptor(name="OFDb Details",
457                                 description=_("Query details from the Online-Filmdatenbank"),
458                                 icon="ofdb.png",
459                                 where = wherelist,
460                                 fnc=main)