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 time, 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 service = eServiceReference(str(current[0][0]))
163 self.session.nav.playService(service)
164 elif self.mode == "timercharts":
165 serviceref = ServiceReference(current[0][0])
166 eventid = int(current[0][1])
167 event = self.getEventFromId(serviceref, eventid)
168 if event is not None:
169 newEntry = RecordTimerEntry(serviceref, *parseEvent(event), checkOldTimers = True, dirname = preferredTimerPath())
170 self.session.openWithCallback(self.addTimerCallback, TimerEntry, newEntry)
172 self.session.open(MessageBox, "Sorry, no EPG Info available for this event", type=MessageBox.TYPE_ERROR, timeout=10)
173 elif self.mode == "moviecharts":
174 print "[TVCharts] ToDo: Show Movie Info here ..."
177 def addTimerCallback(self, answer):
180 simulTimerList = self.session.nav.RecordTimer.record(entry)
181 if simulTimerList is not None:
182 for x in simulTimerList:
183 if x.setAutoincreaseEnd(entry):
184 self.session.nav.RecordTimer.timeChanged(x)
185 simulTimerList = self.session.nav.RecordTimer.record(entry)
186 if simulTimerList is not None:
187 self.session.openWithCallback(self.finishSanityCorrection, TimerSanityConflict, simulTimerList)
189 print "Timeredit aborted"
191 def finishSanityCorrection(self, answer):
192 self.addTimerCallback(answer)
194 def SettingsMenu(self):
195 self.session.open(TVChartsSetup)
197 def ShowEventInfo(self):
198 current = self["channellist"].getCurrent()
202 serviceref = current[0][0]
203 eventid = current[0][1]
205 service = ServiceReference(serviceref)
206 event = self.getEventFromId(service, eventid)
208 if event is not None:
209 self.session.open(EventViewSimple, event, service)
211 def getEventFromId(self, service, eventid):
213 if self.epgcache is not None and eventid is not None:
214 event = self.epgcache.lookupEventId(service.ref, eventid)
217 def updateEventCache(self):
219 from Screens.ChannelSelection import service_types_tv
220 from Components.Sources.ServiceList import ServiceList
221 bouquetlist = ServiceList(eServiceReference(service_types_tv + ' FROM BOUQUET "bouquets.tv" ORDER BY bouquet'), validate_commands=False).getServicesAsList()
222 for bouquetitem in bouquetlist:
223 serviceHandler = eServiceCenter.getInstance()
224 list = serviceHandler.list(eServiceReference(str(bouquetitem[0])))
225 services = list and list.getContent('S')
226 search = ['IBDCTSERNX']
228 if services: # It's a Bouquet
229 search.extend([(service, 0, -1) for service in services])
231 events = self.epgcache.lookupEvent(search)
233 for eventinfo in events:
234 #0 eventID | 4 eventname | 5 short descr | 6 long descr | 7 serviceref | 8 channelname
235 self.eventcache.append((eventinfo[0], eventinfo[7], eventinfo[8], eventinfo[4]))
238 print "[TVCharts Plugin] Error creating eventcache!"
240 def switchToTVCharts(self):
241 self.mode = "tvcharts"
242 self.setTitle("TV Charts")
243 self["channellist"].setList([])
244 self.feedurl = "http://www.dreambox-plugins.de/feeds/topchannels.php"
247 def switchToTimerCharts(self):
248 self.mode = "timercharts"
249 self.setTitle("Timer Charts")
250 self["channellist"].setList([])
251 self.feedurl = "http://www.dreambox-plugins.de/feeds/toptimers.php?limit=%s" % config.plugins.tvcharts.maxtimerentries.value
254 def switchToMovieCharts(self):
255 self.mode = "moviecharts"
256 self.setTitle("Movie Charts")
257 self["channellist"].setList([])
258 self.feedurl = "http://www.dreambox-plugins.de/feeds/topmovies.php"
261 def downloadList(self):
262 if config.plugins.tvcharts.enabled.value:
263 self["info"].setText("Downloading feeds from server ...")
264 getPage(self.feedurl).addCallback(self.downloadListCallback).addErrback(self.downloadListError)
266 self["info"].setText("Error: Plugin disabled in Settings ...")
268 def downloadListError(self, error=""):
270 self.session.open(MessageBox, "Error downloading Feed:\n%s" % str(error), type=MessageBox.TYPE_ERROR)
271 self["info"].setText("Error downloading Feed!")
273 def downloadListCallback(self, page=""):
274 self["info"].setText("Parsing Feeds ...")
282 xml = parseString(page)
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
472 self.LastTimerlistUpdate = 0
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 = eServiceReference(self.session.nav.getCurrentlyPlayingServiceReference().toString())
503 serviceHandler = eServiceCenter.getInstance()
504 info = serviceHandler.info(ref)
505 channel_name = info and info.getName(ref).replace('\xc2\x86', '').replace('\xc2\x87', '').decode("utf-8", "ignore").encode("utf-8") or ""
506 self.serviceref = ref.toString()
512 service = self.session.nav.getCurrentService()
513 info = service and service.info()
514 event = info and info.getEvent(0)
515 event_name = event and event.getEventName() or ""
516 event_description = ""
519 if event is not None:
520 curEvent = parseEvent(event)
521 event_begin = int(curEvent[0])+(config.recording.margin_before.value*60)
522 event_description = event.getExtendedDescription()
525 self.BoxID = iNetwork.getAdapterAttribute("eth0", "mac")
526 self.DeviceName = HardwareInfo().get_device_name()
527 self.EnigmaVersion = about.getEnigmaVersionString()
528 self.ImageVersion = about.getVersionString()
532 if config.plugins.tvcharts.submittimers.value and self.LastTimerlistUpdate <= (time()-1800):
533 self.LastTimerlistUpdate = time()
535 for timer in self.recordtimer.timer_list:
536 if timer.disabled == 0 and timer.justplay == 0:
537 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"))
539 print "[TVCharts] Error loading timers!"
542 getPage(url='http://www.dreambox-plugins.de/feeds/TVCharts/status.php', 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, 'eventdescr' : event_description, 'lastbegin' : event_begin, 'lastserviceref' : self.serviceref, 'timerlist' : self.timerlist})).addErrback(self.updateError)
545 self.DBStatusTimer.start(900000, True)
547 def updateError(self, error=""):
548 self.NetworkConnectionAvailable = False
549 self.DBStatusTimer.stop()
551 #############################
552 ##### INIT PLUGIN #####
553 #############################
554 def main(session, **kwargs):
555 session.open(TVChartsMain)
557 def autostart(reason, **kwargs):
559 if "session" in kwargs:
560 session = kwargs["session"]
561 DBUpdateStatus(session)
563 def Plugins(path, **kwargs):
565 PluginDescriptor(where = [PluginDescriptor.WHERE_SESSIONSTART], fnc = autostart),
566 PluginDescriptor(name="TV Charts", description="TV Charts Plugin", where=PluginDescriptor.WHERE_EXTENSIONSMENU, fnc=main),
567 PluginDescriptor(name="TV Charts", description="TV Charts Plugin", where=PluginDescriptor.WHERE_PLUGINMENU, fnc=main) ]