1 from ChannelSelection import ChannelSelection, BouquetSelector
3 from Components.ActionMap import ActionMap, HelpableActionMap
4 from Components.ActionMap import NumberActionMap
5 from Components.Harddisk import harddiskmanager
6 from Components.Input import Input
7 from Components.Label import Label
8 from Components.PluginComponent import plugins
9 from Components.ServiceEventTracker import ServiceEventTracker
10 from Components.Sources.Boolean import Boolean
11 from Components.config import config, ConfigBoolean, ConfigClock
12 from Components.SystemInfo import SystemInfo
13 from EpgSelection import EPGSelection
14 from Plugins.Plugin import PluginDescriptor
16 from Screen import Screen
17 from Screens.ChoiceBox import ChoiceBox
18 from Screens.Dish import Dish
19 from Screens.EventView import EventViewEPGSelect, EventViewSimple
20 from Screens.InputBox import InputBox
21 from Screens.MessageBox import MessageBox
22 from Screens.MinuteInput import MinuteInput
23 from Screens.TimerSelection import TimerSelection
24 from Screens.PictureInPicture import PictureInPicture
25 from Screens.SubtitleDisplay import SubtitleDisplay
26 from Screens.RdsDisplay import RdsInfoDisplay, RassInteractive
27 from Screens.TimeDateInput import TimeDateInput
28 from ServiceReference import ServiceReference
30 from Tools import Notifications
31 from Tools.Directories import SCOPE_HDD, resolveFilename, fileExists
33 from enigma import eTimer, eServiceCenter, eDVBServicePMTHandler, iServiceInformation, \
34 iPlayableService, eServiceReference, eEPGCache
36 from time import time, localtime, strftime
37 from os import stat as os_stat
38 from bisect import insort
40 from RecordTimer import RecordTimerEntry, RecordTimer
43 from Menu import MainMenu, mdom
47 self.dishDialog = self.session.instantiateDialog(Dish)
49 class InfoBarShowHide:
50 """ InfoBar show/hide control, accepts toggleShow and hide actions, might start
58 self["ShowHideActions"] = ActionMap( ["InfobarShowHideActions"] ,
60 "toggleShow": self.toggleShow,
62 }, 1) # lower prio to make it possible to override ok and cancel..
64 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
66 iPlayableService.evStart: self.serviceStarted,
69 self.__state = self.STATE_SHOWN
72 self.hideTimer = eTimer()
73 self.hideTimer.callback.append(self.doTimerHide)
74 self.hideTimer.start(5000, True)
76 self.onShow.append(self.__onShow)
77 self.onHide.append(self.__onHide)
79 def serviceStarted(self):
81 if config.usage.show_infobar_on_zap.value:
85 self.__state = self.STATE_SHOWN
88 def startHideTimer(self):
89 if self.__state == self.STATE_SHOWN and not self.__locked:
90 idx = config.usage.infobar_timeout.index
92 self.hideTimer.start(idx*1000, True)
95 self.__state = self.STATE_HIDDEN
101 def doTimerHide(self):
102 self.hideTimer.stop()
103 if self.__state == self.STATE_SHOWN:
106 def toggleShow(self):
107 if self.__state == self.STATE_SHOWN:
109 self.hideTimer.stop()
110 elif self.__state == self.STATE_HIDDEN:
114 self.__locked = self.__locked + 1
117 self.hideTimer.stop()
119 def unlockShow(self):
120 self.__locked = self.__locked - 1
122 self.startHideTimer()
124 # def startShow(self):
125 # self.instance.m_animation.startMoveAnimation(ePoint(0, 600), ePoint(0, 380), 100)
126 # self.__state = self.STATE_SHOWN
128 # def startHide(self):
129 # self.instance.m_animation.startMoveAnimation(ePoint(0, 380), ePoint(0, 600), 100)
130 # self.__state = self.STATE_HIDDEN
132 class NumberZap(Screen):
139 self.close(int(self["number"].getText()))
141 def keyNumberGlobal(self, number):
142 self.Timer.start(3000, True) #reset timer
143 self.field = self.field + str(number)
144 self["number"].setText(self.field)
145 if len(self.field) >= 4:
148 def __init__(self, session, number):
149 Screen.__init__(self, session)
150 self.field = str(number)
152 self["channel"] = Label(_("Channel:"))
154 self["number"] = Label(self.field)
156 self["actions"] = NumberActionMap( [ "SetupActions" ],
160 "1": self.keyNumberGlobal,
161 "2": self.keyNumberGlobal,
162 "3": self.keyNumberGlobal,
163 "4": self.keyNumberGlobal,
164 "5": self.keyNumberGlobal,
165 "6": self.keyNumberGlobal,
166 "7": self.keyNumberGlobal,
167 "8": self.keyNumberGlobal,
168 "9": self.keyNumberGlobal,
169 "0": self.keyNumberGlobal
172 self.Timer = eTimer()
173 self.Timer.callback.append(self.keyOK)
174 self.Timer.start(3000, True)
176 class InfoBarNumberZap:
177 """ Handles an initial number for NumberZapping """
179 self["NumberActions"] = NumberActionMap( [ "NumberActions"],
181 "1": self.keyNumberGlobal,
182 "2": self.keyNumberGlobal,
183 "3": self.keyNumberGlobal,
184 "4": self.keyNumberGlobal,
185 "5": self.keyNumberGlobal,
186 "6": self.keyNumberGlobal,
187 "7": self.keyNumberGlobal,
188 "8": self.keyNumberGlobal,
189 "9": self.keyNumberGlobal,
190 "0": self.keyNumberGlobal,
193 def keyNumberGlobal(self, number):
194 # print "You pressed number " + str(number)
196 if isinstance(self, InfoBarPiP) and self.pipHandles0Action():
197 self.pipDoHandle0Action()
199 self.servicelist.recallPrevService()
201 if self.has_key("TimeshiftActions") and not self.timeshift_enabled:
202 self.session.openWithCallback(self.numberEntered, NumberZap, number)
204 def numberEntered(self, retval):
205 # print self.servicelist
207 self.zapToNumber(retval)
209 def searchNumberHelper(self, serviceHandler, num, bouquet):
210 servicelist = serviceHandler.list(bouquet)
211 if not servicelist is None:
213 serviceIterator = servicelist.getNext()
214 if not serviceIterator.valid(): #check end of list
216 playable = not (serviceIterator.flags & (eServiceReference.isMarker|eServiceReference.isDirectory))
219 if not num: #found service with searched number ?
220 return serviceIterator, 0
223 def zapToNumber(self, number):
224 bouquet = self.servicelist.bouquet_root
226 serviceHandler = eServiceCenter.getInstance()
227 if not config.usage.multibouquet.value:
228 service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
230 bouquetlist = serviceHandler.list(bouquet)
231 if not bouquetlist is None:
233 bouquet = bouquetlist.getNext()
234 if not bouquet.valid(): #check end of list
236 if bouquet.flags & eServiceReference.isDirectory:
237 service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
238 if not service is None:
239 if self.servicelist.getRoot() != bouquet: #already in correct bouquet?
240 self.servicelist.clearPath()
241 if self.servicelist.bouquet_root != bouquet:
242 self.servicelist.enterPath(self.servicelist.bouquet_root)
243 self.servicelist.enterPath(bouquet)
244 self.servicelist.setCurrentSelection(service) #select the service in servicelist
245 self.servicelist.zap()
247 config.misc.initialchannelselection = ConfigBoolean(default = True)
249 class InfoBarChannelSelection:
250 """ ChannelSelection - handles the channelSelection dialog and the initial
251 channelChange actions which open the channelSelection dialog """
254 self.servicelist = self.session.instantiateDialog(ChannelSelection)
256 if config.misc.initialchannelselection.value:
257 self.onShown.append(self.firstRun)
259 self["ChannelSelectActions"] = HelpableActionMap(self, "InfobarChannelSelection",
261 "switchChannelUp": (self.switchChannelUp, _("open servicelist(up)")),
262 "switchChannelDown": (self.switchChannelDown, _("open servicelist(down)")),
263 "zapUp": (self.zapUp, _("previous channel")),
264 "zapDown": (self.zapDown, _("next channel")),
265 "historyBack": (self.historyBack, _("previous channel in history")),
266 "historyNext": (self.historyNext, _("next channel in history")),
267 "openServiceList": (self.openServiceList, _("open servicelist")),
270 def showTvChannelList(self, zap=False):
271 self.servicelist.setModeTv()
273 self.servicelist.zap()
274 self.session.execDialog(self.servicelist)
276 def showRadioChannelList(self, zap=False):
277 self.servicelist.setModeRadio()
279 self.servicelist.zap()
280 self.session.execDialog(self.servicelist)
283 self.onShown.remove(self.firstRun)
284 config.misc.initialchannelselection.value = False
285 config.misc.initialchannelselection.save()
286 self.switchChannelDown()
288 def historyBack(self):
289 self.servicelist.historyBack()
291 def historyNext(self):
292 self.servicelist.historyNext()
294 def switchChannelUp(self):
295 self.servicelist.moveUp()
296 self.session.execDialog(self.servicelist)
298 def switchChannelDown(self):
299 self.servicelist.moveDown()
300 self.session.execDialog(self.servicelist)
302 def openServiceList(self):
303 self.session.execDialog(self.servicelist)
306 if self.servicelist.inBouquet():
307 prev = self.servicelist.getCurrentSelection()
309 prev = prev.toString()
311 if config.usage.quickzap_bouquet_change.value:
312 if self.servicelist.atBegin():
313 self.servicelist.prevBouquet()
314 self.servicelist.moveUp()
315 cur = self.servicelist.getCurrentSelection()
316 if not cur or (not (cur.flags & 64)) or cur.toString() == prev:
319 self.servicelist.moveUp()
320 self.servicelist.zap()
323 if self.servicelist.inBouquet():
324 prev = self.servicelist.getCurrentSelection()
326 prev = prev.toString()
328 if config.usage.quickzap_bouquet_change.value and self.servicelist.atEnd():
329 self.servicelist.nextBouquet()
331 self.servicelist.moveDown()
332 cur = self.servicelist.getCurrentSelection()
333 if not cur or (not (cur.flags & 64)) or cur.toString() == prev:
336 self.servicelist.moveDown()
337 self.servicelist.zap()
340 """ Handles a menu action, to open the (main) menu """
342 self["MenuActions"] = HelpableActionMap(self, "InfobarMenuActions",
344 "mainMenu": (self.mainMenu, _("Enter main menu...")),
346 self.session.infobar = None
349 print "loading mainmenu XML..."
350 menu = mdom.getroot()
351 assert menu.tag == "menu", "root element in menu must be 'menu'!"
353 self.session.infobar = self
354 # so we can access the currently active infobar from screens opened from within the mainmenu
355 # at the moment used from the SubserviceSelection
357 self.session.openWithCallback(self.mainMenuClosed, MainMenu, menu)
359 def mainMenuClosed(self, *val):
360 self.session.infobar = None
362 class InfoBarSimpleEventView:
363 """ Opens the Eventview for now/next """
365 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
367 "showEventInfo": (self.openEventView, _("show event details")),
368 "showInfobarOrEpgWhenInfobarAlreadyVisible": self.showEventInfoWhenNotVisible,
371 def showEventInfoWhenNotVisible(self):
378 def openEventView(self):
380 self.epglist = epglist
381 service = self.session.nav.getCurrentService()
382 ref = self.session.nav.getCurrentlyPlayingServiceReference()
383 info = service.info()
391 self.session.open(EventViewSimple, epglist[0], ServiceReference(ref), self.eventViewCallback)
393 def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
394 epglist = self.epglist
397 epglist[0] = epglist[1]
401 class SimpleServicelist:
402 def __init__(self, services):
403 self.services = services
404 self.length = len(services)
407 def selectService(self, service):
413 while self.services[self.current].ref != service:
415 if self.current >= self.length:
419 def nextService(self):
422 if self.current+1 < self.length:
427 def prevService(self):
430 if self.current-1 > -1:
433 self.current = self.length - 1
435 def currentService(self):
436 if not self.length or self.current >= self.length:
438 return self.services[self.current]
441 """ EPG - Opens an EPG list when the showEPGList action fires """
443 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
445 iPlayableService.evUpdatedEventInfo: self.__evEventInfoChanged,
448 self.is_now_next = False
450 self.bouquetSel = None
451 self.eventView = None
452 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
454 "showEventInfo": (self.openEventView, _("show EPG...")),
455 "showEventInfoPlugin": (self.showEventInfoPlugins, _("show single service EPG...")),
456 "showInfobarOrEpgWhenInfobarAlreadyVisible": self.showEventInfoWhenNotVisible,
459 def showEventInfoWhenNotVisible(self):
466 def zapToService(self, service):
467 if not service is None:
468 if self.servicelist.getRoot() != self.epg_bouquet: #already in correct bouquet?
469 self.servicelist.clearPath()
470 if self.servicelist.bouquet_root != self.epg_bouquet:
471 self.servicelist.enterPath(self.servicelist.bouquet_root)
472 self.servicelist.enterPath(self.epg_bouquet)
473 self.servicelist.setCurrentSelection(service) #select the service in servicelist
474 self.servicelist.zap()
476 def getBouquetServices(self, bouquet):
478 servicelist = eServiceCenter.getInstance().list(bouquet)
479 if not servicelist is None:
481 service = servicelist.getNext()
482 if not service.valid(): #check if end of list
484 if service.flags & (eServiceReference.isDirectory | eServiceReference.isMarker): #ignore non playable services
486 services.append(ServiceReference(service))
489 def openBouquetEPG(self, bouquet, withCallback=True):
490 services = self.getBouquetServices(bouquet)
492 self.epg_bouquet = bouquet
494 self.dlg_stack.append(self.session.openWithCallback(self.closed, EPGSelection, services, self.zapToService, None, self.changeBouquetCB))
496 self.session.open(EPGSelection, services, self.zapToService, None, self.changeBouquetCB)
498 def changeBouquetCB(self, direction, epg):
501 self.bouquetSel.down()
504 bouquet = self.bouquetSel.getCurrent()
505 services = self.getBouquetServices(bouquet)
507 self.epg_bouquet = bouquet
508 epg.setServices(services)
510 def closed(self, ret=False):
511 closedScreen = self.dlg_stack.pop()
512 if self.bouquetSel and closedScreen == self.bouquetSel:
513 self.bouquetSel = None
514 elif self.eventView and closedScreen == self.eventView:
515 self.eventView = None
517 dlgs=len(self.dlg_stack)
519 self.dlg_stack[dlgs-1].close(dlgs > 1)
521 def openMultiServiceEPG(self, withCallback=True):
522 bouquets = self.servicelist.getBouquetList()
527 if cnt > 1: # show bouquet list
529 self.bouquetSel = self.session.openWithCallback(self.closed, BouquetSelector, bouquets, self.openBouquetEPG, enableWrapAround=True)
530 self.dlg_stack.append(self.bouquetSel)
532 self.bouquetSel = self.session.open(BouquetSelector, bouquets, self.openBouquetEPG, enableWrapAround=True)
534 self.openBouquetEPG(bouquets[0][1], withCallback)
536 def changeServiceCB(self, direction, epg):
539 self.serviceSel.nextService()
541 self.serviceSel.prevService()
542 epg.setService(self.serviceSel.currentService())
544 def SingleServiceEPGClosed(self, ret=False):
545 self.serviceSel = None
547 def openSingleServiceEPG(self):
548 ref=self.session.nav.getCurrentlyPlayingServiceReference()
550 if self.servicelist.getMutableList() is not None: # bouquet in channellist
551 current_path = self.servicelist.getRoot()
552 services = self.getBouquetServices(current_path)
553 self.serviceSel = SimpleServicelist(services)
554 if self.serviceSel.selectService(ref):
555 self.session.openWithCallback(self.SingleServiceEPGClosed, EPGSelection, ref, serviceChangeCB = self.changeServiceCB)
557 self.session.openWithCallback(self.SingleServiceEPGClosed, EPGSelection, ref)
559 self.session.open(EPGSelection, ref)
561 def showEventInfoPlugins(self):
562 list = [(p.name, boundFunction(self.runPlugin, p)) for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EVENTINFO)]
565 list.append((_("show single service EPG..."), self.openSingleServiceEPG))
566 self.session.openWithCallback(self.EventInfoPluginChosen, ChoiceBox, title=_("Please choose an extension..."), list = list, skin_name = "EPGExtensionsList")
568 self.openSingleServiceEPG()
570 def runPlugin(self, plugin):
571 plugin(session = self.session, servicelist = self.servicelist)
573 def EventInfoPluginChosen(self, answer):
574 if answer is not None:
577 def openSimilarList(self, eventid, refstr):
578 self.session.open(EPGSelection, refstr, None, eventid)
580 def getNowNext(self):
582 service = self.session.nav.getCurrentService()
583 info = service and service.info()
584 ptr = info and info.getEvent(0)
587 ptr = info and info.getEvent(1)
590 self.epglist = epglist
592 def __evEventInfoChanged(self):
593 if self.is_now_next and len(self.dlg_stack) == 1:
595 assert self.eventView
597 self.eventView.setEvent(self.epglist[0])
599 def openEventView(self):
600 ref = self.session.nav.getCurrentlyPlayingServiceReference()
602 epglist = self.epglist
604 self.is_now_next = False
605 epg = eEPGCache.getInstance()
606 ptr = ref and ref.valid() and epg.lookupEventTime(ref, -1)
609 ptr = epg.lookupEventTime(ref, ptr.getBeginTime(), +1)
613 self.is_now_next = True
615 self.eventView = self.session.openWithCallback(self.closed, EventViewEPGSelect, self.epglist[0], ServiceReference(ref), self.eventViewCallback, self.openSingleServiceEPG, self.openMultiServiceEPG, self.openSimilarList)
616 self.dlg_stack.append(self.eventView)
618 print "no epg for the service avail.. so we show multiepg instead of eventinfo"
619 self.openMultiServiceEPG(False)
621 def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
622 epglist = self.epglist
625 epglist[0]=epglist[1]
629 class InfoBarRdsDecoder:
630 """provides RDS and Rass support/display"""
632 self.rds_display = self.session.instantiateDialog(RdsInfoDisplay)
633 self.rass_interactive = None
635 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
637 iPlayableService.evEnd: self.__serviceStopped,
638 iPlayableService.evUpdatedRassSlidePic: self.RassSlidePicChanged
641 self["RdsActions"] = ActionMap(["InfobarRdsActions"],
643 "startRassInteractive": self.startRassInteractive
646 self["RdsActions"].setEnabled(False)
648 self.onLayoutFinish.append(self.rds_display.show)
649 self.rds_display.onRassInteractivePossibilityChanged.append(self.RassInteractivePossibilityChanged)
651 def RassInteractivePossibilityChanged(self, state):
652 self["RdsActions"].setEnabled(state)
654 def RassSlidePicChanged(self):
655 if not self.rass_interactive:
656 service = self.session.nav.getCurrentService()
657 decoder = service and service.rdsDecoder()
659 decoder.showRassSlidePicture()
661 def __serviceStopped(self):
662 if self.rass_interactive is not None:
663 rass_interactive = self.rass_interactive
664 self.rass_interactive = None
665 rass_interactive.close()
667 def startRassInteractive(self):
668 self.rds_display.hide()
669 self.rass_interactive = self.session.openWithCallback(self.RassInteractiveClosed, RassInteractive)
671 def RassInteractiveClosed(self, *val):
672 if self.rass_interactive is not None:
673 self.rass_interactive = None
674 self.RassSlidePicChanged()
675 self.rds_display.show()
678 """handles actions like seeking, pause"""
680 SEEK_STATE_PLAY = (0, 0, 0, ">")
681 SEEK_STATE_PAUSE = (1, 0, 0, "||")
682 SEEK_STATE_EOF = (1, 0, 0, "END")
684 def __init__(self, actionmap = "InfobarSeekActions", useSeekBackHack=True):
685 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
687 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
688 iPlayableService.evStart: self.__serviceStarted,
690 iPlayableService.evEOF: self.__evEOF,
691 iPlayableService.evSOF: self.__evSOF,
694 self.minSpeedBackward = useSeekBackHack and 16 or 0
696 class InfoBarSeekActionMap(HelpableActionMap):
697 def __init__(self, screen, *args, **kwargs):
698 HelpableActionMap.__init__(self, screen, *args, **kwargs)
701 def action(self, contexts, action):
702 print "action:", action
703 if action[:5] == "seek:":
704 time = int(action[5:])
705 self.screen.doSeekRelative(time * 90000)
707 elif action[:8] == "seekdef:":
708 key = int(action[8:])
709 time = (-config.seek.selfdefined_13.value, False, config.seek.selfdefined_13.value,
710 -config.seek.selfdefined_46.value, False, config.seek.selfdefined_46.value,
711 -config.seek.selfdefined_79.value, False, config.seek.selfdefined_79.value)[key-1]
712 self.screen.doSeekRelative(time * 90000)
715 return HelpableActionMap.action(self, contexts, action)
717 self["SeekActions"] = InfoBarSeekActionMap(self, actionmap,
719 "playpauseService": self.playpauseService,
720 "pauseService": (self.pauseService, _("pause")),
721 "unPauseService": (self.unPauseService, _("continue")),
723 "seekFwd": (self.seekFwd, _("skip forward")),
724 "seekFwdManual": (self.seekFwdManual, _("skip forward (enter time)")),
725 "seekBack": (self.seekBack, _("skip backward")),
726 "seekBackManual": (self.seekBackManual, _("skip backward (enter time)"))
728 # give them a little more priority to win over color buttons
730 self["SeekActions"].setEnabled(False)
732 self.seekstate = self.SEEK_STATE_PLAY
733 self.lastseekstate = self.SEEK_STATE_PLAY
735 self.onPlayStateChanged = [ ]
737 self.lockedBecauseOfSkipping = False
739 self.__seekableStatusChanged()
741 def makeStateForward(self, n):
742 minspeed = config.seek.stepwise_minspeed.value
743 repeat = int(config.seek.stepwise_repeat.value)
744 if minspeed != "Never" and n >= int(minspeed) and repeat > 1:
745 return (0, n * repeat, repeat, ">> %dx" % n)
747 return (0, n, 0, ">> %dx" % n)
749 def makeStateBackward(self, n):
750 minspeed = config.seek.stepwise_minspeed.value
751 repeat = int(config.seek.stepwise_repeat.value)
752 if self.minSpeedBackward and n < self.minSpeedBackward:
753 r = (self.minSpeedBackward - 1)/ n + 1
754 if minspeed != "Never" and n >= int(minspeed) and repeat > 1:
756 return (0, -n * r, r, "<< %dx" % n)
757 elif minspeed != "Never" and n >= int(minspeed) and repeat > 1:
758 return (0, -n * repeat, repeat, "<< %dx" % n)
760 return (0, -n, 0, "<< %dx" % n)
762 def makeStateSlowMotion(self, n):
763 return (0, 0, n, "/%d" % n)
765 def isStateForward(self, state):
768 def isStateBackward(self, state):
771 def isStateSlowMotion(self, state):
772 return state[1] == 0 and state[2] > 1
774 def getHigher(self, n, lst):
780 def getLower(self, n, lst):
788 def showAfterSeek(self):
789 if isinstance(self, InfoBarShowHide):
799 service = self.session.nav.getCurrentService()
803 seek = service.seek()
805 if seek is None or not seek.isCurrentlySeekable():
810 def isSeekable(self):
811 if self.getSeek() is None:
815 def __seekableStatusChanged(self):
816 # print "seekable status changed!"
817 if not self.isSeekable():
818 self["SeekActions"].setEnabled(False)
819 # print "not seekable, return to play"
820 self.setSeekState(self.SEEK_STATE_PLAY)
822 self["SeekActions"].setEnabled(True)
825 def __serviceStarted(self):
826 self.seekstate = self.SEEK_STATE_PLAY
827 self.__seekableStatusChanged()
829 def setSeekState(self, state):
830 service = self.session.nav.getCurrentService()
835 if not self.isSeekable():
836 if state not in (self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE):
837 state = self.SEEK_STATE_PLAY
839 pauseable = service.pause()
841 if pauseable is None:
842 print "not pauseable."
843 state = self.SEEK_STATE_PLAY
845 self.seekstate = state
847 if pauseable is not None:
848 if self.seekstate[0]:
849 print "resolved to PAUSE"
851 elif self.seekstate[1]:
852 print "resolved to FAST FORWARD"
853 pauseable.setFastForward(self.seekstate[1])
854 elif self.seekstate[2]:
855 print "resolved to SLOW MOTION"
856 pauseable.setSlowMotion(self.seekstate[2])
858 print "resolved to PLAY"
861 for c in self.onPlayStateChanged:
864 self.checkSkipShowHideLock()
868 def playpauseService(self):
869 if self.seekstate != self.SEEK_STATE_PLAY:
870 self.unPauseService()
874 def pauseService(self):
875 if self.seekstate == self.SEEK_STATE_PAUSE:
876 if config.seek.on_pause.value == "play":
877 self.unPauseService()
878 elif config.seek.on_pause.value == "step":
879 self.doSeekRelative(0)
880 elif config.seek.on_pause.value == "last":
881 self.setSeekState(self.lastseekstate)
882 self.lastseekstate = self.SEEK_STATE_PLAY
884 if self.seekstate != self.SEEK_STATE_EOF:
885 self.lastseekstate = self.seekstate
886 self.setSeekState(self.SEEK_STATE_PAUSE);
888 def unPauseService(self):
890 if self.seekstate == self.SEEK_STATE_PLAY:
892 self.setSeekState(self.SEEK_STATE_PLAY)
894 def doSeek(self, pts):
895 seekable = self.getSeek()
900 def doSeekRelative(self, pts):
901 seekable = self.getSeek()
904 prevstate = self.seekstate
906 if self.seekstate == self.SEEK_STATE_EOF:
907 if prevstate == self.SEEK_STATE_PAUSE:
908 self.setSeekState(self.SEEK_STATE_PAUSE)
910 self.setSeekState(self.SEEK_STATE_PLAY)
911 seekable.seekRelative(pts<0 and -1 or 1, abs(pts))
912 if abs(pts) > 100 and config.usage.show_infobar_on_skip.value:
916 if self.seekstate == self.SEEK_STATE_PLAY:
917 self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
918 elif self.seekstate == self.SEEK_STATE_PAUSE:
919 if len(config.seek.speeds_slowmotion.value):
920 self.setSeekState(self.makeStateSlowMotion(config.seek.speeds_slowmotion.value[-1]))
922 self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
923 elif self.seekstate == self.SEEK_STATE_EOF:
925 elif self.isStateForward(self.seekstate):
926 speed = self.seekstate[1]
927 if self.seekstate[2]:
928 speed /= self.seekstate[2]
929 speed = self.getHigher(speed, config.seek.speeds_forward.value) or config.seek.speeds_forward.value[-1]
930 self.setSeekState(self.makeStateForward(speed))
931 elif self.isStateBackward(self.seekstate):
932 speed = -self.seekstate[1]
933 if self.seekstate[2]:
934 speed /= self.seekstate[2]
935 speed = self.getLower(speed, config.seek.speeds_backward.value)
937 self.setSeekState(self.makeStateBackward(speed))
939 self.setSeekState(self.SEEK_STATE_PLAY)
940 elif self.isStateSlowMotion(self.seekstate):
941 speed = self.getLower(self.seekstate[2], config.seek.speeds_slowmotion.value) or config.seek.speeds_slowmotion.value[0]
942 self.setSeekState(self.makeStateSlowMotion(speed))
945 seekstate = self.seekstate
946 if seekstate == self.SEEK_STATE_PLAY:
947 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
948 elif seekstate == self.SEEK_STATE_EOF:
949 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
950 self.doSeekRelative(-6)
951 elif seekstate == self.SEEK_STATE_PAUSE:
952 self.doSeekRelative(-3)
953 elif self.isStateForward(seekstate):
956 speed /= seekstate[2]
957 speed = self.getLower(speed, config.seek.speeds_forward.value)
959 self.setSeekState(self.makeStateForward(speed))
961 self.setSeekState(self.SEEK_STATE_PLAY)
962 elif self.isStateBackward(seekstate):
963 speed = -seekstate[1]
965 speed /= seekstate[2]
966 speed = self.getHigher(speed, config.seek.speeds_backward.value) or config.seek.speeds_backward.value[-1]
967 self.setSeekState(self.makeStateBackward(speed))
968 elif self.isStateSlowMotion(seekstate):
969 speed = self.getHigher(seekstate[2], config.seek.speeds_slowmotion.value)
971 self.setSeekState(self.makeStateSlowMotion(speed))
973 self.setSeekState(self.SEEK_STATE_PAUSE)
975 def seekFwdManual(self):
976 self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
978 def fwdSeekTo(self, minutes):
979 print "Seek", minutes, "minutes forward"
980 self.doSeekRelative(minutes * 60 * 90000)
982 def seekBackManual(self):
983 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
985 def rwdSeekTo(self, minutes):
987 self.doSeekRelative(-minutes * 60 * 90000)
989 def checkSkipShowHideLock(self):
990 wantlock = self.seekstate != self.SEEK_STATE_PLAY
992 if config.usage.show_infobar_on_skip.value:
993 if self.lockedBecauseOfSkipping and not wantlock:
995 self.lockedBecauseOfSkipping = False
997 if wantlock and not self.lockedBecauseOfSkipping:
999 self.lockedBecauseOfSkipping = True
1001 def calcRemainingTime(self):
1002 seekable = self.getSeek()
1003 if seekable is not None:
1004 len = seekable.getLength()
1006 tmp = self.cueGetEndCutPosition()
1011 pos = seekable.getPlayPosition()
1012 speednom = self.seekstate[1] or 1
1013 speedden = self.seekstate[2] or 1
1014 if not len[0] and not pos[0]:
1015 if len[1] <= pos[1]:
1017 time = (len[1] - pos[1])*speedden/(90*speednom)
1022 if self.seekstate == self.SEEK_STATE_EOF:
1025 # if we are seeking forward, we try to end up ~1s before the end, and pause there.
1026 seekstate = self.seekstate
1027 if self.seekstate != self.SEEK_STATE_PAUSE:
1028 self.setSeekState(self.SEEK_STATE_EOF)
1030 if seekstate not in (self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE): # if we are seeking
1031 seekable = self.getSeek()
1032 if seekable is not None:
1034 if seekstate == self.SEEK_STATE_PLAY: # regular EOF
1035 self.doEofInternal(True)
1037 self.doEofInternal(False)
1039 def doEofInternal(self, playing):
1040 pass # Defined in subclasses
1043 self.setSeekState(self.SEEK_STATE_PLAY)
1046 from Screens.PVRState import PVRState, TimeshiftState
1048 class InfoBarPVRState:
1049 def __init__(self, screen=PVRState, force_show = False):
1050 self.onPlayStateChanged.append(self.__playStateChanged)
1051 self.pvrStateDialog = self.session.instantiateDialog(screen)
1052 self.onShow.append(self._mayShow)
1053 self.onHide.append(self.pvrStateDialog.hide)
1054 self.force_show = force_show
1057 if self.execing and self.seekstate != self.SEEK_STATE_PLAY:
1058 self.pvrStateDialog.show()
1060 def __playStateChanged(self, state):
1061 playstateString = state[3]
1062 self.pvrStateDialog["state"].setText(playstateString)
1064 # if we return into "PLAY" state, ensure that the dialog gets hidden if there will be no infobar displayed
1065 if not config.usage.show_infobar_on_skip.value and self.seekstate == self.SEEK_STATE_PLAY and not self.force_show:
1066 self.pvrStateDialog.hide()
1071 class InfoBarTimeshiftState(InfoBarPVRState):
1073 InfoBarPVRState.__init__(self, screen=TimeshiftState, force_show = True)
1076 if self.execing and self.timeshift_enabled and self.seekstate != self.SEEK_STATE_PLAY:
1077 self.pvrStateDialog.show()
1079 class InfoBarShowMovies:
1081 # i don't really like this class.
1082 # it calls a not further specified "movie list" on up/down/movieList,
1083 # so this is not more than an action map
1085 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions",
1087 "movieList": (self.showMovies, _("movie list")),
1088 "up": (self.showMovies, _("movie list")),
1089 "down": (self.showMovies, _("movie list"))
1092 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
1096 # Timeshift works the following way:
1097 # demux0 demux1 "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
1098 # - normal playback TUNER unused PLAY enable disable disable
1099 # - user presses "yellow" button. FILE record PAUSE enable disable enable
1100 # - user presess pause again FILE record PLAY enable disable enable
1101 # - user fast forwards FILE record FF enable disable enable
1102 # - end of timeshift buffer reached TUNER record PLAY enable enable disable
1103 # - user backwards FILE record BACK # !! enable disable enable
1107 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
1108 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
1109 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
1110 # - the user can now PVR around
1111 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
1112 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
1114 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
1115 # - if the user rewinds, or press pause, timeshift will be activated again
1117 # note that a timeshift can be enabled ("recording") and
1118 # activated (currently time-shifting).
1120 class InfoBarTimeshift:
1122 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions",
1124 "timeshiftStart": (self.startTimeshift, _("start timeshift")), # the "yellow key"
1125 "timeshiftStop": (self.stopTimeshift, _("stop timeshift")) # currently undefined :), probably 'TV'
1127 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
1129 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "rewind key"
1130 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause # something like "pause key"
1131 }, prio=-1) # priority over record
1133 self.timeshift_enabled = 0
1134 self.timeshift_state = 0
1135 self.ts_rewind_timer = eTimer()
1136 self.ts_rewind_timer.callback.append(self.rewindService)
1138 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1140 iPlayableService.evStart: self.__serviceStarted,
1141 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged
1144 def getTimeshift(self):
1145 service = self.session.nav.getCurrentService()
1146 return service and service.timeshift()
1148 def startTimeshift(self):
1149 print "enable timeshift"
1150 ts = self.getTimeshift()
1152 self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
1153 print "no ts interface"
1156 if self.timeshift_enabled:
1157 print "hu, timeshift already enabled?"
1159 if not ts.startTimeshift():
1160 self.timeshift_enabled = 1
1162 # we remove the "relative time" for now.
1163 #self.pvrStateDialog["timeshift"].setRelative(time.time())
1166 #self.setSeekState(self.SEEK_STATE_PAUSE)
1167 self.activateTimeshiftEnd(False)
1169 # enable the "TimeshiftEnableActions", which will override
1170 # the startTimeshift actions
1171 self.__seekableStatusChanged()
1173 print "timeshift failed"
1175 def stopTimeshift(self):
1176 if not self.timeshift_enabled:
1178 print "disable timeshift"
1179 ts = self.getTimeshift()
1182 self.session.openWithCallback(self.stopTimeshiftConfirmed, MessageBox, _("Stop Timeshift?"), MessageBox.TYPE_YESNO)
1184 def stopTimeshiftConfirmed(self, confirmed):
1188 ts = self.getTimeshift()
1193 self.timeshift_enabled = 0
1196 self.__seekableStatusChanged()
1198 # activates timeshift, and seeks to (almost) the end
1199 def activateTimeshiftEnd(self, back = True):
1200 ts = self.getTimeshift()
1201 print "activateTimeshiftEnd"
1206 if ts.isTimeshiftActive():
1207 print "!! activate timeshift called - but shouldn't this be a normal pause?"
1211 ts.activateTimeshift() # activate timeshift will automatically pause
1212 self.setSeekState(self.SEEK_STATE_PAUSE)
1215 self.doSeek(-5) # seek some gops before end
1216 self.ts_rewind_timer.start(200, 1)
1218 self.doSeek(-1) # seek 1 gop before end
1220 def rewindService(self):
1221 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1223 # same as activateTimeshiftEnd, but pauses afterwards.
1224 def activateTimeshiftEndAndPause(self):
1225 print "activateTimeshiftEndAndPause"
1226 #state = self.seekstate
1227 self.activateTimeshiftEnd(False)
1229 def __seekableStatusChanged(self):
1232 # print "self.isSeekable", self.isSeekable()
1233 # print "self.timeshift_enabled", self.timeshift_enabled
1235 # when this service is not seekable, but timeshift
1236 # is enabled, this means we can activate
1238 if not self.isSeekable() and self.timeshift_enabled:
1241 # print "timeshift activate:", enabled
1242 self["TimeshiftActivateActions"].setEnabled(enabled)
1244 def __serviceStarted(self):
1245 self.timeshift_enabled = False
1246 self.__seekableStatusChanged()
1248 from Screens.PiPSetup import PiPSetup
1250 class InfoBarExtensions:
1251 EXTENSION_SINGLE = 0
1257 self["InstantExtensionsActions"] = HelpableActionMap(self, "InfobarExtensions",
1259 "extensions": (self.showExtensionSelection, _("view extensions...")),
1260 }, 1) # lower priority
1262 def addExtension(self, extension, key = None, type = EXTENSION_SINGLE):
1263 self.list.append((type, extension, key))
1265 def updateExtension(self, extension, key = None):
1266 self.extensionsList.append(extension)
1268 if self.extensionKeys.has_key(key):
1272 for x in self.availableKeys:
1273 if not self.extensionKeys.has_key(x):
1278 self.extensionKeys[key] = len(self.extensionsList) - 1
1280 def updateExtensions(self):
1281 self.extensionsList = []
1282 self.availableKeys = [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "red", "green", "yellow", "blue" ]
1283 self.extensionKeys = {}
1285 if x[0] == self.EXTENSION_SINGLE:
1286 self.updateExtension(x[1], x[2])
1289 self.updateExtension(y[0], y[1])
1292 def showExtensionSelection(self):
1293 self.updateExtensions()
1294 extensionsList = self.extensionsList[:]
1297 for x in self.availableKeys:
1298 if self.extensionKeys.has_key(x):
1299 entry = self.extensionKeys[x]
1300 extension = self.extensionsList[entry]
1302 name = str(extension[0]())
1303 list.append((extension[0](), extension))
1305 extensionsList.remove(extension)
1307 extensionsList.remove(extension)
1308 list.extend([(x[0](), x) for x in extensionsList])
1310 keys += [""] * len(extensionsList)
1311 self.session.openWithCallback(self.extensionCallback, ChoiceBox, title=_("Please choose an extension..."), list = list, keys = keys, skin_name = "ExtensionsList")
1313 def extensionCallback(self, answer):
1314 if answer is not None:
1317 from Tools.BoundFunction import boundFunction
1319 # depends on InfoBarExtensions
1321 class InfoBarPlugins:
1323 self.addExtension(extension = self.getPluginList, type = InfoBarExtensions.EXTENSION_LIST)
1325 def getPluginName(self, name):
1328 def getPluginList(self):
1329 list = [((boundFunction(self.getPluginName, p.name), boundFunction(self.runPlugin, p), lambda: True), None, p.name) for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EXTENSIONSMENU)]
1330 list.sort(key = lambda e: e[2]) # sort by name
1333 def runPlugin(self, plugin):
1334 if isinstance(self, InfoBarChannelSelection):
1335 plugin(session = self.session, servicelist = self.servicelist)
1337 plugin(session = self.session)
1339 from Components.Task import job_manager
1340 class InfoBarJobman:
1342 self.addExtension(extension = self.getJobList, type = InfoBarExtensions.EXTENSION_LIST)
1344 def getJobList(self):
1345 return [((boundFunction(self.getJobName, job), boundFunction(self.showJobView, job), lambda: True), None) for job in job_manager.getPendingJobs()]
1347 def getJobName(self, job):
1348 return "%s: %s (%d%%)" % (job.getStatustext(), job.name, int(100*job.progress/float(job.end)))
1350 def showJobView(self, job):
1351 from Screens.TaskView import JobView
1352 job_manager.in_background = False
1353 self.session.openWithCallback(self.JobViewCB, JobView, job)
1355 def JobViewCB(self, in_background):
1356 job_manager.in_background = in_background
1358 # depends on InfoBarExtensions
1362 self.session.pipshown
1364 self.session.pipshown = False
1365 if SystemInfo.get("NumVideoDecoders", 1) > 1:
1367 self.addExtension((self.getShowHideName, self.showPiP, lambda: True), "blue")
1368 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1369 self.addExtension((self.getSwapName, self.swapPiP, self.pipShown), "yellow")
1371 self.addExtension((self.getShowHideName, self.showPiP, self.pipShown), "blue")
1372 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1375 return self.session.pipshown
1377 def pipHandles0Action(self):
1378 return self.pipShown() and config.usage.pip_zero_button.value != "standard"
1380 def getShowHideName(self):
1381 if self.session.pipshown:
1382 return _("Disable Picture in Picture")
1384 return _("Activate Picture in Picture")
1386 def getSwapName(self):
1387 return _("Swap Services")
1389 def getMoveName(self):
1390 return _("Move Picture in Picture")
1393 if self.session.pipshown:
1394 del self.session.pip
1395 self.session.pipshown = False
1397 self.session.pip = self.session.instantiateDialog(PictureInPicture)
1398 self.session.pip.show()
1399 newservice = self.session.nav.getCurrentlyPlayingServiceReference()
1400 if self.session.pip.playService(newservice):
1401 self.session.pipshown = True
1402 self.session.pip.servicePath = self.servicelist.getCurrentServicePath()
1404 self.session.pipshown = False
1405 del self.session.pip
1406 self.session.nav.playService(newservice)
1409 swapservice = self.session.nav.getCurrentlyPlayingServiceReference()
1410 if self.session.pip.servicePath:
1411 servicepath = self.servicelist.getCurrentServicePath()
1412 ref=servicepath[len(servicepath)-1]
1413 pipref=self.session.pip.getCurrentService()
1414 self.session.pip.playService(swapservice)
1415 self.servicelist.setCurrentServicePath(self.session.pip.servicePath)
1416 if pipref.toString() != ref.toString(): # is a subservice ?
1417 self.session.nav.stopService() # stop portal
1418 self.session.nav.playService(pipref) # start subservice
1419 self.session.pip.servicePath=servicepath
1422 self.session.open(PiPSetup, pip = self.session.pip)
1424 def pipDoHandle0Action(self):
1425 use = config.usage.pip_zero_button.value
1428 elif "swapstop" == use:
1434 from RecordTimer import parseEvent, RecordTimerEntry
1436 class InfoBarInstantRecord:
1437 """Instant Record - handles the instantRecord action in order to
1438 start/stop instant records"""
1440 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
1442 "instantRecord": (self.instantRecord, _("Instant Record...")),
1446 def stopCurrentRecording(self, entry = -1):
1447 if entry is not None and entry != -1:
1448 self.session.nav.RecordTimer.removeEntry(self.recording[entry])
1449 self.recording.remove(self.recording[entry])
1451 def startInstantRecording(self, limitEvent = False):
1452 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
1454 # try to get event info
1457 service = self.session.nav.getCurrentService()
1458 epg = eEPGCache.getInstance()
1459 event = epg.lookupEventTime(serviceref, -1, 0)
1461 info = service.info()
1462 ev = info.getEvent(0)
1468 end = begin + 3600 # dummy
1469 name = "instant record"
1473 if event is not None:
1474 curEvent = parseEvent(event)
1476 description = curEvent[3]
1477 eventid = curEvent[4]
1482 self.session.open(MessageBox, _("No event info found, recording indefinitely."), MessageBox.TYPE_INFO)
1484 if isinstance(serviceref, eServiceReference):
1485 serviceref = ServiceReference(serviceref)
1487 recording = RecordTimerEntry(serviceref, begin, end, name, description, eventid, dirname = config.movielist.last_videodir.value)
1488 recording.dontSave = True
1490 if event is None or limitEvent == False:
1491 recording.autoincrease = True
1492 if recording.setAutoincreaseEnd():
1493 self.session.nav.RecordTimer.record(recording)
1494 self.recording.append(recording)
1496 simulTimerList = self.session.nav.RecordTimer.record(recording)
1497 if simulTimerList is not None: # conflict with other recording
1498 name = simulTimerList[1].name
1499 name_date = ' '.join((name, strftime('%c', localtime(simulTimerList[1].begin))))
1500 print "[TIMER] conflicts with", name_date
1501 recording.autoincrease = True # start with max available length, then increment
1502 if recording.setAutoincreaseEnd():
1503 self.session.nav.RecordTimer.record(recording)
1504 self.recording.append(recording)
1505 self.session.open(MessageBox, _("Record time limited due to conflicting timer %s") % name_date, MessageBox.TYPE_INFO)
1507 self.session.open(MessageBox, _("Couldn't record due to conflicting timer %s") % name, MessageBox.TYPE_INFO)
1508 recording.autoincrease = False
1510 self.recording.append(recording)
1512 def isInstantRecordRunning(self):
1513 print "self.recording:", self.recording
1515 for x in self.recording:
1520 def recordQuestionCallback(self, answer):
1521 print "pre:\n", self.recording
1523 if answer is None or answer[1] == "no":
1526 recording = self.recording[:]
1528 if not x in self.session.nav.RecordTimer.timer_list:
1529 self.recording.remove(x)
1530 elif x.dontSave and x.isRunning():
1531 list.append((x, False))
1533 if answer[1] == "changeduration":
1534 if len(self.recording) == 1:
1535 self.changeDuration(0)
1537 self.session.openWithCallback(self.changeDuration, TimerSelection, list)
1538 elif answer[1] == "changeendtime":
1539 if len(self.recording) == 1:
1542 self.session.openWithCallback(self.setEndtime, TimerSelection, list)
1543 elif answer[1] == "stop":
1544 if len(self.recording) == 1:
1545 self.stopCurrentRecording(0)
1547 self.session.openWithCallback(self.stopCurrentRecording, TimerSelection, list)
1548 elif answer[1] in ( "indefinitely" , "manualduration", "manualendtime", "event"):
1549 self.startInstantRecording(limitEvent = answer[1] in ("event", "manualendtime") or False)
1550 if answer[1] == "manualduration":
1551 self.changeDuration(len(self.recording)-1)
1552 elif answer[1] == "manualendtime":
1553 self.setEndtime(len(self.recording)-1)
1554 print "after:\n", self.recording
1556 def setEndtime(self, entry):
1557 if entry is not None and entry >= 0:
1558 self.selectedEntry = entry
1559 self.endtime=ConfigClock(default = self.recording[self.selectedEntry].end)
1560 dlg = self.session.openWithCallback(self.TimeDateInputClosed, TimeDateInput, self.endtime)
1561 dlg.setTitle(_("Please change recording endtime"))
1563 def TimeDateInputClosed(self, ret):
1566 localendtime = localtime(ret[1])
1567 print "stopping recording at", strftime("%c", localendtime)
1568 if self.recording[self.selectedEntry].end != ret[1]:
1569 self.recording[self.selectedEntry].autoincrease = False
1570 self.recording[self.selectedEntry].end = ret[1]
1571 self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
1573 def changeDuration(self, entry):
1574 if entry is not None and entry >= 0:
1575 self.selectedEntry = entry
1576 self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1578 def inputCallback(self, value):
1579 if value is not None:
1580 print "stopping recording after", int(value), "minutes."
1581 entry = self.recording[self.selectedEntry]
1583 entry.autoincrease = False
1584 entry.end = int(time()) + 60 * int(value)
1585 self.session.nav.RecordTimer.timeChanged(entry)
1587 def instantRecord(self):
1588 dir = config.movielist.last_videodir.value
1589 if not fileExists(dir, 'w'):
1590 dir = resolveFilename(SCOPE_HDD)
1594 # XXX: this message is a little odd as we might be recording to a remote device
1595 self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1598 if self.isInstantRecordRunning():
1599 self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1600 title=_("A recording is currently running.\nWhat do you want to do?"), \
1601 list=((_("stop recording"), "stop"), \
1602 (_("add recording (stop after current event)"), "event"), \
1603 (_("add recording (indefinitely)"), "indefinitely"), \
1604 (_("add recording (enter recording duration)"), "manualduration"), \
1605 (_("add recording (enter recording endtime)"), "manualendtime"), \
1606 (_("change recording (duration)"), "changeduration"), \
1607 (_("change recording (endtime)"), "changeendtime"), \
1608 (_("do nothing"), "no")))
1610 self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1611 title=_("Start recording?"), \
1612 list=((_("add recording (stop after current event)"), "event"), \
1613 (_("add recording (indefinitely)"), "indefinitely"), \
1614 (_("add recording (enter recording duration)"), "manualduration"), \
1615 (_("add recording (enter recording endtime)"), "manualendtime"), \
1616 (_("don't record"), "no")))
1618 from Tools.ISO639 import LanguageCodes
1620 class InfoBarAudioSelection:
1622 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions",
1624 "audioSelection": (self.audioSelection, _("Audio Options...")),
1627 def audioSelection(self):
1628 service = self.session.nav.getCurrentService()
1629 self.audioTracks = audio = service and service.audioTracks()
1630 n = audio and audio.getNumberOfTracks() or 0
1633 self.audioChannel = service.audioChannel()
1638 i = audio.getTrackInfo(idx)
1639 languages = i.getLanguage().split('/')
1640 description = i.getDescription()
1643 for lang in languages:
1646 if LanguageCodes.has_key(lang):
1647 language += LanguageCodes[lang][0]
1652 if len(description):
1653 description += " (" + language + ")"
1655 description = language
1657 tlist.append((description, idx))
1660 tlist.sort(key=lambda x: x[0])
1662 selectedAudio = self.audioTracks.getCurrentTrack()
1667 if x[1] != selectedAudio:
1675 if SystemInfo["CanDownmixAC3"]:
1676 flist = [(_("AC3 downmix") + " - " +(_("Off"), _("On"))[config.av.downmix_ac3.value and 1 or 0], "CALLFUNC", self.changeAC3Downmix),
1677 ((_("Left"), _("Stereo"), _("Right"))[self.audioChannel.getCurrentChannel()], "mode")]
1678 usedKeys.extend(["red", "green"])
1679 availableKeys.extend(["yellow", "blue"])
1682 flist = [((_("Left"), _("Stereo"), _("Right"))[self.audioChannel.getCurrentChannel()], "mode")]
1683 usedKeys.extend(["red"])
1684 availableKeys.extend(["green", "yellow", "blue"])
1687 if hasattr(self, "runPlugin"):
1689 def __init__(self, fnc, *args):
1692 def __call__(self, *args, **kwargs):
1693 self.fnc(*self.args)
1695 Plugins = [ (p.name, PluginCaller(self.runPlugin, p)) for p in plugins.getPlugins(where = PluginDescriptor.WHERE_AUDIOMENU) ]
1699 flist.append((p[0], "CALLFUNC", p[1]))
1701 usedKeys.append(availableKeys[0])
1702 del availableKeys[0]
1706 flist.append(("--", ""))
1710 keys = usedKeys + [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "0" ] + [""] * n
1711 self.session.openWithCallback(self.audioSelected, ChoiceBox, title=_("Select audio track"), list = flist + tlist, selection = selection, keys = keys, skin_name = "AudioTrackSelection")
1713 del self.audioTracks
1715 def changeAC3Downmix(self, arg):
1716 choicelist = self.session.current_dialog["list"]
1717 list = choicelist.list
1719 list[0][1]=(t[0], t[1], t[2], t[3], t[4], t[5], t[6],
1720 _("AC3 downmix") + " - " + (_("On"), _("Off"))[config.av.downmix_ac3.value and 1 or 0])
1721 choicelist.setList(list)
1722 if config.av.downmix_ac3.value:
1723 config.av.downmix_ac3.value = False
1725 config.av.downmix_ac3.value = True
1726 config.av.downmix_ac3.save()
1728 def audioSelected(self, audio):
1729 if audio is not None:
1730 if isinstance(audio[1], str):
1731 if audio[1] == "mode":
1732 keys = ["red", "green", "yellow"]
1733 selection = self.audioChannel.getCurrentChannel()
1734 tlist = ((_("left"), 0), (_("stereo"), 1), (_("right"), 2))
1735 self.session.openWithCallback(self.modeSelected, ChoiceBox, title=_("Select audio mode"), list = tlist, selection = selection, keys = keys, skin_name ="AudioModeSelection")
1737 del self.audioChannel
1738 if self.session.nav.getCurrentService().audioTracks().getNumberOfTracks() > audio[1]:
1739 self.audioTracks.selectTrack(audio[1])
1741 del self.audioChannel
1742 del self.audioTracks
1744 def modeSelected(self, mode):
1745 if mode is not None:
1746 self.audioChannel.selectChannel(mode[1])
1747 del self.audioChannel
1749 class InfoBarSubserviceSelection:
1751 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1753 "subserviceSelection": (self.subserviceSelection, _("Subservice list...")),
1756 self["SubserviceQuickzapAction"] = HelpableActionMap(self, "InfobarSubserviceQuickzapActions",
1758 "nextSubservice": (self.nextSubservice, _("Switch to next subservice")),
1759 "prevSubservice": (self.prevSubservice, _("Switch to previous subservice"))
1761 self["SubserviceQuickzapAction"].setEnabled(False)
1763 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1765 iPlayableService.evUpdatedEventInfo: self.checkSubservicesAvail
1770 def checkSubservicesAvail(self):
1771 service = self.session.nav.getCurrentService()
1772 subservices = service and service.subServices()
1773 if not subservices or subservices.getNumberOfSubservices() == 0:
1774 self["SubserviceQuickzapAction"].setEnabled(False)
1776 def nextSubservice(self):
1777 self.changeSubservice(+1)
1779 def prevSubservice(self):
1780 self.changeSubservice(-1)
1782 def changeSubservice(self, direction):
1783 service = self.session.nav.getCurrentService()
1784 subservices = service and service.subServices()
1785 n = subservices and subservices.getNumberOfSubservices()
1788 ref = self.session.nav.getCurrentlyPlayingServiceReference()
1791 if subservices.getSubservice(idx).toString() == ref.toString():
1796 selection += direction
1801 newservice = subservices.getSubservice(selection)
1802 if newservice.valid():
1805 self.session.nav.playService(newservice, False)
1807 def subserviceSelection(self):
1808 service = self.session.nav.getCurrentService()
1809 subservices = service and service.subServices()
1810 self.bouquets = self.servicelist.getBouquetList()
1811 n = subservices and subservices.getNumberOfSubservices()
1814 ref = self.session.nav.getCurrentlyPlayingServiceReference()
1818 i = subservices.getSubservice(idx)
1819 if i.toString() == ref.toString():
1821 tlist.append((i.getName(), i))
1824 if self.bouquets and len(self.bouquets):
1825 keys = ["red", "blue", "", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1826 if config.usage.multibouquet.value:
1827 tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to bouquet"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1829 tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to favourites"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1832 tlist = [(_("Quickzap"), "quickzap", service.subServices()), ("--", "")] + tlist
1833 keys = ["red", "", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1836 self.session.openWithCallback(self.subserviceSelected, ChoiceBox, title=_("Please select a subservice..."), list = tlist, selection = selection, keys = keys, skin_name = "SubserviceSelection")
1838 def subserviceSelected(self, service):
1840 if not service is None:
1841 if isinstance(service[1], str):
1842 if service[1] == "quickzap":
1843 from Screens.SubservicesQuickzap import SubservicesQuickzap
1844 self.session.open(SubservicesQuickzap, service[2])
1846 self["SubserviceQuickzapAction"].setEnabled(True)
1847 self.session.nav.playService(service[1], False)
1849 def addSubserviceToBouquetCallback(self, service):
1850 if len(service) > 1 and isinstance(service[1], eServiceReference):
1851 self.selectedSubservice = service
1852 if self.bouquets is None:
1855 cnt = len(self.bouquets)
1856 if cnt > 1: # show bouquet list
1857 self.bsel = self.session.openWithCallback(self.bouquetSelClosed, BouquetSelector, self.bouquets, self.addSubserviceToBouquet)
1858 elif cnt == 1: # add to only one existing bouquet
1859 self.addSubserviceToBouquet(self.bouquets[0][1])
1860 self.session.open(MessageBox, _("Service has been added to the favourites."), MessageBox.TYPE_INFO)
1862 def bouquetSelClosed(self, confirmed):
1864 del self.selectedSubservice
1866 self.session.open(MessageBox, _("Service has been added to the selected bouquet."), MessageBox.TYPE_INFO)
1868 def addSubserviceToBouquet(self, dest):
1869 self.servicelist.addServiceToBouquet(dest, self.selectedSubservice[1])
1871 self.bsel.close(True)
1873 del self.selectedSubservice
1875 class InfoBarAdditionalInfo:
1878 self["RecordingPossible"] = Boolean(fixed=harddiskmanager.HDDCount() > 0 and config.misc.rcused.value == 1)
1879 self["TimeshiftPossible"] = self["RecordingPossible"]
1880 self["ShowTimeshiftOnYellow"] = Boolean(fixed=(not config.misc.rcused.value == 0))
1881 self["ShowAudioOnYellow"] = Boolean(fixed=config.misc.rcused.value == 0)
1882 self["ShowRecordOnRed"] = Boolean(fixed=config.misc.rcused.value == 1)
1883 self["ExtensionsAvailable"] = Boolean(fixed=1)
1885 class InfoBarNotifications:
1887 self.onExecBegin.append(self.checkNotifications)
1888 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1889 self.onClose.append(self.__removeNotification)
1891 def __removeNotification(self):
1892 Notifications.notificationAdded.remove(self.checkNotificationsIfExecing)
1894 def checkNotificationsIfExecing(self):
1896 self.checkNotifications()
1898 def checkNotifications(self):
1899 notifications = Notifications.notifications
1901 n = notifications[0]
1903 del notifications[0]
1906 if n[3].has_key("onSessionOpenCallback"):
1907 n[3]["onSessionOpenCallback"]()
1908 del n[3]["onSessionOpenCallback"]
1911 dlg = self.session.openWithCallback(cb, n[1], *n[2], **n[3])
1913 dlg = self.session.open(n[1], *n[2], **n[3])
1915 # remember that this notification is currently active
1917 Notifications.current_notifications.append(d)
1918 dlg.onClose.append(boundFunction(self.__notificationClosed, d))
1920 def __notificationClosed(self, d):
1921 Notifications.current_notifications.remove(d)
1923 class InfoBarServiceNotifications:
1925 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1927 iPlayableService.evEnd: self.serviceHasEnded
1930 def serviceHasEnded(self):
1931 print "service end!"
1934 self.setSeekState(self.SEEK_STATE_PLAY)
1938 class InfoBarCueSheetSupport:
1944 ENABLE_RESUME_SUPPORT = False
1946 def __init__(self, actionmap = "InfobarCueSheetActions"):
1947 self["CueSheetActions"] = HelpableActionMap(self, actionmap,
1949 "jumpPreviousMark": (self.jumpPreviousMark, _("jump to previous marked position")),
1950 "jumpNextMark": (self.jumpNextMark, _("jump to next marked position")),
1951 "toggleMark": (self.toggleMark, _("toggle a cut mark at the current position"))
1955 self.is_closing = False
1956 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1958 iPlayableService.evStart: self.__serviceStarted,
1961 def __serviceStarted(self):
1964 print "new service started! trying to download cuts!"
1965 self.downloadCuesheet()
1967 if self.ENABLE_RESUME_SUPPORT:
1970 for (pts, what) in self.cut_list:
1971 if what == self.CUT_TYPE_LAST:
1974 if last is not None:
1975 self.resume_point = last
1976 if config.usage.on_movie_start.value == "ask":
1977 Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Do you want to resume this playback?"), timeout=10)
1978 elif config.usage.on_movie_start.value == "resume":
1979 # TRANSLATORS: The string "Resuming playback" flashes for a moment
1980 # TRANSLATORS: at the start of a movie, when the user has selected
1981 # TRANSLATORS: "Resume from last position" as start behavior.
1982 # TRANSLATORS: The purpose is to notify the user that the movie starts
1983 # TRANSLATORS: in the middle somewhere and not from the beginning.
1984 # TRANSLATORS: (Some translators seem to have interpreted it as a
1985 # TRANSLATORS: question or a choice, but it is a statement.)
1986 Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Resuming playback"), timeout=2, type=MessageBox.TYPE_INFO)
1988 def playLastCB(self, answer):
1990 self.doSeek(self.resume_point)
1991 self.hideAfterResume()
1993 def hideAfterResume(self):
1994 if isinstance(self, InfoBarShowHide):
1997 def __getSeekable(self):
1998 service = self.session.nav.getCurrentService()
2001 return service.seek()
2003 def cueGetCurrentPosition(self):
2004 seek = self.__getSeekable()
2007 r = seek.getPlayPosition()
2012 def cueGetEndCutPosition(self):
2015 for cp in self.cut_list:
2016 if cp[1] == self.CUT_TYPE_OUT:
2020 elif cp[1] == self.CUT_TYPE_IN:
2024 def jumpPreviousNextMark(self, cmp, start=False):
2025 current_pos = self.cueGetCurrentPosition()
2026 if current_pos is None:
2028 mark = self.getNearestCutPoint(current_pos, cmp=cmp, start=start)
2029 if mark is not None:
2037 def jumpPreviousMark(self):
2038 # we add 2 seconds, so if the play position is <2s after
2039 # the mark, the mark before will be used
2040 self.jumpPreviousNextMark(lambda x: -x-5*90000, start=True)
2042 def jumpNextMark(self):
2043 if not self.jumpPreviousNextMark(lambda x: x):
2046 def getNearestCutPoint(self, pts, cmp=abs, start=False):
2052 bestdiff = cmp(0 - pts)
2054 nearest = [0, False]
2055 for cp in self.cut_list:
2056 if beforecut and cp[1] in (self.CUT_TYPE_IN, self.CUT_TYPE_OUT):
2058 if cp[1] == self.CUT_TYPE_IN: # Start is here, disregard previous marks
2059 diff = cmp(cp[0] - pts)
2065 if cp[1] in (self.CUT_TYPE_MARK, self.CUT_TYPE_LAST):
2066 diff = cmp(cp[0] - pts)
2067 if diff >= 0 and (nearest is None or bestdiff > diff):
2072 def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
2073 current_pos = self.cueGetCurrentPosition()
2074 if current_pos is None:
2075 print "not seekable"
2078 nearest_cutpoint = self.getNearestCutPoint(current_pos)
2080 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
2082 return nearest_cutpoint
2084 self.removeMark(nearest_cutpoint)
2085 elif not onlyremove and not onlyreturn:
2086 self.addMark((current_pos, self.CUT_TYPE_MARK))
2091 def addMark(self, point):
2092 insort(self.cut_list, point)
2093 self.uploadCuesheet()
2094 self.showAfterCuesheetOperation()
2096 def removeMark(self, point):
2097 self.cut_list.remove(point)
2098 self.uploadCuesheet()
2099 self.showAfterCuesheetOperation()
2101 def showAfterCuesheetOperation(self):
2102 if isinstance(self, InfoBarShowHide):
2105 def __getCuesheet(self):
2106 service = self.session.nav.getCurrentService()
2109 return service.cueSheet()
2111 def uploadCuesheet(self):
2112 cue = self.__getCuesheet()
2115 print "upload failed, no cuesheet interface"
2117 cue.setCutList(self.cut_list)
2119 def downloadCuesheet(self):
2120 cue = self.__getCuesheet()
2123 print "download failed, no cuesheet interface"
2126 self.cut_list = cue.getCutList()
2128 class InfoBarSummary(Screen):
2130 <screen position="0,0" size="132,64">
2131 <widget source="global.CurrentTime" render="Label" position="62,46" size="82,18" font="Regular;16" >
2132 <convert type="ClockToText">WithSeconds</convert>
2134 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="82,18" zPosition="1" >
2135 <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2136 <convert type="ConditionalShowHide">Blink</convert>
2138 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2139 <convert type="ServiceName">Name</convert>
2141 <widget source="session.Event_Now" render="Progress" position="6,46" size="46,18" borderWidth="1" >
2142 <convert type="EventTime">Progress</convert>
2146 # for picon: (path="piconlcd" will use LCD picons)
2147 # <widget source="session.CurrentService" render="Picon" position="6,0" size="120,64" path="piconlcd" >
2148 # <convert type="ServiceName">Reference</convert>
2151 class InfoBarSummarySupport:
2155 def createSummary(self):
2156 return InfoBarSummary
2158 class InfoBarMoviePlayerSummary(Screen):
2160 <screen position="0,0" size="132,64">
2161 <widget source="global.CurrentTime" render="Label" position="62,46" size="64,18" font="Regular;16" halign="right" >
2162 <convert type="ClockToText">WithSeconds</convert>
2164 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="64,18" zPosition="1" >
2165 <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2166 <convert type="ConditionalShowHide">Blink</convert>
2168 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2169 <convert type="ServiceName">Name</convert>
2171 <widget source="session.CurrentService" render="Progress" position="6,46" size="56,18" borderWidth="1" >
2172 <convert type="ServicePosition">Position</convert>
2176 class InfoBarMoviePlayerSummarySupport:
2180 def createSummary(self):
2181 return InfoBarMoviePlayerSummary
2183 class InfoBarTeletextPlugin:
2185 self.teletext_plugin = None
2187 for p in plugins.getPlugins(PluginDescriptor.WHERE_TELETEXT):
2188 self.teletext_plugin = p
2190 if self.teletext_plugin is not None:
2191 self["TeletextActions"] = HelpableActionMap(self, "InfobarTeletextActions",
2193 "startTeletext": (self.startTeletext, _("View teletext..."))
2196 print "no teletext plugin found!"
2198 def startTeletext(self):
2199 self.teletext_plugin(session=self.session, service=self.session.nav.getCurrentService())
2201 class InfoBarSubtitleSupport(object):
2203 object.__init__(self)
2204 self.subtitle_window = self.session.instantiateDialog(SubtitleDisplay)
2205 self.__subtitles_enabled = False
2207 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2209 iPlayableService.evEnd: self.__serviceStopped,
2210 iPlayableService.evUpdatedInfo: self.__updatedInfo
2212 self.cached_subtitle_checked = False
2213 self.__selected_subtitle = None
2215 def __serviceStopped(self):
2216 self.cached_subtitle_checked = False
2217 if self.__subtitles_enabled:
2218 self.subtitle_window.hide()
2219 self.__subtitles_enabled = False
2220 self.__selected_subtitle = None
2222 def __updatedInfo(self):
2223 if not self.cached_subtitle_checked:
2224 self.cached_subtitle_checked = True
2225 subtitle = self.getCurrentServiceSubtitle()
2226 self.setSelectedSubtitle(subtitle and subtitle.getCachedSubtitle())
2227 if self.__selected_subtitle:
2228 self.setSubtitlesEnable(True)
2230 def getCurrentServiceSubtitle(self):
2231 service = self.session.nav.getCurrentService()
2232 return service and service.subtitle()
2234 def setSubtitlesEnable(self, enable=True):
2235 subtitle = self.getCurrentServiceSubtitle()
2237 if self.__selected_subtitle:
2238 if subtitle and not self.__subtitles_enabled:
2239 subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
2240 self.subtitle_window.show()
2241 self.__subtitles_enabled = True
2244 subtitle.disableSubtitles(self.subtitle_window.instance)
2245 self.__selected_subtitle = False
2246 self.__subtitles_enabled = False
2247 self.subtitle_window.hide()
2249 def setSelectedSubtitle(self, subtitle):
2250 self.__selected_subtitle = subtitle
2252 subtitles_enabled = property(lambda self: self.__subtitles_enabled, setSubtitlesEnable)
2253 selected_subtitle = property(lambda self: self.__selected_subtitle, setSelectedSubtitle)
2255 class InfoBarServiceErrorPopupSupport:
2257 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2259 iPlayableService.evTuneFailed: self.__tuneFailed,
2260 iPlayableService.evStart: self.__serviceStarted
2262 self.__serviceStarted()
2264 def __serviceStarted(self):
2265 self.last_error = None
2266 Notifications.RemovePopup(id = "ZapError")
2268 def __tuneFailed(self):
2269 service = self.session.nav.getCurrentService()
2270 info = service and service.info()
2271 error = info and info.getInfo(iServiceInformation.sDVBState)
2273 if error == self.last_error:
2276 self.last_error = error
2279 eDVBServicePMTHandler.eventNoResources: _("No free tuner!"),
2280 eDVBServicePMTHandler.eventTuneFailed: _("Tune failed!"),
2281 eDVBServicePMTHandler.eventNoPAT: _("No data on transponder!\n(Timeout reading PAT)"),
2282 eDVBServicePMTHandler.eventNoPATEntry: _("Service not found!\n(SID not found in PAT)"),
2283 eDVBServicePMTHandler.eventNoPMT: _("Service invalid!\n(Timeout reading PMT)"),
2284 eDVBServicePMTHandler.eventNewProgramInfo: None,
2285 eDVBServicePMTHandler.eventTuned: None,
2286 eDVBServicePMTHandler.eventSOF: None,
2287 eDVBServicePMTHandler.eventEOF: None,
2288 eDVBServicePMTHandler.eventMisconfiguration: _("Service unavailable!\nCheck tuner configuration!"),
2289 }.get(error) #this returns None when the key not exist in the dict
2291 if error is not None:
2292 Notifications.AddPopup(text = error, type = MessageBox.TYPE_ERROR, timeout = 5, id = "ZapError")
2294 Notifications.RemovePopup(id = "ZapError")