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