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