1 #####################################################
2 # TVCharts Plugin for Enigma2 Dreamboxes
3 # Coded by Homey (c) 2010
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 time import gmtime, strftime
35 from twisted.web.client import getPage
36 from xml.dom.minidom import parse, parseString
37 from urllib import urlencode
40 import xml.etree.cElementTree
41 import Screens.Standby
43 ##############################
44 ##### CONFIG SETTINGS #####
45 ##############################
46 config.plugins.tvcharts = ConfigSubsection()
47 config.plugins.tvcharts.enabled = ConfigYesNo(default = True)
48 config.plugins.tvcharts.maxentries = ConfigInteger(default=10, limits=(5, 100))
49 config.plugins.tvcharts.maxtimerentries = ConfigInteger(default=10, limits=(5, 100))
50 config.plugins.tvcharts.submittimers = ConfigYesNo(default = True)
51 config.plugins.tvcharts.bouquetfilter = ConfigYesNo(default = True)
53 ##########################################################
56 #Channellist Menu Entry
57 class ChannelListMenu(MenuList):
58 def __init__(self, list, enableWrapAround=False):
59 MenuList.__init__(self, list, enableWrapAround, eListboxPythonMultiContent)
60 self.l.setFont(0, gFont("Regular", 24))
61 self.l.setFont(1, gFont("Regular", 20))
62 self.l.setFont(2, gFont("Regular", 16))
63 self.l.setItemHeight(76)
65 def ChannelListEntryComponent(type, channelname, serviceref, eventid, eventname, starttime, endtime, usercount, percent):
66 res = [ (serviceref, eventid) ]
69 pixmap = "/usr/share/enigma2/skin_default/picon_default.png"
70 searchPaths = ('/usr/share/enigma2/picon/','/media/cf/picon/','/media/usb/picon/')
72 srefstring = serviceref
73 pos = srefstring.rfind(':')
75 srefstring = srefstring[:pos].rstrip(':').replace(':','_')
76 for path in searchPaths:
77 pngname = path + srefstring + ".png"
78 if fileExists(pngname):
82 if type == "tvcharts":
83 res.append(MultiContentEntryPixmapAlphaTest(pos=(8, 8), size=(100, 60), png=loadPNG(pixmap)))
84 res.append(MultiContentEntryText(pos=(130, 5), size=(480, 30), font=0, text="%s (Viewer: %s)" % (channelname, usercount)))
85 res.append(MultiContentEntryText(pos=(130, 35), size=(480, 25), font=1, text=eventname))
86 elif type == "timercharts":
87 res.append(MultiContentEntryPixmapAlphaTest(pos=(10, 10), size=(100, 60), png=loadPNG(pixmap)))
88 res.append(MultiContentEntryText(pos=(130, 5), size=(480, 28), font=0, text="%s (User: %s)" % (channelname, usercount)))
89 res.append(MultiContentEntryText(pos=(130, 33), size=(480, 25), font=1, text=eventname))
90 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))))
91 elif type == "moviecharts":
92 res.append(MultiContentEntryPixmapAlphaTest(pos=(8, 8), size=(100, 60), png=loadPNG(pixmap)))
93 res.append(MultiContentEntryText(pos=(130, 5), size=(480, 30), font=0, text=eventname))
94 res.append(MultiContentEntryText(pos=(130, 33), size=(480, 25), font=1, text="Viewer: %s" % (usercount)))
95 res.append(MultiContentEntryText(pos=(130, 57), size=(480, 20), font=2, text="%s Uhr - %s" % (strftime("%d.%m.%Y %H:%M", gmtime(starttime)), channelname)))
99 ##############################
100 ##### TV Charts MAIN #####
101 ##############################
103 class TVChartsMain(Screen):
106 <screen position="center,center" size="620,510" title="TV Charts">
107 <widget name="channellist" position="10,10" zPosition="1" size="600,458" scrollbarMode="showOnDemand" />
108 <widget name="info" position="0,447" zPosition="2" size="620,20" font="Regular;18" noWrap="1" foregroundColor="#ffffff" transparent="1" halign="center" valign="center" />
109 <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" />
110 <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" />
111 <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" />
112 <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" />
113 <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" />
114 <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" />
115 <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" />
116 <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" />
119 def __init__(self, session):
120 Screen.__init__(self, session)
122 self.session = session
124 self["channellist"] = ChannelListMenu([])
125 self["info"] = Label()
127 self["key_red"] = Button("TV Charts")
128 self["key_green"] = Button("Timer Charts")
129 self["key_yellow"] = Button("Movie Charts")
130 self["key_blue"] = Button("Settings")
132 self["actions"] = ActionMap(["OkCancelActions", "ColorActions", "EPGSelectActions"],
134 "ok": self.okClicked,
135 "red": self.switchToTVCharts,
136 "green": self.switchToTimerCharts,
137 "yellow": self.switchToMovieCharts,
138 "blue": self.SettingsMenu,
139 "info": self.ShowEventInfo,
143 self.epgcache = eEPGCache.getInstance()
146 self.RefreshTimer = eTimer()
147 self.RefreshTimer.callback.append(self.downloadList)
149 self.onLayoutFinish.append(self.firstPluginExec)
151 def firstPluginExec(self):
152 self.updateEventCache()
153 self.switchToTVCharts()
156 current = self["channellist"].getCurrent()
160 if self.mode == "tvcharts":
161 self.session.nav.playService(eServiceReference(str(current[0][0])))
162 elif self.mode == "timercharts":
163 serviceref = ServiceReference(current[0][0])
164 eventid = int(current[0][1])
165 event = self.getEventFromId(serviceref, eventid)
166 if event is not None:
167 newEntry = RecordTimerEntry(serviceref, *parseEvent(event), checkOldTimers = True, dirname = preferredTimerPath())
168 self.session.openWithCallback(self.addTimerCallback, TimerEntry, newEntry)
170 self.session.open(MessageBox, "Sorry, no EPG Info available for this event", type=MessageBox.TYPE_ERROR, timeout=10)
171 elif self.mode == "moviecharts":
172 print "[TVCharts] ToDo: Show Movie Info here ..."
175 def addTimerCallback(self, answer):
178 simulTimerList = self.session.nav.RecordTimer.record(entry)
179 if simulTimerList is not None:
180 for x in simulTimerList:
181 if x.setAutoincreaseEnd(entry):
182 self.session.nav.RecordTimer.timeChanged(x)
183 simulTimerList = self.session.nav.RecordTimer.record(entry)
184 if simulTimerList is not None:
185 self.session.openWithCallback(self.finishSanityCorrection, TimerSanityConflict, simulTimerList)
187 print "Timeredit aborted"
189 def finishSanityCorrection(self, answer):
190 self.addTimerCallback(answer)
192 def SettingsMenu(self):
193 self.session.open(TVChartsSetup)
195 def ShowEventInfo(self):
196 current = self["channellist"].getCurrent()
200 serviceref = current[0][0]
201 eventid = current[0][1]
203 service = ServiceReference(serviceref)
204 event = self.getEventFromId(service, eventid)
206 if event is not None:
207 self.session.open(EventViewSimple, event, service)
209 def getEventFromId(self, service, eventid):
211 if self.epgcache is not None and eventid is not None:
212 event = self.epgcache.lookupEventId(service.ref, eventid)
215 def updateEventCache(self):
217 from Screens.ChannelSelection import service_types_tv
218 from Components.Sources.ServiceList import ServiceList
219 bouquetlist = ServiceList(eServiceReference(service_types_tv + ' FROM BOUQUET "bouquets.tv" ORDER BY bouquet'), validate_commands=False).getServicesAsList()
220 for bouquetitem in bouquetlist:
221 serviceHandler = eServiceCenter.getInstance()
222 list = serviceHandler.list(eServiceReference(str(bouquetitem[0])))
223 services = list and list.getContent('S')
224 search = ['IBDCTSERNX']
226 if services: # It's a Bouquet
227 search.extend([(service, 0, -1) for service in services])
229 events = self.epgcache.lookupEvent(search)
231 for eventinfo in events:
232 #0 eventID | 4 eventname | 5 short descr | 6 long descr | 7 serviceref | 8 channelname
233 self.eventcache.append((eventinfo[0], eventinfo[7], eventinfo[8], eventinfo[4]))
236 print "[TVCharts Plugin] Error creating eventcache!"
238 def switchToTVCharts(self):
239 self.mode = "tvcharts"
240 self.setTitle("TV Charts")
241 self["channellist"].setList([])
242 self.feedurl = "http://www.dreambox-plugins.de/feeds/topchannels.php"
245 def switchToTimerCharts(self):
246 self.mode = "timercharts"
247 self.setTitle("Timer Charts")
248 self["channellist"].setList([])
249 self.feedurl = "http://www.dreambox-plugins.de/feeds/toptimers.php?limit=%s" % config.plugins.tvcharts.maxtimerentries.value
252 def switchToMovieCharts(self):
253 self.mode = "moviecharts"
254 self.setTitle("Movie Charts")
255 self["channellist"].setList([])
256 self.feedurl = "http://www.dreambox-plugins.de/feeds/topmovies.php"
259 def downloadList(self):
260 if config.plugins.tvcharts.enabled.value:
261 self["info"].setText("Download feeds from server ...")
262 getPage(self.feedurl).addCallback(self.downloadListCallback).addErrback(self.downloadListError)
264 self["info"].setText("Error: Plugin disabled in Settings ...")
266 def downloadListError(self, error=""):
268 self.session.open(MessageBox, "Error downloading Feed:\n%s" % str(error), type=MessageBox.TYPE_ERROR)
269 self["info"].setText("Error downloading Feed!")
271 def downloadListCallback(self, page=""):
272 self["info"].setText("Parsing Feeds ...")
280 xml = parseString(page)
282 #channellist.append(ChannelListEntryComponent("NAME", "SERVICEREF", "EVENTNAME", "USERCOUNT", "PERCENT"))
284 if self.mode == "tvcharts":
285 for node in xml.getElementsByTagName("DATA"):
286 useronline = int(node.getElementsByTagName("USERCOUNT")[0].childNodes[0].data)
287 totalusers = int(node.getElementsByTagName("TOTALUSERS")[0].childNodes[0].data)
289 for node in xml.getElementsByTagName("CHANNEL"):
291 channelname =str(node.getElementsByTagName("NAME")[0].childNodes[0].data)
292 serviceref = str(node.getElementsByTagName("SERVICEREF")[0].childNodes[0].data)
293 eventname = str(node.getElementsByTagName("EVENTNAME")[0].childNodes[0].data)
294 usercount = int(node.getElementsByTagName("USERCOUNT")[0].childNodes[0].data)
295 percent = int(node.getElementsByTagName("PERCENT")[0].childNodes[0].data)
298 # Look for favourite channel for this event in my bouqets
299 for sepginfo in self.eventcache:
300 if sepginfo[2] == channelname:
302 if sepginfo[3] == eventname:
303 event_id = sepginfo[0]
304 if sepginfo[3] == eventname and sepginfo[1] != serviceref:
305 if channelname[0:3].lower() == sepginfo[2][0:3].lower():
306 serviceref = sepginfo[1]
307 channelname = sepginfo[2]
310 elif sepginfo[3] == eventname and sepginfo[1] == serviceref:
313 # Skip Channels that are not in my bouquets
314 if config.plugins.tvcharts.bouquetfilter.value and not inBouquet:
317 # Skip Channels that are not in my bouquets
319 if channelcount > config.plugins.tvcharts.maxentries.value:
323 channellist.append(ChannelListEntryComponent(self.mode, channelname, serviceref, event_id, eventname, 0, 0, usercount, percent))
326 self.setTitle("TV Charts (User online: %s of %s)" % (useronline, totalusers))
328 elif self.mode == "timercharts":
329 for node in xml.getElementsByTagName("DATA"):
330 totaltimer = int(node.getElementsByTagName("TIMERCOUNT")[0].childNodes[0].data)
332 for node in xml.getElementsByTagName("TIMER"):
333 eitID = int(node.getElementsByTagName("ID")[0].childNodes[0].data)
334 channelname = str(node.getElementsByTagName("CHANNELNAME")[0].childNodes[0].data)
335 serviceref = str(node.getElementsByTagName("SERVICEREF")[0].childNodes[0].data)
336 eventname = str(node.getElementsByTagName("EVENTNAME")[0].childNodes[0].data)
337 starttime = int(node.getElementsByTagName("STARTTIME")[0].childNodes[0].data)
338 endtime = int(node.getElementsByTagName("ENDTIME")[0].childNodes[0].data)
339 usercount = int(node.getElementsByTagName("USERCOUNT")[0].childNodes[0].data)
340 percent = int(node.getElementsByTagName("PERCENT")[0].childNodes[0].data)
342 # Look for favourite channel for this event in my bouqets
343 for sepginfo in self.eventcache:
344 if sepginfo[2] == channelname:
345 serviceref = sepginfo[1]
346 channelname = sepginfo[2]
351 channellist.append(ChannelListEntryComponent(self.mode, channelname, serviceref, eitID, eventname, starttime, endtime, usercount, percent))
354 self.setTitle("Timer Charts (Total Timer: %s)" % (totaltimer))
356 elif self.mode == "moviecharts":
357 for node in xml.getElementsByTagName("DATA"):
358 totalmovies = int(node.getElementsByTagName("MOVIECOUNT")[0].childNodes[0].data)
360 for node in xml.getElementsByTagName("MOVIE"):
361 eventid = int(node.getElementsByTagName("EVENTID")[0].childNodes[0].data)
362 eventname = str(node.getElementsByTagName("EVENTNAME")[0].childNodes[0].data)
363 channelname = str(node.getElementsByTagName("CHANNELNAME")[0].childNodes[0].data)
364 serviceref = str(node.getElementsByTagName("SERVICEREF")[0].childNodes[0].data)
365 starttime = int(node.getElementsByTagName("STARTTIME")[0].childNodes[0].data)
366 usercount = int(node.getElementsByTagName("USERCOUNT")[0].childNodes[0].data)
369 channellist.append(ChannelListEntryComponent(self.mode, channelname, serviceref, eventid, eventname, starttime, 0, usercount, 0))
372 # self.setTitle("Movie Charts (Total Movies: %s)" % (totalmovies))
374 self["info"].setText("")
375 self["channellist"].setList(channellist)
377 self.RefreshTimer.start(60000, True)
379 ############################
380 ##### SETTINGS SCREEN #####
381 ############################
382 class TVChartsSetup(Screen, ConfigListScreen):
383 def __init__(self, session):
384 Screen.__init__(self, session)
385 self.skinName = [ "TVChartsSetup", "Setup" ]
386 self.setup_title = _("TV Charts Settings")
388 self.onChangedEntry = [ ]
390 ConfigListScreen.__init__(self, self.list, session = session, on_change = self.changedEntry)
392 self["actions"] = ActionMap(["SetupActions", "ColorActions"],
394 "ok": self.SaveSettings,
395 "green": self.SaveSettings,
400 self["key_green"] = StaticText(_("OK"))
401 self["key_red"] = StaticText(_("Cancel"))
404 self.onLayoutFinish.append(self.layoutFinished)
406 def layoutFinished(self):
407 self.setTitle(self.setup_title)
409 def createSetup(self):
410 self.list = [ getConfigListEntry(_("TV Charts Plugin Enable"), config.plugins.tvcharts.enabled) ]
411 if config.plugins.tvcharts.enabled.value:
413 getConfigListEntry(_("Max Toplist Entries"), config.plugins.tvcharts.maxentries),
414 getConfigListEntry(_("Max Timerlist Entries"), config.plugins.tvcharts.maxtimerentries),
415 getConfigListEntry(_("Enable Bouquet-Filter?"), config.plugins.tvcharts.bouquetfilter),
416 getConfigListEntry(_("Submit Timerlist?"), config.plugins.tvcharts.submittimers),
419 self["config"].list = self.list
420 self["config"].setList(self.list)
423 ConfigListScreen.keyLeft(self)
424 if self["config"].getCurrent()[1] == config.plugins.tvcharts.enabled:
428 ConfigListScreen.keyRight(self)
429 if self["config"].getCurrent()[1] == config.plugins.tvcharts.enabled:
432 def changedEntry(self):
433 for x in self.onChangedEntry:
436 def getCurrentEntry(self):
437 return self["config"].getCurrent()[0]
439 def getCurrentValue(self):
440 return str(self["config"].getCurrent()[1].getText())
442 def createSummary(self):
445 def SaveSettings(self):
446 config.plugins.tvcharts.save()
454 ##############################
455 ##### UPDATE STATUS #####
456 ##############################
457 class DBUpdateStatus(Screen):
458 def __init__(self, session):
459 Screen.__init__(self, session)
461 self.DBStatusTimer = eTimer()
462 self.DBStatusTimer.callback.append(self.updateStatus)
464 self.__event_tracker = ServiceEventTracker(screen = self, eventmap =
466 iPlayableService.evUpdatedInfo: self.restartTimer,
467 iPlayableService.evUpdatedEventInfo: self.restartTimer
470 self.recordtimer = session.nav.RecordTimer
471 self.NetworkConnectionAvailable = False
473 self.onShow.append(self.restartTimer)
475 def restartTimer(self):
476 if self.NetworkConnectionAvailable:
477 self.DBStatusTimer.stop()
478 self.DBStatusTimer.start(15000, True)
480 iNetwork.checkNetworkState(self.checkNetworkCB)
482 def checkNetworkCB(self, data):
485 self.NetworkConnectionAvailable = True
486 self.DBStatusTimer.start(15000, True)
488 self.NetworkConnectionAvailable = False
489 self.DBStatusTimer.stop()
491 def updateStatus(self):
492 print "[TVCharts] Status Update ..."
493 self.DBStatusTimer.stop()
495 if not config.plugins.tvcharts.enabled.value or Screens.Standby.inStandby:
499 ref = self.session.nav.getCurrentlyPlayingServiceReference()
501 serviceHandler = eServiceCenter.getInstance()
502 info = serviceHandler.info(ref)
503 channel_name = info and info.getName(ref).replace('\xc2\x86', '').replace('\xc2\x87', '').decode("utf-8", "ignore").encode("utf-8") or ""
504 self.serviceref = ref.toString()
510 service = self.session.nav.getCurrentService()
511 info = service and service.info()
512 event = info and info.getEvent(0)
513 event_name = event and event.getEventName() or ""
516 if event is not None:
517 curEvent = parseEvent(event)
518 event_begin = int(curEvent[0])+(config.recording.margin_before.value*60)
521 self.BoxID = iNetwork.getAdapterAttribute("eth0", "mac")
522 self.DeviceName = HardwareInfo().get_device_name()
523 self.EnigmaVersion = about.getEnigmaVersionString()
524 self.ImageVersion = about.getVersionString()
528 if config.plugins.tvcharts.submittimers.value:
530 for timer in self.recordtimer.timer_list:
531 if timer.disabled == 0 and timer.justplay == 0:
532 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"))
534 print "[TVCharts] Error loading timers!"
537 url = "http://www.dreambox-plugins.de/feeds/TVCharts/status.php"
538 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}))
541 self.DBStatusTimer.start(900000, True)
544 ############################
545 ##### INIT PLUGIN #####
546 ############################
547 def main(session, **kwargs):
548 session.open(TVChartsMain)
550 def autostart(reason, **kwargs):
552 if "session" in kwargs:
553 session = kwargs["session"]
554 DBUpdateStatus(session)
556 def Plugins(path, **kwargs):
558 PluginDescriptor(where = [PluginDescriptor.WHERE_SESSIONSTART], fnc = autostart),
559 PluginDescriptor(name="TV Charts", description="TV Charts Plugin", where=PluginDescriptor.WHERE_EXTENSIONSMENU, fnc=main),
560 PluginDescriptor(name="TV Charts", description="TV Charts Plugin", where=PluginDescriptor.WHERE_PLUGINMENU, fnc=main) ]