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 quote, quote_plus, unquote, unquote_plus, 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))))
94 ##############################
95 ##### TV Charts MAIN #####
96 ##############################
98 class TVChartsMain(Screen):
101 <screen position="center,center" size="600,510" title="TV Charts">
102 <widget name="channellist" position="10,10" zPosition="1" size="580,458" scrollbarMode="showOnDemand" />
103 <widget name="info" position="0,447" zPosition="2" size="600,20" font="Regular;18" noWrap="1" foregroundColor="#ffffff" transparent="1" halign="center" valign="center" />
104 <ePixmap name="red" position="20,470" zPosition="3" size="140,40" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/TVCharts/images/key_red.png" transparent="1" alphatest="on" />
105 <ePixmap name="green" position="160,470" zPosition="3" size="140,40" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/TVCharts/images/key_green.png" transparent="1" alphatest="on" />
106 <ePixmap name="yellow" position="300,470" zPosition="3" size="140,40" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/TVCharts/images/key_yellow.png" transparent="1" alphatest="on" />
107 <ePixmap name="blue" position="440,470" zPosition="3" size="140,40" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/TVCharts/images/key_blue.png" transparent="1" alphatest="on" />
108 <widget name="key_red" position="20,470" zPosition="4" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
109 <widget name="key_green" position="160,470" zPosition="4" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
110 <widget name="key_yellow" position="300,470" zPosition="4" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
111 <widget name="key_blue" position="440,470" zPosition="4" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
114 def __init__(self, session):
115 Screen.__init__(self, session)
117 self.session = session
119 self["channellist"] = ChannelListMenu([])
120 self["info"] = Label()
122 self["key_red"] = Button("TV Charts")
123 self["key_green"] = Button("Timer Charts")
124 self["key_yellow"] = Button("")
125 self["key_blue"] = Button("Settings")
127 self["actions"] = ActionMap(["OkCancelActions", "ColorActions", "EPGSelectActions"],
129 "ok": self.okClicked,
130 "red": self.switchToTVCharts,
131 "green": self.switchToTimerCharts,
132 "blue": self.SettingsMenu,
133 "info": self.ShowEventInfo,
137 self.epgcache = eEPGCache.getInstance()
140 self.RefreshTimer = eTimer()
141 self.RefreshTimer.callback.append(self.downloadList)
143 self.onLayoutFinish.append(self.firstPluginExec)
145 def firstPluginExec(self):
146 self.updateEventCache()
147 self.switchToTVCharts()
150 current = self["channellist"].getCurrent()
154 if self.mode == "tvcharts":
155 self.session.nav.playService(eServiceReference(str(current[0][0])))
156 elif self.mode == "timercharts":
157 serviceref = ServiceReference(current[0][0])
158 eventid = int(current[0][1])
159 event = self.getEventFromId(serviceref, eventid)
160 if event is not None:
161 newEntry = RecordTimerEntry(serviceref, *parseEvent(event), checkOldTimers = True, dirname = preferredTimerPath())
162 self.session.openWithCallback(self.addTimerCallback, TimerEntry, newEntry)
164 self.session.open(MessageBox, "Sorry, no EPG Info available for this event", type=MessageBox.TYPE_ERROR, timeout=10)
166 def addTimerCallback(self, answer):
169 simulTimerList = self.session.nav.RecordTimer.record(entry)
170 if simulTimerList is not None:
171 for x in simulTimerList:
172 if x.setAutoincreaseEnd(entry):
173 self.session.nav.RecordTimer.timeChanged(x)
174 simulTimerList = self.session.nav.RecordTimer.record(entry)
175 if simulTimerList is not None:
176 self.session.openWithCallback(self.finishSanityCorrection, TimerSanityConflict, simulTimerList)
178 print "Timeredit aborted"
180 def finishSanityCorrection(self, answer):
181 self.addTimerCallback(answer)
183 def SettingsMenu(self):
184 self.session.open(TVChartsSetup)
186 def ShowEventInfo(self):
187 current = self["channellist"].getCurrent()
191 serviceref = current[0][0]
192 eventid = current[0][1]
194 service = ServiceReference(serviceref)
195 event = self.getEventFromId(service, eventid)
197 if event is not None:
198 self.session.open(EventViewSimple, event, service)
200 def getEventFromId(self, service, eventid):
202 if self.epgcache is not None and eventid is not None:
203 event = self.epgcache.lookupEventId(service.ref, eventid)
206 def updateEventCache(self):
208 from Screens.ChannelSelection import service_types_tv
209 from Components.Sources.ServiceList import ServiceList
210 bouquetlist = ServiceList(eServiceReference(service_types_tv + ' FROM BOUQUET "bouquets.tv" ORDER BY bouquet'), validate_commands=False).getServicesAsList()
211 for bouquetitem in bouquetlist:
212 serviceHandler = eServiceCenter.getInstance()
213 list = serviceHandler.list(eServiceReference(str(bouquetitem[0])))
214 services = list and list.getContent('S')
215 search = ['IBDCTSERNX']
217 if services: # It's a Bouquet
218 search.extend([(service, 0, -1) for service in services])
220 events = self.epgcache.lookupEvent(search)
222 for eventinfo in events:
223 #0 eventID | 4 eventname | 5 short descr | 6 long descr | 7 serviceref | 8 channelname
224 self.eventcache.append((eventinfo[0], eventinfo[7], eventinfo[8], eventinfo[4]))
227 print "[TVCharts Plugin] Error creating eventcache!"
229 def switchToTVCharts(self):
230 self.mode = "tvcharts"
231 self.setTitle("TV Charts")
232 self["channellist"].setList([])
233 self.feedurl = "http://www.dreambox-plugins.de/feeds/topchannels.php"
236 def switchToTimerCharts(self):
237 self.mode = "timercharts"
238 self.setTitle("Timer Charts")
239 self["channellist"].setList([])
240 self.feedurl = "http://www.dreambox-plugins.de/feeds/toptimers.php?limit=%s" % config.plugins.tvcharts.maxtimerentries.value
243 def downloadList(self):
244 if not config.plugins.tvcharts.enabled.value:
245 self["info"].setText("Error: Plugin disabled in Settings ...")
247 self["info"].setText("Download feeds from server ...")
248 getPage(self.feedurl).addCallback(self.downloadListCallback).addErrback(self.downloadListError)
250 def downloadListError(self, error=""):
252 self.session.open(MessageBox, "Error downloading Feed:\n%s" % str(error), type=MessageBox.TYPE_ERROR)
253 self["info"].setText("Error downloading Feed!")
255 def downloadListCallback(self, page=""):
256 self["info"].setText("Parsing Feeds ...")
262 xml = parseString(page)
264 #channellist.append(ChannelListEntryComponent("NAME", "SERVICEREF", "EVENTNAME", "USERCOUNT", "PERCENT"))
266 if self.mode == "tvcharts":
267 for node in xml.getElementsByTagName("DATA"):
268 useronline = int(node.getElementsByTagName("USERCOUNT")[0].childNodes[0].data)
269 totalusers = int(node.getElementsByTagName("TOTALUSERS")[0].childNodes[0].data)
271 for node in xml.getElementsByTagName("CHANNEL"):
273 channelname = unquote_plus(str(node.getElementsByTagName("NAME")[0].childNodes[0].data))
274 serviceref = unquote_plus(str(node.getElementsByTagName("SERVICEREF")[0].childNodes[0].data))
275 eventname = unquote_plus(str(node.getElementsByTagName("EVENTNAME")[0].childNodes[0].data))
276 usercount = int(node.getElementsByTagName("USERCOUNT")[0].childNodes[0].data)
277 percent = int(node.getElementsByTagName("PERCENT")[0].childNodes[0].data)
280 # Look for favourite channel for this event in my bouqets
281 for sepginfo in self.eventcache:
282 if sepginfo[2] == channelname:
284 if sepginfo[3] == eventname:
285 event_id = sepginfo[0]
286 if sepginfo[3] == eventname and sepginfo[1] != serviceref:
287 if channelname[0:3].lower() == sepginfo[2][0:3].lower():
288 serviceref = sepginfo[1]
289 channelname = sepginfo[2]
292 elif sepginfo[3] == eventname and sepginfo[1] == serviceref:
295 # Skip Channels that are not in my bouquets
296 if config.plugins.tvcharts.bouquetfilter.value and not inBouquet:
299 # Skip Channels that are not in my bouquets
301 if channelcount > config.plugins.tvcharts.maxentries.value:
305 channellist.append(ChannelListEntryComponent(self.mode, channelname, serviceref, event_id, eventname, 0, 0, usercount, percent))
308 self.setTitle("TV Charts (User online: %s of %s)" % (useronline, totalusers))
310 elif self.mode == "timercharts":
311 for node in xml.getElementsByTagName("TIMER"):
312 eitID = str(node.getElementsByTagName("ID")[0].childNodes[0].data)
313 channelname = str(node.getElementsByTagName("CHANNELNAME")[0].childNodes[0].data)
314 serviceref = str(node.getElementsByTagName("SERVICEREF")[0].childNodes[0].data)
315 eventname = str(node.getElementsByTagName("EVENTNAME")[0].childNodes[0].data)
316 starttime = str(node.getElementsByTagName("STARTTIME")[0].childNodes[0].data)
317 endtime = str(node.getElementsByTagName("ENDTIME")[0].childNodes[0].data)
318 usercount = str(node.getElementsByTagName("USERCOUNT")[0].childNodes[0].data)
319 percent = str(node.getElementsByTagName("PERCENT")[0].childNodes[0].data)
322 channellist.append(ChannelListEntryComponent(self.mode, unquote_plus(channelname), unquote_plus(serviceref), int(eitID), unquote_plus(eventname), int(starttime), int(endtime), unquote_plus(usercount), unquote_plus(percent)))
324 self["info"].setText("")
325 self["channellist"].setList(channellist)
327 self.RefreshTimer.start(60000, True)
330 ############################
331 ##### SETTINGS SCREEN #####
332 ############################
333 class TVChartsSetup(Screen, ConfigListScreen):
334 def __init__(self, session):
335 Screen.__init__(self, session)
336 self.skinName = [ "TVChartsSetup", "Setup" ]
337 self.setup_title = _("TV Charts Settings")
339 self.onChangedEntry = [ ]
341 ConfigListScreen.__init__(self, self.list, session = session, on_change = self.changedEntry)
343 self["actions"] = ActionMap(["SetupActions", "ColorActions"],
345 "ok": self.SaveSettings,
346 "green": self.SaveSettings,
351 self["key_green"] = StaticText(_("OK"))
352 self["key_red"] = StaticText(_("Cancel"))
355 self.onLayoutFinish.append(self.layoutFinished)
357 def layoutFinished(self):
358 self.setTitle(self.setup_title)
360 def createSetup(self):
361 self.list = [ getConfigListEntry(_("TV Charts Plugin Enable"), config.plugins.tvcharts.enabled) ]
362 if config.plugins.tvcharts.enabled.value:
364 getConfigListEntry(_("Max Toplist Entries"), config.plugins.tvcharts.maxentries),
365 getConfigListEntry(_("Max Timerlist Entries"), config.plugins.tvcharts.maxtimerentries),
366 getConfigListEntry(_("Enable Bouquet-Filter?"), config.plugins.tvcharts.bouquetfilter),
367 getConfigListEntry(_("Submit Timerlist?"), config.plugins.tvcharts.submittimers),
370 self["config"].list = self.list
371 self["config"].setList(self.list)
374 ConfigListScreen.keyLeft(self)
375 if self["config"].getCurrent()[1] == config.plugins.tvcharts.enabled:
379 ConfigListScreen.keyRight(self)
380 if self["config"].getCurrent()[1] == config.plugins.tvcharts.enabled:
383 def changedEntry(self):
384 for x in self.onChangedEntry:
387 def getCurrentEntry(self):
388 return self["config"].getCurrent()[0]
390 def getCurrentValue(self):
391 return str(self["config"].getCurrent()[1].getText())
393 def createSummary(self):
396 def SaveSettings(self):
397 config.plugins.tvcharts.save()
405 ##############################
406 ##### UPDATE STATUS #####
407 ##############################
409 class DBUpdateStatus(Screen):
410 def __init__(self, session):
411 Screen.__init__(self, session)
413 self.DBStatusTimer = eTimer()
414 self.DBStatusTimer.callback.append(self.updateStatus)
416 self.__event_tracker = ServiceEventTracker(screen = self, eventmap =
418 iPlayableService.evUpdatedInfo: self.restartTimer,
419 iPlayableService.evUpdatedEventInfo: self.restartTimer
422 self.recordtimer = session.nav.RecordTimer
423 self.NetworkConnectionAvailable = False
425 self.onShow.append(self.restartTimer)
427 def restartTimer(self):
428 if self.NetworkConnectionAvailable:
429 self.DBStatusTimer.stop()
430 self.DBStatusTimer.start(15000, True)
432 iNetwork.checkNetworkState(self.checkNetworkCB)
434 def checkNetworkCB(self, data):
437 self.NetworkConnectionAvailable = True
438 self.DBStatusTimer.start(15000, True)
440 self.NetworkConnectionAvailable = False
441 self.DBStatusTimer.stop()
443 def updateStatus(self):
444 print "[TVCharts] Status Update ..."
445 self.DBStatusTimer.stop()
447 if not config.plugins.tvcharts.enabled.value or Screens.Standby.inStandby:
451 ref = self.session.nav.getCurrentlyPlayingServiceReference()
453 serviceHandler = eServiceCenter.getInstance()
454 info = serviceHandler.info(ref)
455 channel_name = info and info.getName(ref).replace('\xc2\x86', '').replace('\xc2\x87', '').decode("utf-8", "ignore").encode("utf-8") or ""
456 self.serviceref = ref.toString()
462 service = self.session.nav.getCurrentService()
463 info = service and service.info()
464 event = info and info.getEvent(0)
465 event_name = event and event.getEventName() or ""
468 if event is not None:
469 curEvent = parseEvent(event)
470 event_begin = int(curEvent[0])
473 self.BoxID = iNetwork.getAdapterAttribute("eth0", "mac")
474 self.DeviceName = HardwareInfo().get_device_name()
475 self.EnigmaVersion = about.getEnigmaVersionString()
476 self.ImageVersion = about.getVersionString()
480 if config.plugins.tvcharts.submittimers.value:
482 for timer in self.recordtimer.timer_list:
483 if timer.disabled == 0 and timer.justplay == 0:
484 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"))
486 print "[TVCharts] Error loading timers!"
489 url = "http://www.dreambox-plugins.de/feeds/TVCharts/status.php"
490 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}))
493 self.DBStatusTimer.start(900000, True)
495 ##########################################################
497 def main(session, **kwargs):
498 session.open(TVChartsMain)
500 def autostart(reason, **kwargs):
502 if "session" in kwargs:
503 session = kwargs["session"]
504 DBUpdateStatus(session)
506 def Plugins(path, **kwargs):
508 PluginDescriptor(where = [PluginDescriptor.WHERE_SESSIONSTART], fnc = autostart),
509 PluginDescriptor(name="TV Charts", description="TV Charts Plugin", where=PluginDescriptor.WHERE_EXTENSIONSMENU, fnc=main),
510 PluginDescriptor(name="TV Charts", description="TV Charts Plugin", where=PluginDescriptor.WHERE_PLUGINMENU, fnc=main) ]