1 #####################################################
2 # TVCharts Plugin for Enigma2 Dreamboxes
3 # Coded by Homey (c) 2011
6 # Support: www.i-have-a-dreambox.com
7 #####################################################
8 from Components.About import about
9 from Components.ActionMap import ActionMap
10 from Components.Button import Button
11 from Components.config import config, configfile, getConfigListEntry, ConfigSubsection, ConfigYesNo, ConfigInteger, ConfigSelection
12 from Components.ConfigList import ConfigList, ConfigListScreen
13 from Components.Label import Label
14 from Components.MenuList import MenuList
15 from Components.MultiContent import MultiContentEntryText, MultiContentEntryPixmapAlphaTest
16 from Components.Network import iNetwork
17 from Components.ServiceEventTracker import ServiceEventTracker
18 from Components.Sources.StaticText import StaticText
19 from Components.UsageConfig import preferredTimerPath
20 from Components.Pixmap import Pixmap
21 from RecordTimer import RecordTimer, RecordTimerEntry, parseEvent
22 from ServiceReference import ServiceReference
23 from Screens.EventView import EventViewSimple
24 from Screens.MessageBox import MessageBox
25 from Screens.Screen import Screen
26 from Screens.Setup import SetupSummary
27 from Screens.TimerEntry import TimerEntry
28 from Screens.TimerEdit import TimerSanityConflict
29 from Tools.Directories import fileExists
30 from Tools.HardwareInfo import HardwareInfo
31 from Plugins.Plugin import PluginDescriptor
33 from enigma import eTimer, eEPGCache, loadJPG, loadPNG, loadPic, eListboxPythonMultiContent, gFont, eServiceReference, eServiceCenter, iPlayableService
34 from random import randint
35 from time import gmtime, strftime
36 from twisted.web.client import getPage
37 from xml.dom.minidom import parse, parseString
38 from urllib import urlencode
41 import xml.etree.cElementTree
42 import Screens.Standby
44 ##############################
45 ##### CONFIG SETTINGS #####
46 ##############################
47 config.plugins.tvcharts = ConfigSubsection()
48 config.plugins.tvcharts.enabled = ConfigYesNo(default = True)
49 config.plugins.tvcharts.maxentries = ConfigInteger(default=10, limits=(5, 100))
50 config.plugins.tvcharts.maxtimerentries = ConfigInteger(default=10, limits=(5, 100))
51 config.plugins.tvcharts.submittimers = ConfigYesNo(default = True)
52 config.plugins.tvcharts.bouquetfilter = ConfigYesNo(default = True)
54 ##########################################################
57 #Channellist Menu Entry
58 class ChannelListMenu(MenuList):
59 def __init__(self, list, enableWrapAround=False):
60 MenuList.__init__(self, list, enableWrapAround, eListboxPythonMultiContent)
61 self.l.setFont(0, gFont("Regular", 24))
62 self.l.setFont(1, gFont("Regular", 20))
63 self.l.setFont(2, gFont("Regular", 16))
64 self.l.setItemHeight(76)
66 def ChannelListEntryComponent(type, channelname, serviceref, eventid, eventname, starttime, endtime, usercount, percent):
67 res = [ (serviceref, eventid) ]
70 pixmap = "/usr/share/enigma2/skin_default/picon_default.png"
71 searchPaths = ('/usr/share/enigma2/picon/','/media/cf/picon/','/media/usb/picon/')
73 srefstring = serviceref
74 pos = srefstring.rfind(':')
76 srefstring = srefstring[:pos].rstrip(':').replace(':','_')
77 for path in searchPaths:
78 pngname = path + srefstring + ".png"
79 if fileExists(pngname):
83 if type == "tvcharts":
84 res.append(MultiContentEntryPixmapAlphaTest(pos=(8, 8), size=(100, 60), png=loadPNG(pixmap)))
85 res.append(MultiContentEntryText(pos=(130, 5), size=(480, 30), font=0, text="%s (Viewer: %s)" % (channelname, usercount)))
86 res.append(MultiContentEntryText(pos=(130, 35), size=(480, 25), font=1, text=eventname))
87 elif type == "timercharts":
88 res.append(MultiContentEntryPixmapAlphaTest(pos=(10, 10), size=(100, 60), png=loadPNG(pixmap)))
89 res.append(MultiContentEntryText(pos=(130, 5), size=(480, 28), font=0, text="%s (User: %s)" % (channelname, usercount)))
90 res.append(MultiContentEntryText(pos=(130, 33), size=(480, 25), font=1, text=eventname))
91 res.append(MultiContentEntryText(pos=(130, 57), size=(480, 20), font=2, text="%s Uhr - %s Uhr (%smin)" % (strftime("%d.%m.%Y %H:%M", gmtime(starttime)), strftime("%H:%M", gmtime(endtime)), int((endtime-starttime)/60))))
92 elif type == "moviecharts":
93 res.append(MultiContentEntryPixmapAlphaTest(pos=(8, 8), size=(100, 60), png=loadPNG(pixmap)))
94 res.append(MultiContentEntryText(pos=(130, 5), size=(480, 30), font=0, text=eventname))
95 res.append(MultiContentEntryText(pos=(130, 33), size=(480, 25), font=1, text="Viewer: %s" % (usercount)))
96 res.append(MultiContentEntryText(pos=(130, 57), size=(480, 20), font=2, text="%s Uhr - %s" % (strftime("%d.%m.%Y %H:%M", gmtime(starttime)), channelname)))
100 ##############################
101 ##### TV Charts MAIN #####
102 ##############################
104 class TVChartsMain(Screen):
107 <screen position="center,center" size="620,510" title="TV Charts">
108 <widget name="channellist" position="10,10" zPosition="1" size="600,458" scrollbarMode="showOnDemand" />
109 <widget name="info" position="0,447" zPosition="2" size="620,20" font="Regular;18" noWrap="1" foregroundColor="#ffffff" transparent="1" halign="center" valign="center" />
110 <ePixmap name="red" position="22,470" zPosition="3" size="140,40" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/TVCharts/images/key_red.png" transparent="1" alphatest="on" />
111 <ePixmap name="green" position="167,470" zPosition="3" size="140,40" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/TVCharts/images/key_green.png" transparent="1" alphatest="on" />
112 <ePixmap name="yellow" position="312,470" zPosition="3" size="140,40" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/TVCharts/images/key_yellow.png" transparent="1" alphatest="on" />
113 <ePixmap name="blue" position="457,470" zPosition="3" size="140,40" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/TVCharts/images/key_blue.png" transparent="1" alphatest="on" />
114 <widget name="key_red" position="22,470" zPosition="4" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
115 <widget name="key_green" position="167,470" zPosition="4" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
116 <widget name="key_yellow" position="312,470" zPosition="4" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
117 <widget name="key_blue" position="457,470" zPosition="4" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
120 def __init__(self, session):
121 Screen.__init__(self, session)
123 self.session = session
125 self["channellist"] = ChannelListMenu([])
126 self["info"] = Label()
128 self["key_red"] = Button("TV Charts")
129 self["key_green"] = Button("Timer Charts")
130 self["key_yellow"] = Button("Movie Charts")
131 self["key_blue"] = Button("Settings")
133 self["actions"] = ActionMap(["OkCancelActions", "ColorActions", "EPGSelectActions"],
135 "ok": self.okClicked,
136 "red": self.switchToTVCharts,
137 "green": self.switchToTimerCharts,
138 "yellow": self.switchToMovieCharts,
139 "blue": self.SettingsMenu,
140 "info": self.ShowEventInfo,
144 self.epgcache = eEPGCache.getInstance()
147 self.RefreshTimer = eTimer()
148 self.RefreshTimer.callback.append(self.downloadList)
150 self.onLayoutFinish.append(self.firstPluginExec)
152 def firstPluginExec(self):
153 self.updateEventCache()
154 self.switchToTVCharts()
157 current = self["channellist"].getCurrent()
161 if self.mode == "tvcharts":
162 self.session.nav.playService(eServiceReference(str(current[0][0])))
163 elif self.mode == "timercharts":
164 serviceref = ServiceReference(current[0][0])
165 eventid = int(current[0][1])
166 event = self.getEventFromId(serviceref, eventid)
167 if event is not None:
168 newEntry = RecordTimerEntry(serviceref, *parseEvent(event), checkOldTimers = True, dirname = preferredTimerPath())
169 self.session.openWithCallback(self.addTimerCallback, TimerEntry, newEntry)
171 self.session.open(MessageBox, "Sorry, no EPG Info available for this event", type=MessageBox.TYPE_ERROR, timeout=10)
172 elif self.mode == "moviecharts":
173 print "[TVCharts] ToDo: Show Movie Info here ..."
176 def addTimerCallback(self, answer):
179 simulTimerList = self.session.nav.RecordTimer.record(entry)
180 if simulTimerList is not None:
181 for x in simulTimerList:
182 if x.setAutoincreaseEnd(entry):
183 self.session.nav.RecordTimer.timeChanged(x)
184 simulTimerList = self.session.nav.RecordTimer.record(entry)
185 if simulTimerList is not None:
186 self.session.openWithCallback(self.finishSanityCorrection, TimerSanityConflict, simulTimerList)
188 print "Timeredit aborted"
190 def finishSanityCorrection(self, answer):
191 self.addTimerCallback(answer)
193 def SettingsMenu(self):
194 self.session.open(TVChartsSetup)
196 def ShowEventInfo(self):
197 current = self["channellist"].getCurrent()
201 serviceref = current[0][0]
202 eventid = current[0][1]
204 service = ServiceReference(serviceref)
205 event = self.getEventFromId(service, eventid)
207 if event is not None:
208 self.session.open(EventViewSimple, event, service)
210 def getEventFromId(self, service, eventid):
212 if self.epgcache is not None and eventid is not None:
213 event = self.epgcache.lookupEventId(service.ref, eventid)
216 def updateEventCache(self):
218 from Screens.ChannelSelection import service_types_tv
219 from Components.Sources.ServiceList import ServiceList
220 bouquetlist = ServiceList(eServiceReference(service_types_tv + ' FROM BOUQUET "bouquets.tv" ORDER BY bouquet'), validate_commands=False).getServicesAsList()
221 for bouquetitem in bouquetlist:
222 serviceHandler = eServiceCenter.getInstance()
223 list = serviceHandler.list(eServiceReference(str(bouquetitem[0])))
224 services = list and list.getContent('S')
225 search = ['IBDCTSERNX']
227 if services: # It's a Bouquet
228 search.extend([(service, 0, -1) for service in services])
230 events = self.epgcache.lookupEvent(search)
232 for eventinfo in events:
233 #0 eventID | 4 eventname | 5 short descr | 6 long descr | 7 serviceref | 8 channelname
234 self.eventcache.append((eventinfo[0], eventinfo[7], eventinfo[8], eventinfo[4]))
237 print "[TVCharts Plugin] Error creating eventcache!"
239 def switchToTVCharts(self):
240 self.mode = "tvcharts"
241 self.setTitle("TV Charts")
242 self["channellist"].setList([])
243 self.feedurl = "http://www.dreambox-plugins.de/feeds/topchannels.php"
246 def switchToTimerCharts(self):
247 self.mode = "timercharts"
248 self.setTitle("Timer Charts")
249 self["channellist"].setList([])
250 self.feedurl = "http://www.dreambox-plugins.de/feeds/toptimers.php?limit=%s" % config.plugins.tvcharts.maxtimerentries.value
253 def switchToMovieCharts(self):
254 self.mode = "moviecharts"
255 self.setTitle("Movie Charts")
256 self["channellist"].setList([])
257 self.feedurl = "http://www.dreambox-plugins.de/feeds/topmovies.php"
260 def downloadList(self):
261 if config.plugins.tvcharts.enabled.value:
262 self["info"].setText("Download feeds from server ...")
263 getPage(self.feedurl).addCallback(self.downloadListCallback).addErrback(self.downloadListError)
265 self["info"].setText("Error: Plugin disabled in Settings ...")
267 def downloadListError(self, error=""):
269 self.session.open(MessageBox, "Error downloading Feed:\n%s" % str(error), type=MessageBox.TYPE_ERROR)
270 self["info"].setText("Error downloading Feed!")
272 def downloadListCallback(self, page=""):
273 self["info"].setText("Parsing Feeds ...")
281 xml = parseString(page)
283 #channellist.append(ChannelListEntryComponent("NAME", "SERVICEREF", "EVENTNAME", "USERCOUNT", "PERCENT"))
285 if self.mode == "tvcharts":
286 for node in xml.getElementsByTagName("DATA"):
287 useronline = int(node.getElementsByTagName("USERCOUNT")[0].childNodes[0].data)
288 totalusers = int(node.getElementsByTagName("TOTALUSERS")[0].childNodes[0].data)
290 for node in xml.getElementsByTagName("CHANNEL"):
292 channelname =str(node.getElementsByTagName("NAME")[0].childNodes[0].data)
293 serviceref = str(node.getElementsByTagName("SERVICEREF")[0].childNodes[0].data)
294 eventname = str(node.getElementsByTagName("EVENTNAME")[0].childNodes[0].data)
295 usercount = int(node.getElementsByTagName("USERCOUNT")[0].childNodes[0].data)
296 percent = int(node.getElementsByTagName("PERCENT")[0].childNodes[0].data)
299 # Look for favourite channel for this event in my bouqets
300 for sepginfo in self.eventcache:
301 if sepginfo[2] == channelname:
303 if sepginfo[3] == eventname:
304 event_id = sepginfo[0]
305 if sepginfo[3] == eventname and sepginfo[1] != serviceref:
306 if channelname[0:3].lower() == sepginfo[2][0:3].lower():
307 serviceref = sepginfo[1]
308 channelname = sepginfo[2]
311 elif sepginfo[3] == eventname and sepginfo[1] == serviceref:
314 # Skip Channels that are not in my bouquets
315 if config.plugins.tvcharts.bouquetfilter.value and not inBouquet:
318 # Skip Channels that are not in my bouquets
320 if channelcount > config.plugins.tvcharts.maxentries.value:
324 channellist.append(ChannelListEntryComponent(self.mode, channelname, serviceref, event_id, eventname, 0, 0, usercount, percent))
327 self.setTitle("TV Charts (User online: %s of %s)" % (useronline, totalusers))
329 elif self.mode == "timercharts":
330 for node in xml.getElementsByTagName("DATA"):
331 totaltimer = int(node.getElementsByTagName("TIMERCOUNT")[0].childNodes[0].data)
333 for node in xml.getElementsByTagName("TIMER"):
334 eitID = int(node.getElementsByTagName("ID")[0].childNodes[0].data)
335 channelname = str(node.getElementsByTagName("CHANNELNAME")[0].childNodes[0].data)
336 serviceref = str(node.getElementsByTagName("SERVICEREF")[0].childNodes[0].data)
337 eventname = str(node.getElementsByTagName("EVENTNAME")[0].childNodes[0].data)
338 starttime = int(node.getElementsByTagName("STARTTIME")[0].childNodes[0].data)
339 endtime = int(node.getElementsByTagName("ENDTIME")[0].childNodes[0].data)
340 usercount = int(node.getElementsByTagName("USERCOUNT")[0].childNodes[0].data)
341 percent = int(node.getElementsByTagName("PERCENT")[0].childNodes[0].data)
343 # Look for favourite channel for this event in my bouqets
344 for sepginfo in self.eventcache:
345 if sepginfo[2] == channelname:
346 serviceref = sepginfo[1]
347 channelname = sepginfo[2]
352 channellist.append(ChannelListEntryComponent(self.mode, channelname, serviceref, eitID, eventname, starttime, endtime, usercount, percent))
355 self.setTitle("Timer Charts (Total Timer: %s)" % (totaltimer))
357 elif self.mode == "moviecharts":
358 for node in xml.getElementsByTagName("DATA"):
359 totalmovies = int(node.getElementsByTagName("MOVIECOUNT")[0].childNodes[0].data)
361 for node in xml.getElementsByTagName("MOVIE"):
362 eventid = int(node.getElementsByTagName("EVENTID")[0].childNodes[0].data)
363 eventname = str(node.getElementsByTagName("EVENTNAME")[0].childNodes[0].data)
364 channelname = str(node.getElementsByTagName("CHANNELNAME")[0].childNodes[0].data)
365 serviceref = str(node.getElementsByTagName("SERVICEREF")[0].childNodes[0].data)
366 starttime = int(node.getElementsByTagName("STARTTIME")[0].childNodes[0].data)
367 usercount = int(node.getElementsByTagName("USERCOUNT")[0].childNodes[0].data)
370 channellist.append(ChannelListEntryComponent(self.mode, channelname, serviceref, eventid, eventname, starttime, 0, usercount, 0))
373 # self.setTitle("Movie Charts (Total Movies: %s)" % (totalmovies))
375 self["info"].setText("")
376 self["channellist"].setList(channellist)
378 self.RefreshTimer.start(60000, True)
380 ############################
381 ##### SETTINGS SCREEN #####
382 ############################
383 class TVChartsSetup(Screen, ConfigListScreen):
384 def __init__(self, session):
385 Screen.__init__(self, session)
386 self.skinName = [ "TVChartsSetup", "Setup" ]
387 self.setup_title = _("TV Charts Settings")
389 self.onChangedEntry = [ ]
391 ConfigListScreen.__init__(self, self.list, session = session, on_change = self.changedEntry)
393 self["actions"] = ActionMap(["SetupActions", "ColorActions"],
395 "ok": self.SaveSettings,
396 "green": self.SaveSettings,
401 self["key_green"] = StaticText(_("OK"))
402 self["key_red"] = StaticText(_("Cancel"))
405 self.onLayoutFinish.append(self.layoutFinished)
407 def layoutFinished(self):
408 self.setTitle(self.setup_title)
410 def createSetup(self):
411 self.list = [ getConfigListEntry(_("TV Charts Plugin Enable"), config.plugins.tvcharts.enabled) ]
412 if config.plugins.tvcharts.enabled.value:
414 getConfigListEntry(_("Max Toplist Entries"), config.plugins.tvcharts.maxentries),
415 getConfigListEntry(_("Max Timerlist Entries"), config.plugins.tvcharts.maxtimerentries),
416 getConfigListEntry(_("Enable Bouquet-Filter?"), config.plugins.tvcharts.bouquetfilter),
417 getConfigListEntry(_("Submit Timerlist?"), config.plugins.tvcharts.submittimers),
420 self["config"].list = self.list
421 self["config"].setList(self.list)
424 ConfigListScreen.keyLeft(self)
425 if self["config"].getCurrent()[1] == config.plugins.tvcharts.enabled:
429 ConfigListScreen.keyRight(self)
430 if self["config"].getCurrent()[1] == config.plugins.tvcharts.enabled:
433 def changedEntry(self):
434 for x in self.onChangedEntry:
437 def getCurrentEntry(self):
438 return self["config"].getCurrent()[0]
440 def getCurrentValue(self):
441 return str(self["config"].getCurrent()[1].getText())
443 def createSummary(self):
446 def SaveSettings(self):
447 config.plugins.tvcharts.save()
455 ##############################
456 ##### UPDATE STATUS #####
457 ##############################
458 class DBUpdateStatus(Screen):
459 def __init__(self, session):
460 Screen.__init__(self, session)
462 self.DBStatusTimer = eTimer()
463 self.DBStatusTimer.callback.append(self.updateStatus)
465 self.__event_tracker = ServiceEventTracker(screen = self, eventmap =
467 iPlayableService.evUpdatedInfo: self.restartTimer,
468 iPlayableService.evUpdatedEventInfo: self.restartTimer
471 self.recordtimer = session.nav.RecordTimer
472 self.NetworkConnectionAvailable = False
474 self.onShow.append(self.restartTimer)
476 def restartTimer(self):
477 if self.NetworkConnectionAvailable:
478 self.DBStatusTimer.stop()
479 self.DBStatusTimer.start((randint(15,60))*1000, True)
481 iNetwork.checkNetworkState(self.checkNetworkCB)
483 def checkNetworkCB(self, data):
486 self.NetworkConnectionAvailable = True
489 self.NetworkConnectionAvailable = False
490 self.DBStatusTimer.stop()
492 def updateStatus(self):
493 print "[TVCharts] Status Update ..."
494 self.DBStatusTimer.stop()
496 if not config.plugins.tvcharts.enabled.value or Screens.Standby.inStandby:
500 ref = self.session.nav.getCurrentlyPlayingServiceReference()
502 serviceHandler = eServiceCenter.getInstance()
503 info = serviceHandler.info(ref)
504 channel_name = info and info.getName(ref).replace('\xc2\x86', '').replace('\xc2\x87', '').decode("utf-8", "ignore").encode("utf-8") or ""
505 self.serviceref = ref.toString()
511 service = self.session.nav.getCurrentService()
512 info = service and service.info()
513 event = info and info.getEvent(0)
514 event_name = event and event.getEventName() or ""
517 if event is not None:
518 curEvent = parseEvent(event)
519 event_begin = int(curEvent[0])+(config.recording.margin_before.value*60)
522 self.BoxID = iNetwork.getAdapterAttribute("eth0", "mac")
523 self.DeviceName = HardwareInfo().get_device_name()
524 self.EnigmaVersion = about.getEnigmaVersionString()
525 self.ImageVersion = about.getVersionString()
529 if config.plugins.tvcharts.submittimers.value:
531 for timer in self.recordtimer.timer_list:
532 if timer.disabled == 0 and timer.justplay == 0:
533 self.timerlist += "%s|%s|%s|%s|%s|%s\n" % (timer.eit,str(int(timer.begin)+(config.recording.margin_before.value*60)), str(int(timer.end)-(config.recording.margin_after.value*60)), str(timer.service_ref), timer.name, timer.service_ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '').decode("utf-8", "ignore").encode("utf-8"))
535 print "[TVCharts] Error loading timers!"
538 url = "http://www.dreambox-plugins.de/feeds/TVCharts/status.php"
539 getPage(url, method='POST', headers={'Content-Type':'application/x-www-form-urlencoded'}, postdata=urlencode({'boxid' : self.BoxID, 'devicename' : self.DeviceName, 'imageversion' : self.ImageVersion, 'enigmaversion' : self.EnigmaVersion, 'lastchannel' : channel_name, 'lastevent' : event_name, 'lastbegin' : event_begin, 'lastserviceref' : self.serviceref, 'timerlist' : self.timerlist})).addErrback(self.updateError)
542 self.DBStatusTimer.start(900000, True)
544 def updateError(self, error=""):
545 print "[TVCharts] Update Error: " + str(error)
546 self.NetworkConnectionAvailable = False
547 self.DBStatusTimer.stop()
549 ############################
550 ##### INIT PLUGIN #####
551 ############################
552 def main(session, **kwargs):
553 session.open(TVChartsMain)
555 def autostart(reason, **kwargs):
557 if "session" in kwargs:
558 session = kwargs["session"]
559 DBUpdateStatus(session)
561 def Plugins(path, **kwargs):
563 PluginDescriptor(where = [PluginDescriptor.WHERE_SESSIONSTART], fnc = autostart),
564 PluginDescriptor(name="TV Charts", description="TV Charts Plugin", where=PluginDescriptor.WHERE_EXTENSIONSMENU, fnc=main),
565 PluginDescriptor(name="TV Charts", description="TV Charts Plugin", where=PluginDescriptor.WHERE_PLUGINMENU, fnc=main) ]