1 # -*- coding: UTF-8 -*-
2 from Plugins.Plugin import PluginDescriptor
3 from twisted.web.client import downloadPage
4 from enigma import ePicLoad, eServiceReference
5 from Screens.Screen import Screen
6 from Screens.EpgSelection import EPGSelection
7 from Screens.ChannelSelection import SimpleChannelSelection
8 from Components.ActionMap import ActionMap
9 from Components.Pixmap import Pixmap
10 from Components.Label import Label
11 from Components.ScrollLabel import ScrollLabel
12 from Components.Button import Button
13 from Components.AVSwitch import AVSwitch
14 from Components.MenuList import MenuList
15 from Components.Language import language
16 from Components.ProgressBar import ProgressBar
17 from Tools.Directories import resolveFilename, SCOPE_PLUGINS, SCOPE_SKIN_IMAGE
18 from os import environ as os_environ
25 lang = language.getLanguage()[:2] # getLanguage returns e.g. "fi_FI" for "language_country"
26 os_environ["LANGUAGE"] = lang # Enigma doesn't set this (or LC_ALL, LC_MESSAGES, LANG). gettext needs it!
27 gettext.bindtextdomain("IMDb", resolveFilename(SCOPE_PLUGINS, "Extensions/IMDb/locale"))
30 t = gettext.dgettext("IMDb", txt)
32 print "[IMDb] fallback to default translation for", txt
33 t = gettext.gettext(txt)
37 language.addCallback(localeInit)
39 class OFDBChannelSelection(SimpleChannelSelection):
40 def __init__(self, session):
41 SimpleChannelSelection.__init__(self, session, _("Channel Selection"))
42 self.skinName = "SimpleChannelSelection"
44 self["ChannelSelectEPGActions"] = ActionMap(["ChannelSelectEPGActions"],
46 "showEPGList": self.channelSelected
50 def channelSelected(self):
51 ref = self.getCurrentSelection()
52 if (ref.flags & 7) == 7:
54 elif not (ref.flags & eServiceReference.isMarker):
55 self.session.openWithCallback(
62 def epgClosed(self, ret = None):
66 class OFDBEPGSelection(EPGSelection):
67 def __init__(self, session, ref, openPlugin = True):
68 EPGSelection.__init__(self, session, ref)
69 self.skinName = "EPGSelection"
70 self["key_green"].setText(_("Lookup"))
71 self.openPlugin = openPlugin
73 def infoKeyPressed(self):
77 cur = self["list"].getCurrent()
89 self.close(evt.getEventName())
91 def onSelectionChanged(self):
96 <screen name="OFDb" position="90,95" size="560,420" title="Online-Filmdatenbank Details Plugin" >
97 <ePixmap pixmap="skin_default/buttons/red.png" position="0,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
98 <ePixmap pixmap="skin_default/buttons/green.png" position="140,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
99 <ePixmap pixmap="skin_default/buttons/yellow.png" position="280,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
100 <ePixmap pixmap="skin_default/buttons/blue.png" position="420,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
101 <widget name="key_red" position="0,0" zPosition="1" size="140,40" font="Regular;20" valign="center" halign="center" backgroundColor="#9f1313" transparent="1" />
102 <widget name="key_green" position="140,0" zPosition="1" size="140,40" font="Regular;20" valign="center" halign="center" backgroundColor="#1f771f" transparent="1" />
103 <widget name="key_yellow" position="280,0" zPosition="1" size="140,40" font="Regular;20" valign="center" halign="center" backgroundColor="#a08500" transparent="1" />
104 <widget name="key_blue" position="420,0" zPosition="1" size="140,40" font="Regular;20" valign="center" halign="center" backgroundColor="#18188b" transparent="1" />
105 <widget name="titellabel" position="10,40" size="330,45" valign="center" font="Regular;22"/>
106 <widget name="detailslabel" position="105,90" size="445,140" font="Regular;18" />
107 <widget name="castlabel" position="10,235" size="540,155" font="Regular;18" />
108 <widget name="extralabel" position="10,40" size="540,350" font="Regular;18" />
109 <widget name="ratinglabel" position="340,62" size="210,20" halign="center" font="Regular;18" foregroundColor="#f0b400"/>
110 <widget name="statusbar" position="10,404" size="540,16" font="Regular;16" foregroundColor="#cccccc" />
111 <widget name="poster" position="4,90" size="96,140" alphatest="on" />
112 <widget name="menu" position="10,115" size="540,275" zPosition="3" scrollbarMode="showOnDemand" />
113 <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" />
114 <widget name="stars" position="340,40" size="210,21" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/OFDb/starsbar_filled.png" transparent="1" />
117 def __init__(self, session, eventName, args = None):
118 self.skin = OFDB.skin
119 Screen.__init__(self, session)
120 self.eventName = eventName
121 self.dictionary_init()
122 self["poster"] = Pixmap()
123 self.picload = ePicLoad()
124 self.picload.PictureData.get().append(self.paintPosterPixmapCB)
126 self["stars"] = ProgressBar()
127 self["starsbg"] = Pixmap()
129 self["starsbg"].hide()
130 self.ratingstars = -1
131 self["titellabel"] = Label(_("The Online-Filmdatenbank"))
132 self["detailslabel"] = ScrollLabel("")
133 self["castlabel"] = ScrollLabel("")
134 self["extralabel"] = ScrollLabel("")
135 self["statusbar"] = Label("")
136 self["ratinglabel"] = Label("")
138 self["menu"] = MenuList(self.resultlist)
140 self["key_red"] = Button(_("Exit"))
141 self["key_green"] = Button("")
142 self["key_yellow"] = Button("")
143 self["key_blue"] = Button("")
144 # 0 = multiple query selection menu page
145 # 1 = movie info page
146 # 2 = extra infos page
149 self["actions"] = ActionMap(["OkCancelActions", "ColorActions", "MovieSelectionActions", "DirectionActions"],
151 "ok": self.showDetails,
152 "cancel": self.close,
153 "down": self.pageDown,
156 "green": self.showMenu,
157 "yellow": self.showDetails,
158 "blue": self.showExtras,
159 "contextMenu": self.openChannelSelection,
160 "showEventInfo": self.showDetails
165 def dictionary_init(self):
166 syslang = language.getLanguage()
167 if "de" not in syslang:
168 self.OFDBlanguage = "" # set to empty ("") for english version
170 self.OFDBlanguage = "german." # it's a subdomain, so add a '.' at the end
172 self.htmltags = re.compile('<.*?>')
174 self.generalinfomask = re.compile(
175 '<title>OFDb - (?P<title>.*?)</title>.*?'
176 '(?P<g_original>Originaltitel):[\s\S]*?class=\"Daten\">(?P<original>.*?)</td>'
177 '(?:.*?(?P<g_country>Herstellungsland):[\s\S]*?class="Daten">(?P<country>.*?)(?:\.\.\.|</td>))*'
178 '(?:.*?(?P<g_year>Erscheinungsjahr):[\s\S]*?class="Daten">(?P<year>.*?)</td>)*'
179 '(?:.*?(?P<g_director>Regie):[\s\S]*?class="Daten">(?P<director>.*?)(?:\.\.\.|</td>))*'
182 def resetLabels(self):
183 self["detailslabel"].setText("")
184 self["ratinglabel"].setText("")
185 self["titellabel"].setText("")
186 self["castlabel"].setText("")
187 self["titellabel"].setText("")
188 self["extralabel"].setText("")
189 self.ratingstars = -1
193 self["menu"].instance.moveSelection(self["menu"].instance.moveUp)
195 self["castlabel"].pageUp()
196 self["detailslabel"].pageUp()
198 self["extralabel"].pageUp()
202 self["menu"].instance.moveSelection(self["menu"].instance.moveDown)
204 self["castlabel"].pageDown()
205 self["detailslabel"].pageDown()
207 self["extralabel"].pageDown()
210 if ( self.Page is 1 or self.Page is 2 ) and self.resultlist:
213 self["starsbg"].hide()
214 self["ratinglabel"].hide()
215 self["castlabel"].hide()
216 self["poster"].hide()
217 self["extralabel"].hide()
218 self["titellabel"].setText(_("Ambiguous results"))
219 self["detailslabel"].setText(_("Please select the matching entry"))
220 self["detailslabel"].show()
221 self["key_blue"].setText("")
222 self["key_green"].setText(_("Title Menu"))
223 self["key_yellow"].setText(_("Details"))
226 def showDetails(self):
227 self["ratinglabel"].show()
228 self["castlabel"].show()
229 self["detailslabel"].show()
231 if self.resultlist and self.Page == 0:
232 link = self["menu"].getCurrent()[1]
233 title = self["menu"].getCurrent()[0]
234 self["statusbar"].setText(_("Re-Query OFDb: %s...") % (title))
235 localfile = "/tmp/ofdbquery2.html"
236 fetchurl = "http://www.ofdb.de/film/" + link
237 print "[OFDb] downloading query " + fetchurl + " to " + localfile
238 downloadPage(fetchurl,localfile).addCallback(self.OFDBquery2).addErrback(self.fetchFailed)
244 self["extralabel"].hide()
245 self["poster"].show()
246 if self.ratingstars > 0:
247 self["starsbg"].show()
249 self["stars"].setValue(self.ratingstars)
253 def showExtras(self):
255 self["extralabel"].show()
256 self["detailslabel"].hide()
257 self["castlabel"].hide()
258 self["poster"].hide()
260 self["starsbg"].hide()
261 self["ratinglabel"].hide()
264 def openChannelSelection(self):
265 self.session.openWithCallback(
266 self.channelSelectionClosed,
270 def channelSelectionClosed(self, ret = None):
277 if self.eventName is "":
278 s = self.session.nav.getCurrentService()
280 event = info.getEvent(0) # 0 = now, 1 = next
282 self.eventName = event.getEventName()
284 if self.eventName is not "":
286 pos = self.eventName.index(" (")
287 self.eventName=self.eventName[0:pos]
290 if self.eventName[-3:] == "...":
291 self.eventName = self.eventName[:-3]
292 for article in ["The", "Der", "Die", "Das"]:
293 if self.eventName[:4].capitalize() == article + " ":
294 self.eventName = self.eventName[4:] + ", " + article
296 self["statusbar"].setText(_("Query OFDb: %s...") % (self.eventName))
298 self.eventName = urllib.quote(self.eventName)
300 self.eventName = urllib.quote(self.eventName.decode('utf8').encode('ascii','ignore'))
301 localfile = "/tmp/ofdbquery.html"
302 fetchurl = "http://www.ofdb.de/view.php?page=suchergebnis&Kat=DTitel&SText=" + self.eventName
303 print "[OFDb] Downloading Query " + fetchurl + " to " + localfile
304 downloadPage(fetchurl,localfile).addCallback(self.OFDBquery).addErrback(self.fetchFailed)
306 self["statusbar"].setText(_("Could't get Eventname"))
308 def fetchFailed(self,string):
309 print "[OFDb] fetch failed " + string
310 self["statusbar"].setText(_("OFDb Download failed"))
312 def html2utf8(self,in_html):
313 htmlentitynumbermask = re.compile('(&#(\d{1,5}?);)')
314 htmlentitynamemask = re.compile('(&(\D{1,5}?);)')
316 entities = htmlentitynamemask.finditer(in_html)
320 entitydict[x.group(1)] = x.group(2)
322 for key, name in entitydict.items():
323 entitydict[key] = htmlentitydefs.name2codepoint[name]
325 entities = htmlentitynumbermask.finditer(in_html)
328 entitydict[x.group(1)] = x.group(2)
330 for key, codepoint in entitydict.items():
331 in_html = in_html.replace(key, (unichr(int(codepoint)).encode('utf8')))
333 self.inhtml = in_html
335 def OFDBquery(self,string):
337 self["statusbar"].setText(_("OFDb Download completed"))
339 self.html2utf8(open("/tmp/ofdbquery.html", "r").read())
341 self.generalinfos = self.generalinfomask.search(self.inhtml)
343 if self.generalinfos:
346 if re.search("<title>OFDb - Suchergebnis</title>", self.inhtml):
347 searchresultmask = re.compile("<br>(\d{1,3}\.) <a href=\"film/(.*?)\"(?:.*?)\)\">(.*?)</a>", re.DOTALL)
348 searchresults = searchresultmask.finditer(self.inhtml)
351 for x in searchresults:
352 self.resultlist.append((self.htmltags.sub('',x.group(3)), x.group(2)))
353 self["menu"].l.setList(self.resultlist)
354 if len(self.resultlist) == 1:
356 self["extralabel"].hide()
358 elif len(self.resultlist) > 1:
362 self["detailslabel"].setText(_("No OFDb match."))
363 self["statusbar"].setText(_("No OFDb match."))
365 self["detailslabel"].setText(_("OFDb query failed!"))
367 def OFDBquery2(self,string):
368 self["statusbar"].setText(_("OFDb Re-Download completed"))
369 self.html2utf8(open("/tmp/ofdbquery2.html", "r").read())
370 self.generalinfos = self.generalinfomask.search(self.inhtml)
376 Detailstext = _("No details found.")
377 if self.generalinfos:
378 self["key_yellow"].setText(_("Details"))
379 self["statusbar"].setText(_("OFDb Details parsed"))
381 Titeltext = self.generalinfos.group("title")
382 if len(Titeltext) > 57:
383 Titeltext = Titeltext[0:54] + "..."
384 self["titellabel"].setText(Titeltext)
388 genreblockmask = re.compile('Genre\(s\):(?:[\s\S]*?)class=\"Daten\">(.*?)</tr>', re.DOTALL)
389 genreblock = genreblockmask.findall(self.inhtml)
390 genremask = re.compile('\">(.*?)</a')
392 genres = genremask.finditer(genreblock[0])
394 Detailstext += "Genre: "
396 Detailstext += self.htmltags.sub('', x.group(1)) + " "
398 for category in ("director", "year", "country", "original"):
399 if self.generalinfos.group('g_'+category):
400 Detailstext += "\n" + self.generalinfos.group('g_'+category) + ": " + self.htmltags.sub('', self.generalinfos.group(category).replace("<br>",' '))
402 self["detailslabel"].setText(Detailstext)
404 #if self.generalinfos.group("alternativ"):
405 #Detailstext += "\n" + self.generalinfos.group("g_alternativ") + ": " + self.htmltags.sub('',(self.generalinfos.group("alternativ").replace('\n','').replace("<br>",'\n').replace(" ",' ')))
407 ratingmask = re.compile('<td>[\s\S]*notenskala.*(?P<g_rating>Note: )(?P<rating>\d.\d{2,2})[\s\S]*</td>', re.DOTALL)
408 rating = ratingmask.search(self.inhtml)
409 Ratingtext = _("no user rating yet")
411 Ratingtext = rating.group("g_rating") + rating.group("rating") + " / 10"
412 self.ratingstars = int(10*round(float(rating.group("rating")),1))
414 self["stars"].setValue(self.ratingstars)
415 self["starsbg"].show()
416 self["ratinglabel"].setText(Ratingtext)
418 castblockmask = re.compile('Darsteller:[\s\S]*?class=\"Daten\">(.*?)(?:\.\.\.|\xbb)', re.DOTALL)
419 castblock = castblockmask.findall(self.inhtml)
420 castmask = re.compile('\">(.*?)</a')
423 cast = castmask.finditer(castblock[0])
426 Casttext += "\n" + self.htmltags.sub('', x.group(1))
427 if Casttext is not "":
428 Casttext = _("Cast: ") + Casttext
430 Casttext = _("No cast list found in the database.")
431 self["castlabel"].setText(Casttext)
433 postermask = re.compile('<img src=\"(http://img.ofdb.de/film.*?)\" alt', re.DOTALL)
434 posterurl = postermask.search(self.inhtml)
435 if posterurl and posterurl.group(1).find("jpg") > 0:
436 posterurl = posterurl.group(1)
437 self["statusbar"].setText(_("Downloading Movie Poster: %s...") % (posterurl))
438 localfile = "/tmp/poster.jpg"
439 print "[OFDb] downloading poster " + posterurl + " to " + localfile
440 downloadPage(posterurl,localfile).addCallback(self.OFDBPoster).addErrback(self.fetchFailed)
442 print "no jpg poster!"
443 self.OFDBPoster(noPoster = True)
445 self["detailslabel"].setText(Detailstext)
447 def OFDBPoster(self, noPoster = False):
448 self["statusbar"].setText(_("OFDb Details parsed"))
450 filename = "/tmp/poster.jpg"
452 filename = resolveFilename(SCOPE_PLUGINS, "Extensions/OFDb/no_poster.png")
453 sc = AVSwitch().getFramebufferScale()
454 self.picload.setPara((self["poster"].instance.size().width(), self["poster"].instance.size().height(), sc[0], sc[1], False, 1, "#00000000"))
455 self.picload.startDecode(filename)
457 def paintPosterPixmapCB(self, picInfo=None):
458 ptr = self.picload.getData()
460 self["poster"].instance.setPixmap(ptr.__deref__())
461 self["poster"].show()
463 def createSummary(self):
466 class OFDbLCDScreen(Screen):
468 <screen position="0,0" size="132,64" title="OFDb Plugin">
469 <widget name="headline" position="4,0" size="128,22" font="Regular;20"/>
470 <widget source="session.Event_Now" render="Label" position="6,26" size="120,34" font="Regular;14" >
471 <convert type="EventName">Name</convert>
475 def __init__(self, session, parent):
476 Screen.__init__(self, session)
477 self["headline"] = Label(_("OFDb Plugin"))
479 def eventinfo(session, servicelist, **kwargs):
480 ref = session.nav.getCurrentlyPlayingServiceReference()
481 session.open(OFDBEPGSelection, ref)
483 def main(session, eventName="", **kwargs):
484 session.open(OFDB, eventName)
486 def Plugins(**kwargs):
488 return [PluginDescriptor(name = "OFDb Details",
489 description = _("Query details from the Online-Filmdatenbank"),
491 where = PluginDescriptor.WHERE_PLUGINMENU,
493 PluginDescriptor(name = "OFDb Details",
494 description = _("Query details from the Online-Filmdatenbank"),
495 where = PluginDescriptor.WHERE_EVENTINFO,
498 except AttributeError:
499 wherelist = [PluginDescriptor.WHERE_EXTENSIONSMENU, PluginDescriptor.WHERE_PLUGINMENU]
500 return PluginDescriptor(name="OFDb Details",
501 description=_("Query details from the Online-Filmdatenbank"),