1 #######################################################################
3 # InfoBar Tuner State for Enigma-2
5 # Coded by betonme (c)2011
8 # This program is free software; you can redistribute it and/or
9 # modify it under the terms of the GNU General Public License
10 # as published by the Free Software Foundation; either version 2
11 # of the License, or (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 #######################################################################
22 import NavigationInstance
24 from collections import defaultdict
25 from operator import attrgetter, itemgetter
27 from Components.ActionMap import ActionMap
28 from Components.Button import Button
29 from Components.config import *
30 from Components.ConfigList import ConfigListScreen
31 from Components.Label import Label
32 from Components.Language import *
33 from Components.Pixmap import Pixmap
34 from Components.ServiceEventTracker import ServiceEventTracker
35 from Components.Sources.StaticText import StaticText
37 from Screens.InfoBar import InfoBar
38 from Screens.InfoBarGenerics import InfoBarShowHide
39 from Screens.Screen import Screen
40 from Plugins.Plugin import PluginDescriptor
43 from enigma import iServiceInformation, ePoint, eSize, getDesktop, iFrontendInformation
45 from enigma import eTimer
47 from enigma import iPlayableService, iRecordableService
49 from enigma import eDVBResourceManager, eActionMap, eListboxPythonMultiContent, eListboxPythonStringContent, eListbox, gFont, RT_HALIGN_LEFT, RT_HALIGN_RIGHT, RT_HALIGN_CENTER, eEPGCache, eServiceCenter, eServiceReference
52 from Screens.Setup import SetupSummary
54 from skin import parseColor, parseFont
57 # # try to import EMC module to check for its existence
58 # from Plugins.Extensions.EnhancedMovieCenter.EnhancedMovieCenter import EMCMediaCenter
59 #except ImportError, ie:
60 # class EMCMediaCenter: pass
67 gInfoBarTunerState = None
68 InfoBarShowHideSHOW = None
69 InfoBarShowHideHIDE = None
72 config.infobartunerstate = ConfigSubsection()
74 config.infobartunerstate.about = ConfigSelection(default = "1", choices = [("1", " ")])
75 config.infobartunerstate.enabled = ConfigEnableDisable(default = True) #TODO needs a restart
77 config.infobartunerstate.show_infobar = ConfigYesNo(default = True)
78 config.infobartunerstate.show_events = ConfigYesNo(default = True) #TODO Show on start, end, start/end
79 config.infobartunerstate.show_overwrite = ConfigYesNo(default = False)
81 config.infobartunerstate.number_finished_records = ConfigSelectionNumber(0, 10, 1, default = 5)
82 config.infobartunerstate.timeout_finished_records = ConfigSelectionNumber(0, 600, 10, default = 60)
85 def Plugins(**kwargs):
91 if config.infobartunerstate.enabled.value:
92 # AutoStart and SessionStart
93 descriptors.append( PluginDescriptor(where = PluginDescriptor.WHERE_AUTOSTART, fnc = start) )
94 descriptors.append( PluginDescriptor(where = PluginDescriptor.WHERE_SESSIONSTART, fnc = start) )
96 #TODO Extension List show InfoBarTunerState ?
99 descriptors.append( PluginDescriptor(name = "InfoBar Tuner State", description = "InfoBar Tuner State " +_("configuration"), where = PluginDescriptor.WHERE_PLUGINMENU, fnc = setup) ) #icon = "/EnhancedMovieCenter.png"
104 def setup(session, **kwargs):
106 # Overwrite Skin Position
107 # Show Live TV Tuners PiP Stream ...
108 # Background: Transparent, Block, Dynamic(Farbverlauf)
109 # Width: FullRow, Adapted/Fitting, Symmetrical
110 # Icon or Text for type
112 # Allow Enable disable of elements
113 # Sort order of entry rows
114 # Type: Live pip stream record endedrecords
119 # Remaining >0 infinite -
120 #Rec A 2 RTL blabla 10min to /media/hdd/serien
121 #Rec A 2 RTL /media/hdd/serien/blabla 10min
122 #alltime permanent display
123 #free space of destination path only records
124 # destination ip only streams
126 session.open(InfoBarTunerStateMenu)
128 print "InfoBarTunerStateMenu exception " + str(e)
129 import sys, traceback
130 traceback.print_stack(None, file=sys.stdout)
133 class InfoBarTunerStateMenu(Screen, ConfigListScreen):
134 def __init__(self, session):
135 Screen.__init__(self, session)
136 self.skinName = [ "InfoBarTunerStateMenu", "Setup" ]
139 self.setup_title = _("InfoBarTunerStateMenu Configuration ") + Version
140 self.onChangedEntry = []
143 self["key_red"] = StaticText(_("Cancel"))
144 self["key_green"] = StaticText(_("OK"))
147 self["custom_actions"] = ActionMap(["SetupActions", "ChannelSelectBaseActions"],
149 "cancel": self.keyCancel,
150 "save": self.keySave,
151 "nextBouquet": self.pageUp,
152 "prevBouquet": self.pageDown,
153 }, -2) # higher priority
155 # Initialize Configuration part
157 ConfigListScreen.__init__(self, self.list, session = session, on_change = self.changed)
166 self.onLayoutFinish.append(self.layoutFinished)
168 def defineConfig(self):
170 separator = "".ljust(250,"-")
171 seperatorE2Usage = "- E2 "+_("Usage")+" "
172 seperatorE2Usage = seperatorE2Usage.ljust(250-len(seperatorE2Usage),"-")
177 #( _("About") , config.infobartunerstate.about ),
179 ( _("Enable InfoBarTunerState") , config.infobartunerstate.enabled ),
180 ( separator , config.infobartunerstate.about ),
182 ( _("Show and hide with InfoBar") , config.infobartunerstate.show_infobar ),
183 ( _("Show on Events") , config.infobartunerstate.show_events ),
184 ( _("MoviePlayer integration") , config.infobartunerstate.show_overwrite ),
186 ( _("Number of finished records in list") , config.infobartunerstate.number_finished_records ),
187 ( _("Number of seconds for displaying finished records") , config.infobartunerstate.timeout_finished_records ),
189 ( seperatorE2Usage , config.infobartunerstate.about ),
190 ( _("Infobar timeout") , config.usage.infobar_timeout ),
191 ( _("Show Message when Recording starts") , config.usage.show_message_when_recording_starts ),
194 def createConfig(self):
196 for conf in self.config:
200 list.append( getConfigListEntry( conf[0], conf[1]) )
202 self["config"].setList(self.list)
204 def layoutFinished(self):
205 self.setTitle(self.setup_title)
208 for x in self.onChangedEntry:
213 # Overwrite Screen close function
215 #TODO enable disable plugin
216 #TODO append onClose remove events undooverwrite
217 if config.infobartunerstate.show_overwrite.value:
220 undoOverwriteInfoBar()
222 # Call baseclass function
225 def getCurrentEntry(self):
226 return self["config"].getCurrent()[0]
228 def getCurrentValue(self):
229 return str(self["config"].getCurrent()[1].getText())
231 def createSummary(self):
235 self["config"].instance.moveSelection(self["config"].instance.pageUp)
238 self["config"].instance.moveSelection(self["config"].instance.pageDown)
241 def start(reason, **kwargs):
242 #print "InfoBarTunerState autostart "
245 if reason == 0: # start
246 if kwargs.has_key("session"):
247 global gInfoBarTunerState
248 session = kwargs["session"]
249 gInfoBarTunerState = InfoBarTunerState(session)
252 # InfoBarShowHide for MoviePlayer integration
253 def overwriteInfoBar():
254 global InfoBarShowHideSHOW, InfoBarShowHideHIDE
255 if InfoBarShowHideSHOW is None:
256 # Backup original function
257 #InfoBarShowHideSHOW = InfoBarShowHide.startHideTimer # show not working
258 InfoBarShowHideSHOW = InfoBarShowHide._InfoBarShowHide__onShow
260 #InfoBarShowHide.startHideTimer = InfoBarShowHideShow
261 InfoBarShowHide._InfoBarShowHide__onShow = InfoBarShowHideShow
262 if InfoBarShowHideHIDE is None:
263 # Backup original function
264 #InfoBarShowHideHIDE = InfoBarShowHide.doTimerHide #hide not working
265 InfoBarShowHideHIDE = InfoBarShowHide._InfoBarShowHide__onHide
267 #InfoBarShowHide.doTimerHide = InfoBarShowHideHide
268 InfoBarShowHide._InfoBarShowHide__onHide = InfoBarShowHideHide
270 def undoOverwriteInfoBar():
271 global InfoBarShowHideSHOW, InfoBarShowHideHIDE
272 if InfoBarShowHideSHOW:
273 InfoBarShowHide._InfoBarShowHide__onShow = InfoBarShowHideSHOW
274 InfoBarShowHideSHOW = None
275 if InfoBarShowHideHIDE:
276 InfoBarShowHide._InfoBarShowHide__onHide = InfoBarShowHideHIDE
277 InfoBarShowHideHIDE = None
279 def InfoBarShowHideShow(self):
280 global gInfoBarTunerState, InfoBarShowHideSHOW
281 if InfoBarShowHideSHOW:
282 InfoBarShowHideSHOW(self)
283 print "InfoBarShowHideShow"
284 if gInfoBarTunerState:
285 gInfoBarTunerState.tunerShow()
287 def InfoBarShowHideHide(self):
288 global gInfoBarTunerState, InfoBarShowHideHIDE
289 if InfoBarShowHideHIDE:
290 InfoBarShowHideHIDE(self)
291 print "InfoBarShowHideHide"
292 if gInfoBarTunerState:
293 gInfoBarTunerState.tunerHide()
297 RecordStarted, RecordFinished, Streaming = range(3)
300 class InfoBarTunerState(Screen):
301 def __init__(self, session):
302 Screen.__init__(self, session)
304 self.hideTimer = eTimer()
305 self.hideTimer.callback.append(self.tunerHide)
307 #self.availTimer = eTimer()
308 #self.availTimer.callback.append(self.tunerShow)
310 self.forceBindInfoBarTimer = eTimer()
311 self.forceBindInfoBarTimer.callback.append(self.bindInfoBar)
313 self.tunerInfo = defaultdict(list)
315 self.posy = getDesktop(0).size().height()
318 # If we append our function, we will never see the timer state StateEnded for repeating timer
319 self.session.nav.RecordTimer.on_state_change.insert(0, self.__onRecordingEvent)
321 self.session.nav.record_event.append(self.__onStreamingEvent)
323 #self.session.nav.event.append(self.__onPlayableEvent)
324 #self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
326 # iPlayableService.evStart: self.__onPlayableEvent,
327 # #iPlayableService.evEnd: self.bindInfoBar,
330 #res_mgr = eDVBResourceManager.getInstance()
332 # res_mgr.frontendUseMaskChanged.get().append(self.__onTunerUseMaskChanged)
334 # Add current running records
335 self.updateRecordTimer()
337 #self.onLayoutFinish.append(self.bindInfoBar)
342 # The Plugin starts before the InfoBar is instantiated
343 # Check every second if the InfoBar instance exists and try to bind our functions
344 # Is there an alternative solution?
345 self.forceBindInfoBarTimer.start(1000, False)
347 if config.infobartunerstate.show_overwrite.value:
352 #InfoBar.instance.session
353 #pip.currentService = service
354 #pip.pipservice = iPlayableService
359 #def test(self, event):
360 # print "InfoBarTuner test " + str(event)
362 def bindInfoBar(self):
363 # Reimport InfoBar to force update of the class instance variable
364 # Rebind only if it isn't done already
365 from Screens.InfoBar import InfoBar
366 print "InfoBarTunerState InfoBar.instance " + str(InfoBar.instance)
370 if hasattr(InfoBar.instance, "onShow"):
371 if self.__onInfoBarEventShow not in InfoBar.instance.onShow:
372 InfoBar.instance.onShow.append(self.__onInfoBarEventShow)
374 if hasattr(InfoBar.instance, "onHide"):
375 if self.__onInfoBarEventHide not in InfoBar.instance.onHide:
376 InfoBar.instance.onHide.append(self.__onInfoBarEventHide)
378 if bindShow and bindHide:
379 # Bind was successful
380 self.forceBindInfoBarTimer.stop()
382 def __onTunerUseMaskChanged(self, mask):
383 print "__onTunerUseMaskChanged " +str(mask)
385 def __onInfoBarEventShow(self):
386 if config.infobartunerstate.show_infobar.value:
387 #TODO check recordings streams ...
388 if self.hideTimer.isActive():
389 self.hideTimer.stop()
392 def __onInfoBarEventHide(self):
393 if config.infobartunerstate.show_infobar.value:
394 #TODO check recordings streams ...
397 def __onRecordingEvent(self, timer):
398 if timer.state == timer.StatePrepared:
399 print "__onRecordingEventPrep " +str(timer)
400 elif timer.state == timer.StateRunning: # timer.isRunning()
401 if not timer.justplay:
404 #TEST find unique record identifier
405 print "__onRecordingEventRun " +str(timer)
406 print "__onRecordingEventRun " +str(timer.eit)
407 print "__onRecordingEventRun " +str(timer.service_ref)
408 print "__onRecordingEventRun " +str(timer.service_ref.ref)
409 channel = timer.service_ref.getServiceName()
410 tuner = getTuner(timer.record_service)
411 name = timer.name # No EPG data available: name = instant record
414 #TEST Bug Repeating timer blocking tuner and are not marked as finished
415 #timer.timeChanged = self.__OnTimeChanged
418 if not id in self.tunerInfo:
419 win = self.session.instantiateDialog(TunerState, type, tuner, channel, name, end)
420 self.tunerInfo[id] = win
423 #elif timer.state == timer.StateEnded:
425 type = RecordFinished
426 print "__onRecordingEventEnd " +str(timer.state)
429 if id in self.tunerInfo:
431 #if config.delete_immediately
432 # print "__onRecordingEvent removed "
433 win = self.tunerInfo[id]
435 # self.session.deleteDialog(win)
436 # del self.tunerInfo[id]
442 def __onStreamingEvent(self, rec_service, event):
443 print "__onStreamingEvent2 " +str(event)
444 print "__onStreamingEvent2 " +str(rec_service)
445 if event == iRecordableService.evStart:
447 from Plugins.Extensions.WebInterface.WebScreens import streamingScreens
448 from Plugins.Extensions.WebInterface.WebComponents.Sources.RequestData import RequestData
450 streamingScreens = []
451 for stream in streamingScreens :
452 print stream.getRecordService() # iRecordableService
453 print stream.getRecordServiceRef() # eServiceReference
454 # if hasattr( stream, 'request' ):
455 # print stream.request
456 # #print "request TODO dir info " + str(dir(stream.request))
457 # #print "request TODO vars info " + str(vars(stream.request))
458 # #print stream.request.getRequestHostname()
459 # print stream.request.host
460 # #print str(stream.request.host.port)
461 # print stream.request.method
462 # print stream.request.path
463 # print stream.request.uri
464 # print stream.request.client
465 # print stream.request.requestHeaders
466 # print stream.request.site
467 # print stream.request.content
468 # if stream.has_key( 'StreamService' ):
469 # print stream["StreamService"]
470 #ServiceReference(stream.getRecordServiceRef())
472 elif event == iRecordableService.evEnd:
474 from Plugins.Extensions.WebInterface.WebScreens import streamingScreens
476 streamingScreens = []
477 for stream in streamingScreens:
478 #Delete first second any difference !?
479 print stream.getRecordService() # iRecordableService
480 print stream.getRecordServiceRef() # eServiceReference
482 def __onPlayableEvent(self, event):
484 print "__onPlayableEvent " + str(event)
486 #self.showWithTimer()
487 # Rebind InfoBar Events
490 def __OnTimeChanged(self):
491 #TODO Config show on timer time changed
494 def updateRecordTimer(self):
495 for timer in NavigationInstance.instance.RecordTimer.timer_list:
496 if timer.isRunning() and not timer.justplay:
497 self.__onRecordingEvent(timer)
499 def showWithTimer(self):
500 if config.infobartunerstate.show_events.value:
501 #if self.availTimer.isActive():
502 # self.availTimer.stop()
503 #self.availTimer.startLongTimer( 10 )
505 # Start timer to avoid permanent displaying
506 # Do not start timer if no timeout is configured
507 idx = config.usage.infobar_timeout.index
509 if self.hideTimer.isActive():
510 self.hideTimer.stop()
511 self.hideTimer.startLongTimer( config.usage.infobar_timeout.index or 1 )
517 # Rebind InfoBar Events
520 # Only show the Tuner information dialog,
521 # if no screen is displayed or the InfoBar is visible
522 #TODO Info can also be showed if info.rectangle is outside currentdialog.rectangle
523 # if self.session.current_dialog is None \
524 # or isinstance(self.session.current_dialog, InfoBar):
525 #MAYBE Tuner Informationen werden zusammen mit der EMCMediaCenter InfoBar angezeigt
526 #or isinstance(self.session.current_dialog, EMCMediaCenter):
529 # if entry reached timeout
530 # if number of entries is reached
532 for id, win in sorted( self.tunerInfo.items(), key=lambda x: (x[1].end), reverse=True ):
533 if win.type == RecordFinished:
534 numberoffinished += 1
536 if win.toberemoved == True \
537 or win.type == RecordFinished and numberoffinished > int( config.infobartunerstate.number_finished_records.value ):
538 # Delete Stopped Timers
539 self.session.deleteDialog(win)
540 del self.tunerInfo[id]
542 # Dynamic column resizing and repositioning
543 #TODO get Initial Position and Size from skin
546 lentuner, lennumber, lenchannel, lenname, lenremaining = 0, 0, 0, 0, 0
547 for id, win in self.tunerInfo.items():
548 timer = getTimer( id )
552 posx = win.instance.position().x()
553 sizeh = win.instance.size().height()
554 posy = min( win.instance.position().y(), posy )
555 lentuner = max( win["Tuner"].instance.calculateSize().width(), lentuner )
556 lennumber = max( win["Number"].instance.calculateSize().width(), lennumber )
557 lenchannel = max( win["Channel"].instance.calculateSize().width(), lenchannel )
558 lenname = max( win["Name"].instance.calculateSize().width(), lenname )
559 lenremaining = max( win["Remaining"].instance.calculateSize().width(), lenremaining )
561 print "InfoBarTunerState Warning no timer found"
565 # Spacing between the column entries
572 # Resize, move and show windows
573 for win in sorted( self.tunerInfo.itervalues(), key=lambda x: (x.type, x.remaining) ):
574 win.resize(lentuner, lennumber, lenchannel, lenname, lenremaining)
575 win.instance.move(ePoint(posx, posy))
581 if self.hideTimer.isActive():
582 self.hideTimer.stop()
583 for win in self.tunerInfo.itervalues():
587 class TunerState(Screen):
588 skinfile = "/usr/lib/enigma2/python/Plugins/Extensions/InfoBarTunerState/skin.xml"
589 skin = open(skinfile).read()
591 def __init__(self, session, type, tuner, channel, name, end):
593 Screen.__init__(self, session)
595 self.removeTimer = eTimer()
596 self.removeTimer.callback.append(self.remove)
597 self.toberemoved = False
600 self["Background"] = Pixmap()
601 self["Record"] = Pixmap()
602 self["Stopped"] = Pixmap()
605 self["Tuner"] = Label(tuner)
606 self["Number"] = Label()
607 self["Channel"] = Label(channel)
609 self["Name"] = Label(name)
612 self["Remaining"] = Label()
617 # CoolWide = getDesktop(0).size().width()
618 # if CoolWide == 720:
619 # skin = "/usr/lib/enigma2/python/Plugins/Extensions/EnhancedMovieCenter/CoolSkin/EMCSelection_720.xml"
620 # elif CoolWide == 1024:
621 # skin = "/usr/lib/enigma2/python/Plugins/Extensions/EnhancedMovieCenter/CoolSkin/EMCSelection_1024.xml"
622 # elif CoolWide == 1280:
623 # skin = "/usr/lib/enigma2/python/Plugins/Extensions/EnhancedMovieCenter/CoolSkin/EMCSelection_1280.xml"
626 # self.skin = Cool.read()
629 def changeType(self, type):
633 def updateType(self):
634 if self.type == RecordStarted:
635 self["Record"].show()
636 self["Stopped"].hide()
637 elif self.type == RecordFinished:
638 self["Record"].hide()
639 self["Stopped"].show()
640 self["Tuner"].setText( "-" )
641 # Check if timer is already started
642 if not self.removeTimer.isActive():
643 # Check if timeout is configured
644 if config.infobartunerstate.timeout_finished_records.value:
645 self.removeTimer.startLongTimer( int( config.infobartunerstate.timeout_finished_records.value ) )
647 def update(self, timer):
648 # Calculate remaining minutes
651 if not timer.autoincrease:
652 if self.type == RecordFinished:
653 # Show recording length
654 duration = int( math.ceil( ( self.end - timer.begin ) / 60.0 ) )
656 self["Remaining"].setText( str(duration) + _(" Min") )
658 # Show remaining recording time
659 self.remaining = int( math.ceil( ( self.end - time() ) / 60.0 ) )
660 self["Remaining"].setText( str(self.remaining) + _(" Min") )
663 self["Remaining"].setText( "" )
665 # Add infinity symbol for indefinitely recordings
666 self.remaining = 0xFFFFFFFFFFFFFFFF
667 self["Remaining"].setText( u"\u221E".encode("utf-8") )
668 #TODO config update name of infinite recordings
669 epg = eEPGCache.getInstance()
670 event = epg and epg.lookupEventTime(timer.service_ref.ref, -1, 0)
672 self["Name"].setText( event.getEventName() )
676 self["Remaining"].setText( "" )
678 if not self["Number"].getText():
679 if timer and timer.service_ref and timer.service_ref.ref:
680 number = getNumber(timer.service_ref.ref)
682 self["Number"].setText( str(number) )
684 #TODO Handle Live Entry - Update all Labels
686 def resize(self, lentuner, lennumber, lenchannel, lenname, lenremaining):
687 sh = self.instance.size().height()
689 self["Tuner"].instance.resize( eSize(lentuner, sh) )
690 px = self["Tuner"].instance.position().x()
691 py = self["Tuner"].instance.position().y()
694 self["Number"].instance.resize( eSize(lennumber, sh) )
695 self["Number"].instance.move( ePoint(px, py) )
698 self["Channel"].instance.resize( eSize(lenchannel, sh) )
699 self["Channel"].instance.move( ePoint(px, py) )
702 self["Name"].instance.resize( eSize(lenname, sh) )
703 self["Name"].instance.move( ePoint(px, py) )
706 self["Remaining"].instance.resize( eSize(lenremaining, sh) )
707 self["Remaining"].instance.move( ePoint(px, py) )
710 #TODO config width and style
712 #if background dynamic
713 #self["Background"].instance.resize( eSize(px, sh) )
714 #self.instance.resize( eSize(px, sh) )
716 #if background color gradiant
717 bw = self["Background"].instance.size().width()
718 self["Background"].instance.move( ePoint(px-bw, py) )
719 self.instance.resize( eSize(px, sh) )
722 self.toberemoved = True
725 # Global helper functions
726 def getTimer(strtimer):
727 #for timer in self.session.nav.RecordTimer.timer_list + self.session.nav.RecordTimer.processed_timers:
728 for timer in NavigationInstance.instance.RecordTimer.timer_list + NavigationInstance.instance.RecordTimer.processed_timers:
729 if str(timer) == strtimer:
733 def getTuner(service):
734 # service must be an instance of iPlayableService or iRecordableService
735 feinfo = service and service.frontendInfo()
736 frontendData = feinfo and feinfo.getAll(False)
737 return chr( frontendData.get("tuner_number", -1) + ord('A') )
739 def getNumber(actservice):
740 # actservice must be an instance of eServiceReference
741 from Screens.InfoBar import InfoBar
743 if InfoBar and InfoBar.instance:
744 Servicelist = InfoBar.instance.servicelist
745 mask = (eServiceReference.isMarker | eServiceReference.isDirectory)
747 bouquets = Servicelist and Servicelist.getBouquetList()
749 actbouquet = Servicelist.getRoot()
750 #TODO get alternative for actbouquet
752 serviceHandler = eServiceCenter.getInstance()
753 for name, bouquet in bouquets:
754 if not bouquet.valid(): #check end of list
756 if bouquet.flags & eServiceReference.isDirectory:
757 servicelist = serviceHandler.list(bouquet)
758 if not servicelist is None:
760 service = servicelist.getNext()
761 if not service.valid(): #check end of list
763 playable = not (service.flags & mask)
766 if actbouquet == bouquet and actservice == service: