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