add depends in control & change description to english
[enigma2-plugins.git] / imdb / 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 IMDB(Screen):
20         skin = """
21                 <screen name="IMDB" position="90,95" size="560,420" title="Internet Movie Database Details Plugin" >
22                         <ePixmap pixmap="skin_default/key-red.png" position="0,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
23                         <ePixmap pixmap="skin_default/key-green.png" position="140,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
24                         <ePixmap pixmap="skin_default/key-yellow.png" position="280,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
25                         <ePixmap pixmap="skin_default/key-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/IMDb/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/IMDb/starsbar_filled.png" transparent="1" />
40                 </screen>"""
41         
42         def __init__(self, session, eventName, args = None):
43                 self.skin = IMDB.skin
44                 Screen.__init__(self, session)
45                 
46                 self.eventName = eventName
47                 
48                 self.dictionary_init()
49
50                 self["poster"] = Pixmap()
51                 self.preview = Pixmap()
52                 
53                 self["stars"] = ProgressBar()
54                 self["starsbg"] = Pixmap()
55                 self["stars"].hide()
56                 self["starsbg"].hide()
57                 self.ratingstars = -1
58         
59                 self["titellabel"] = Label("The Internet Movie Database")
60                 self["detailslabel"] = ScrollLabel("")
61                 self["castlabel"] = ScrollLabel("")
62                 self["extralabel"] = ScrollLabel("")
63                 self["statusbar"] = Label("")
64                 self["ratinglabel"] = Label("")
65                 self.resultlist = []
66                 self["menu"] = MenuList(self.resultlist)
67                 self["menu"].hide()
68                 
69                 self["key_red"] = Button(self._("Exit"))
70                 self["key_green"] = Button("")
71                 self["key_yellow"] = Button("")
72                 self["key_blue"] = Button("")
73                 
74                 # 0 = multiple query selection menu page
75                 # 1 = movie info page
76                 # 2 = extra infos page
77                 self.Page = 0
78                                 
79                 self["actions"] = ActionMap(["OkCancelActions", "ColorActions", "MovieSelectionActions", "DirectionActions"],
80                 {
81                         "ok": self.showDetails,
82                         "cancel": self.close,
83                         "down": self.pageDown,
84                         "up": self.pageUp,
85                         "red": self.close,
86                         "green": self.showMenu,
87                         "yellow": self.showDetails,
88                         "blue": self.showExtras,
89                         "showEventInfo": self.showDetails
90                 }, -1)
91                 
92                 self.getIMDB()
93                 
94         def dictionary_init(self):
95                 syslang = language.getLanguage()
96                 if syslang.find("de") is -1:
97                         self.IMDBlanguage = ""  # set to empty ("") for english version
98                 else:
99                         self.IMDBlanguage = "german." # it's a subdomain, so add a '.' at the end
100                 
101                 self.dict = {}
102                 self.dict["of"]="von"
103                 self.dict[" as "]=" als "
104                 self.dict["Ambiguous results"]="Kein eindeutiger Treffer"
105                 self.dict["Please select the matching entry"]="Bitte passenden Eintrag auswählen"
106                 self.dict["No IMDb match."]="Keine passenden Einträge gefunden."
107                 self.dict["IMDb query failed!"]="IMDb-Query fehlgeschlagen!"
108                 self.dict["No details found."]="Keine Details gefunden."
109                 self.dict["no user rating yet"]="noch keine Nutzerwertung"
110                 self.dict["Cast: "]="Darsteller: "
111                 self.dict["No cast list found in the database."]="Keine Darstellerliste in der Datenbank gefunden."
112                 self.dict["Exit"]="Beenden"
113                 self.dict["Extra Info"]="Zusatzinfos"
114                 self.dict["Title Menu"]="Titelauswahl"
115                 
116                 self.htmltags = re.compile('<.*?>')
117                 
118                 self.generalinfomask = re.compile(
119                 '<h1>(?P<title>.*?) <.*?</h1>.*?'
120                 '(?:.*?<h5>(?P<g_director>Regisseur|Directors?):</h5>.*?>(?P<director>.*?)</a>)*'
121                 '(?:.*?<h5>(?P<g_creator>Sch\S*?pfer|Creators?):</h5>.*?>(?P<creator>.*?)</a>)*'
122                 '(?:.*?<h5>(?P<g_seasons>Seasons):</h5>(?:.*?)<a href=\".*?\">(?P<seasons>\d+?)</a>\s+?(?:<a class|\|\s+?<a href="episodes#season-unknown))*'
123                 '(?:.*?<h5>(?P<g_writer>Drehbuch|Writer).*?</h5>.*?>(?P<writer>.*?)</a>)*'
124                 '(?:.*?<h5>(?P<g_premiere>Premiere|Release Date).*?</h5>\s.*?\n?(?P<premiere>.*?)\n\s.*?<)*'
125                 '(?:.*?<h5>(?P<g_alternativ>Alternativ|Also Known As):</h5>(?P<alternativ>.*?)<br>\s{0,8}<a.*?>(?:mehr|more))*'
126                 '(?:.*?<h5>(?P<g_country>Produktionsland|Country):</h5>.*?<a.*?>(?P<country>.*?)</a>(?:.*?mehr|\n</div>))*'
127                 , re.DOTALL)
128                 
129                 self.extrainfomask = re.compile(
130                 '(?:.*?<h5>(?P<g_tagline>Werbezeile|Tagline?):</h5>\n(?P<tagline>.+?)<)*'
131                 '(?:.*?<h5>(?P<g_outline>Kurzbeschreibung|Plot Outline):</h5>(?P<outline>.+?)<)*'
132                 '(?:.*?<h5>(?P<g_synopsis>Plot Synopsis):</h5>(?:.*?)(?:<a href=\".*?\">)*?(?P<synopsis>.+?)(?:</a>|</div>))*'
133                 '(?:.*?<h5>(?P<g_keywords>Plot Keywords):</h5>(?P<keywords>.+?)(?:mehr|more</a>|</div>))*'
134                 '(?:.*?<h5>(?P<g_awards>Filmpreise|Awards):</h5>(?P<awards>.+?)(?:mehr|more</a>|</div>))*'
135                 '(?:.*?<h5>(?P<g_runtime>L\S*?nge|Runtime):</h5>(?P<runtime>.+?)<)*'
136                 '(?:.*?<h5>(?P<g_language>Sprache|Language):</h5>(?P<language>.+?)</div>)*'
137                 '(?:.*?<h5>(?P<g_color>Farbe|Color):</h5>(?P<color>.+?)</div>)*'
138                 '(?:.*?<h5>(?P<g_aspect>Seitenverh\S*?ltnis|Aspect Ratio):</h5>(?P<aspect>.+?)(?:mehr|more</a>|</div>))*'
139                 '(?:.*?<h5>(?P<g_sound>Tonverfahren|Sound Mix):</h5>(?P<sound>.+?)</div>)*'
140                 '(?:.*?<h5>(?P<g_cert>Altersfreigabe|Certification):</h5>(?P<cert>.+?)</div>)*'
141                 '(?:.*?<h5>(?P<g_locations>Drehorte|Filming Locations):</h5>(?P<locations>.+?)(?:mehr|more</a>|</div>))*'
142                 '(?:.*?<h5>(?P<g_company>Firma|Company):</h5>(?P<company>.+?)(?:mehr|more</a>|</div>))*'
143                 '(?:.*?<h5>(?P<g_trivia>Dies und das|Trivia):</h5>(?P<trivia>.+?)(?:mehr|more</a>|</div>))*'
144                 '(?:.*?<h5>(?P<g_goofs>Pannen|Goofs):</h5>(?P<goofs>.+?)(?:mehr|more</a>|</div>))*'
145                 '(?:.*?<h5>(?P<g_quotes>Dialogzitate|Quotes):</h5>(?P<quotes>.+?)(?:mehr|more</a>|</div>))*'
146                 '(?:.*?<h5>(?P<g_connections>Bez\S*?ge zu anderen Titeln|Movie Connections):</h5>(?P<connections>.+?)(?:mehr|more</a>|</div>))*'
147                 '(?:.*?<h3>(?P<g_comments>Nutzerkommentare|User Comments)</h3>.*?<a href="/user/ur\d{7,7}/comments">(?P<commenter>.+?)\n</div>.*?<p>(?P<comment>.+?)</p>)*'
148                 , re.DOTALL)
149
150         def _(self, in_string):
151                 out_string = in_string
152                 if ((self.IMDBlanguage).find("german")) != -1:
153                         out_string = self.dict.get(in_string, in_string)
154                 return out_string
155                 
156         def resetLabels(self):
157                 self["detailslabel"].setText("")
158                 self["ratinglabel"].setText("")
159                 self["titellabel"].setText("")
160                 self["castlabel"].setText("")
161                 self["titellabel"].setText("")
162                 self["extralabel"].setText("")
163                 self.ratingstars = -1
164
165         def pageUp(self):
166                 if self.Page == 0:
167                         self["menu"].instance.moveSelection(self["menu"].instance.moveUp)
168                 if self.Page == 1:
169                         self["castlabel"].pageUp()
170                         self["detailslabel"].pageUp()
171                 if self.Page == 2:
172                         self["extralabel"].pageUp()
173         
174         def pageDown(self):
175                 if self.Page == 0:
176                         self["menu"].instance.moveSelection(self["menu"].instance.moveDown)
177                 if self.Page == 1:
178                         self["castlabel"].pageDown()
179                         self["detailslabel"].pageDown()
180                 if self.Page == 2:
181                         self["extralabel"].pageDown()
182
183         def showMenu(self):
184                 if ( self.Page is 1 or self.Page is 2 ) and self.resultlist:
185                         self["menu"].show()
186                         self["stars"].hide()
187                         self["starsbg"].hide()
188                         self["ratinglabel"].hide()
189                         self["castlabel"].hide()
190                         self["poster"].hide()
191                         self["extralabel"].hide()
192                         self["titellabel"].setText(self._("Ambiguous results"))
193                         self["detailslabel"].setText(self._("Please select the matching entry"))
194                         self["detailslabel"].show()
195                         self["key_blue"].setText("")
196                         self["key_green"].setText(self._("Title Menu"))
197                         self["key_yellow"].setText(self._("Details"))
198                         self.Page = 0
199                 
200         def showDetails(self):
201                 self["ratinglabel"].show()
202                 self["castlabel"].show()
203                 self["detailslabel"].show()
204                 
205                 if self.resultlist and self.Page == 0:
206                         link = self["menu"].getCurrent()[1]
207                         title = self["menu"].getCurrent()[0]
208                         self["statusbar"].setText("Re-Query IMDb: "+title+"...")
209                         localfile = "/home/root/imdbquery2.html"
210                         fetchurl = "http://" + self.IMDBlanguage + "imdb.com/title/" + link
211                         print "[IMDB] downloading query " + fetchurl + " to " + localfile
212                         downloadPage(fetchurl,localfile).addCallback(self.IMDBquery2).addErrback(self.fetchFailed)
213                         self["menu"].hide()
214                         self.resetLabels()
215                         self.Page = 1
216                         
217                 if self.Page == 2:
218                         self["extralabel"].hide()
219                         self["poster"].show()
220                         if self.ratingstars > 0:
221                                 self["starsbg"].show()
222                                 self["stars"].show()
223                                 self["stars"].setValue(self.ratingstars)
224
225                         self.Page = 1
226                                 
227         def showExtras(self):
228                 if self.Page == 1:
229                         self["extralabel"].show()
230                         self["detailslabel"].hide()
231                         self["castlabel"].hide()
232                         self["poster"].hide()
233                         self["stars"].hide()
234                         self["starsbg"].hide()
235                         self["ratinglabel"].hide()
236                         self.Page = 2
237                 
238         def getIMDB(self):
239                 self.resetLabels()
240                 if self.eventName is "":
241                         s = self.session.nav.getCurrentService()
242                         info = s.info()
243                         event = info.getEvent(0) # 0 = now, 1 = next
244                         if event:
245                                 self.eventName = event.getEventName()
246                 if self.eventName is not "":
247                         self["statusbar"].setText("Query IMDb: " + self.eventName + "...")
248                         self.eventName = urllib.quote(self.eventName.decode('utf8').encode('latin-1','ignore'))
249                         localfile = "/home/root/imdbquery.html"
250                         fetchurl = "http://" + self.IMDBlanguage + "imdb.com/find?q=" + self.eventName + "&s=tt&site=aka"
251                         print "[IMDB] Downloading Query " + fetchurl + " to " + localfile
252                         downloadPage(fetchurl,localfile).addCallback(self.IMDBquery).addErrback(self.fetchFailed)
253                 else:
254                         self["statusbar"].setText("Could't get Eventname -_-")
255                                 
256         def fetchFailed(self,string):
257                 print "[IMDB] fetch failed " + string
258                 self["statusbar"].setText("IMDb Download failed -_-")
259                 
260         def html2utf8(self,in_html):
261                 htmlentitynumbermask = re.compile('(&#(\d{1,5}?);)')
262                 htmlentitynamemask = re.compile('(&(\D{1,5}?);)')
263                 
264                 entities = htmlentitynamemask.finditer(in_html)
265                 entitydict = {}
266                 
267                 for x in entities:
268                         entitydict[x.group(1)] = x.group(2)
269                 
270                 for key, name in entitydict.items():
271                         entitydict[key] = htmlentitydefs.name2codepoint[name]
272                 
273                 entities = htmlentitynumbermask.finditer(in_html)
274                 
275                 for x in entities:
276                         entitydict[x.group(1)] = x.group(2)
277                 
278                 for key, codepoint in entitydict.items():
279                         in_html = in_html.replace(key, (unichr(int(codepoint)).encode('latin-1')))
280         
281                 self.inhtml = in_html.decode('latin-1').encode('utf8')
282
283         def IMDBquery(self,string):
284                 print "[IMDBquery]"
285                 self["statusbar"].setText("IMDb Download completed")
286                 
287                 self.html2utf8(open("/home/root/imdbquery.html", "r").read())
288                 
289                 self.generalinfos = self.generalinfomask.search(self.inhtml)
290                 
291                 if self.generalinfos:
292                         self.IMDBparse()
293                 else:
294                         if re.search("<title>IMDb.{0,9}Search</title>", self.inhtml):
295                                 searchresultmask = re.compile("href=\".*?/title/(tt\d{7,7})/\">(.*?)</td>", re.DOTALL)
296                                 searchresults = searchresultmask.finditer(self.inhtml)
297                                 self.resultlist = []
298                                 if searchresults:
299                                         for x in searchresults:
300                                                 self.resultlist.append((self.htmltags.sub('',x.group(2)), x.group(1)))
301                                         self["menu"].l.setList(self.resultlist)
302                                 if len(self.resultlist) > 1:
303                                         self.Page = 1
304                                         self.showMenu()
305                                 else:
306                                         self["detailslabel"].setText(self._("No IMDb match."))
307                                         self["statusbar"].setText("No IMDb match")
308                         else:
309                                 self["detailslabel"].setText(self._("IMDb query failed!"))
310                 
311         def IMDBquery2(self,string):
312                 self["statusbar"].setText("IMDb Re-Download completed")
313                 self.html2utf8(open("/home/root/imdbquery2.html", "r").read())
314                 self.generalinfos = self.generalinfomask.search(self.inhtml)
315                 self.IMDBparse()
316                 
317         def IMDBparse(self):
318                 print "[IMDBparse]"
319                 self.Page = 1
320                 Detailstext = self._("No details found.")
321                 if self.generalinfos:
322                         self["key_yellow"].setText(self._("Details"))
323                         self["statusbar"].setText("IMDb Details parsed ^^")
324                         
325                         Titeltext = self.generalinfos.group("title")
326                         if len(Titeltext) > 57:
327                                 Titeltext = Titeltext[0:54] + "..."
328                         self["titellabel"].setText(Titeltext)
329                         
330                         Detailstext = ""
331                         
332                         genreblockmask = re.compile('<h5>Genre:</h5>(.*?)(?:mehr|more|</div>)', re.DOTALL)
333                         genreblock = genreblockmask.findall(self.inhtml)
334                         genremask = re.compile('\">(.*?)</a')
335                         if genreblock:
336                                 genres = genremask.finditer(genreblock[0])
337                                 if genres:
338                                         Detailstext += "Genre: "
339                                         for x in genres:
340                                                 Detailstext += x.group(1) + " "
341                                                 
342                         detailscategories = ["director", "creator", "writer", "premiere", "seasons", "country"]
343                                 
344                         for category in detailscategories:
345                                 if self.generalinfos.group('g_'+category):
346                                         Detailstext += "\n" + self.generalinfos.group('g_'+category) + ": " + self.generalinfos.group(category)
347
348                         if self.generalinfos.group("alternativ"):
349                                 Detailstext += "\n" + self.generalinfos.group("g_alternativ") + ": " + self.htmltags.sub('',(self.generalinfos.group("alternativ").replace('\n','').replace("<br>",'\n').replace("  ",' ')))
350                                 
351                         ratingmask = re.compile('(?P<g_rating>Nutzer-Bewertung|User Rating):</b>.{0,2}<b>(?P<rating>.*?)/10</b>', re.DOTALL)
352                         rating = ratingmask.search(self.inhtml)
353                         Ratingtext = self._("no user rating yet")
354                         if rating:
355                                 Ratingtext = rating.group("g_rating") + ": " + rating.group("rating") + " / 10"
356                                 self.ratingstars = int(10*round(float(rating.group("rating")),1))
357                                 self["stars"].show()
358                                 self["stars"].setValue(self.ratingstars)
359                                 self["starsbg"].show()
360                         self["ratinglabel"].setText(Ratingtext)
361
362                         castmask = re.compile('<td class="nm">.*?>(.*?)</a>.*?<td class="char">(?:<a.*?>)?(.*?)(?:</a>)?</td>', re.DOTALL)
363                         castresult = castmask.finditer(self.inhtml)
364                         if castresult:
365                                 Casttext = ""
366                                 for x in castresult:
367                                         Casttext += "\n" + self.htmltags.sub('', x.group(1))
368                                         if x.group(2):
369                                                 Casttext += self._(" als ") + self.htmltags.sub('', x.group(2).replace('/ ...',''))
370                                 if Casttext is not "":
371                                         Casttext = self._("Cast: ") + Casttext
372                                 else:
373                                         Casttext = self._("No cast list found in the database.")
374                                 self["castlabel"].setText(Casttext)
375                         
376                         postermask = re.compile('<div class="photo">.*?<img .*? src=\"(http.*?)\" .*?>', re.DOTALL)
377                         posterurl = postermask.search(self.inhtml).group(1)
378                         if posterurl.find("jpg") > 0:
379                                 self["statusbar"].setText("Downloading Movie Poster: "+posterurl+"...")
380                                 localfile = "/home/root/poster.jpg"
381                                 print "[IMDB] downloading poster " + posterurl + " to " + localfile
382                                 downloadPage(posterurl,localfile).addCallback(self.IMDBPoster).addErrback(self.fetchFailed)
383                         else:
384                                 self.IMDBPoster("kein Poster")
385
386                         extrainfos = self.extrainfomask.search(self.inhtml)
387                         
388                         if extrainfos:
389                                 Extratext = "Extra Info\n"
390                                 extracategories = ["tagline","outline","synopsis","keywords","awards","runtime","language","color","aspect","sound","cert","locations","company","trivia","goofs","quotes","connections"]
391                                         
392                                 for category in extracategories:
393                                         if extrainfos.group('g_'+category):
394                                                 Extratext += extrainfos.group('g_'+category) + ": " + self.htmltags.sub('',extrainfos.group(category).replace("\n",'').replace("<br>",'\n')) + "\n"
395                                 if extrainfos.group("g_comments"):
396                                         Extratext += extrainfos.group("g_comments") + " [" + self.htmltags.sub('',extrainfos.group("commenter")) + "]: " + self.htmltags.sub('',extrainfos.group("comment").replace("\n",' ')) + "\n"
397         
398                                 self["extralabel"].setText(Extratext)
399                                 self["extralabel"].hide()
400                                 self["key_blue"].setText(self._("Extra Info"))
401                 
402                 self["detailslabel"].setText(Detailstext)
403                 
404         def IMDBPoster(self,string):
405                 self["statusbar"].setText("IMDb Details parsed ^^")
406                 if not string:
407                         filename = "/home/root/poster.jpg"
408                 else:
409                         filename = "/usr/lib/enigma2/python/Plugins/Extensions/IMDb/no_poster.png"
410                 pixmap = loadPic(filename, 96,140, AVSwitch().getAspectRatioSetting()/2,1,0,0)
411                 if pixmap is not None:
412                         self["poster"].instance.setPixmap(pixmap.__deref__())
413                         self["poster"].move(4,90)
414                         self["poster"].show()
415                 
416         def createSummary(self):
417                 return IMDbLCDScreen
418                         
419 class IMDbLCDScreen(Screen):
420         skin = """
421         <screen position="0,0" size="132,64" title="IMDB Plugin">
422                 <widget name="headline" position="4,0" size="128,22" font="Regular;20"/>
423                 <widget source="session.Event_Now" render="Label" position="6,26" size="120,34" font="Regular;14" >
424                         <convert type="EventName">Name</convert>
425                 </widget>
426         </screen>"""
427         
428         def __init__(self, session, parent):
429                 Screen.__init__(self, session)
430                 self["headline"] = Label("IMDB Plugin")
431
432 def main(session, eventName="", **kwargs):
433         session.open(IMDB, eventName)
434         
435 def Plugins(**kwargs):
436         try:
437                 wherelist = [PluginDescriptor.WHERE_EVENTINFO, PluginDescriptor.WHERE_PLUGINMENU]
438                 return PluginDescriptor(name="IMDb Details",
439                                 description=_("Query details from the Internet Movie Database"),
440                                 icon="imdb.png",
441                                 where = wherelist,
442                                 fnc=main)
443         except AttributeError:
444                 wherelist = [PluginDescriptor.WHERE_EXTENSIONSMENU, PluginDescriptor.WHERE_PLUGINMENU]
445                 return PluginDescriptor(name="IMDb Details",
446                                 description=_("Query details from the Internet Movie Database"),
447                                 icon="imdb.png",
448                                 where = wherelist,
449                                 fnc=main)