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 Components.UsageConfig import preferredInstantRecordPath, defaultMoviePath
14 from EpgSelection import EPGSelection
15 from Plugins.Plugin import PluginDescriptor
17 from Screen import Screen
18 from Screens.ChoiceBox import ChoiceBox
19 from Screens.Dish import Dish
20 from Screens.EventView import EventViewEPGSelect, EventViewSimple
21 from Screens.InputBox import InputBox
22 from Screens.MessageBox import MessageBox
23 from Screens.MinuteInput import MinuteInput
24 from Screens.TimerSelection import TimerSelection
25 from Screens.PictureInPicture import PictureInPicture
26 from Screens.SubtitleDisplay import SubtitleDisplay
27 from Screens.RdsDisplay import RdsInfoDisplay, RassInteractive
28 from Screens.SleepTimerEdit import SleepTimerEdit
29 from Screens.TimeDateInput import TimeDateInput
30 from ServiceReference import ServiceReference
32 from Tools import Notifications
33 from Tools.Directories import fileExists
35 from enigma import eTimer, eServiceCenter, eDVBServicePMTHandler, iServiceInformation, \
36 iPlayableService, eServiceReference, eEPGCache
38 from time import time, localtime, strftime
39 from os import stat as os_stat
40 from bisect import insort
42 from RecordTimer import RecordTimerEntry, RecordTimer
45 from Menu import MainMenu, mdom
49 self.dishDialog = self.session.instantiateDialog(Dish)
51 class InfoBarShowHide:
52 """ InfoBar show/hide control, accepts toggleShow and hide actions, might start
60 self["ShowHideActions"] = ActionMap( ["InfobarShowHideActions"] ,
62 "toggleShow": self.toggleShow,
64 }, 1) # lower prio to make it possible to override ok and cancel..
66 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
68 iPlayableService.evStart: self.serviceStarted,
71 self.__state = self.STATE_SHOWN
74 self.hideTimer = eTimer()
75 self.hideTimer.callback.append(self.doTimerHide)
76 self.hideTimer.start(5000, True)
78 self.onShow.append(self.__onShow)
79 self.onHide.append(self.__onHide)
81 def serviceStarted(self):
83 if config.usage.show_infobar_on_zap.value:
87 self.__state = self.STATE_SHOWN
90 def startHideTimer(self):
91 if self.__state == self.STATE_SHOWN and not self.__locked:
92 idx = config.usage.infobar_timeout.index
94 self.hideTimer.start(idx*1000, True)
97 self.__state = self.STATE_HIDDEN
101 self.startHideTimer()
103 def doTimerHide(self):
104 self.hideTimer.stop()
105 if self.__state == self.STATE_SHOWN:
108 def toggleShow(self):
109 if self.__state == self.STATE_SHOWN:
111 self.hideTimer.stop()
112 elif self.__state == self.STATE_HIDDEN:
116 self.__locked = self.__locked + 1
119 self.hideTimer.stop()
121 def unlockShow(self):
122 self.__locked = self.__locked - 1
124 self.startHideTimer()
126 # def startShow(self):
127 # self.instance.m_animation.startMoveAnimation(ePoint(0, 600), ePoint(0, 380), 100)
128 # self.__state = self.STATE_SHOWN
130 # def startHide(self):
131 # self.instance.m_animation.startMoveAnimation(ePoint(0, 380), ePoint(0, 600), 100)
132 # self.__state = self.STATE_HIDDEN
134 class NumberZap(Screen):
141 self.close(int(self["number"].getText()))
143 def keyNumberGlobal(self, number):
144 self.Timer.start(3000, True) #reset timer
145 self.field = self.field + str(number)
146 self["number"].setText(self.field)
147 if len(self.field) >= 4:
150 def __init__(self, session, number):
151 Screen.__init__(self, session)
152 self.field = str(number)
154 self["channel"] = Label(_("Channel:"))
156 self["number"] = Label(self.field)
158 self["actions"] = NumberActionMap( [ "SetupActions" ],
162 "1": self.keyNumberGlobal,
163 "2": self.keyNumberGlobal,
164 "3": self.keyNumberGlobal,
165 "4": self.keyNumberGlobal,
166 "5": self.keyNumberGlobal,
167 "6": self.keyNumberGlobal,
168 "7": self.keyNumberGlobal,
169 "8": self.keyNumberGlobal,
170 "9": self.keyNumberGlobal,
171 "0": self.keyNumberGlobal
174 self.Timer = eTimer()
175 self.Timer.callback.append(self.keyOK)
176 self.Timer.start(3000, True)
178 class InfoBarNumberZap:
179 """ Handles an initial number for NumberZapping """
181 self["NumberActions"] = NumberActionMap( [ "NumberActions"],
183 "1": self.keyNumberGlobal,
184 "2": self.keyNumberGlobal,
185 "3": self.keyNumberGlobal,
186 "4": self.keyNumberGlobal,
187 "5": self.keyNumberGlobal,
188 "6": self.keyNumberGlobal,
189 "7": self.keyNumberGlobal,
190 "8": self.keyNumberGlobal,
191 "9": self.keyNumberGlobal,
192 "0": self.keyNumberGlobal,
195 def keyNumberGlobal(self, number):
196 # print "You pressed number " + str(number)
198 if isinstance(self, InfoBarPiP) and self.pipHandles0Action():
199 self.pipDoHandle0Action()
201 self.servicelist.recallPrevService()
203 if self.has_key("TimeshiftActions") and not self.timeshift_enabled:
204 self.session.openWithCallback(self.numberEntered, NumberZap, number)
206 def numberEntered(self, retval):
207 # print self.servicelist
209 self.zapToNumber(retval)
211 def searchNumberHelper(self, serviceHandler, num, bouquet):
212 servicelist = serviceHandler.list(bouquet)
213 if not servicelist is None:
215 serviceIterator = servicelist.getNext()
216 if not serviceIterator.valid(): #check end of list
218 playable = not (serviceIterator.flags & (eServiceReference.isMarker|eServiceReference.isDirectory))
221 if not num: #found service with searched number ?
222 return serviceIterator, 0
225 def zapToNumber(self, number):
226 bouquet = self.servicelist.bouquet_root
228 serviceHandler = eServiceCenter.getInstance()
229 if not config.usage.multibouquet.value:
230 service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
232 bouquetlist = serviceHandler.list(bouquet)
233 if not bouquetlist is None:
235 bouquet = bouquetlist.getNext()
236 if not bouquet.valid(): #check end of list
238 if bouquet.flags & eServiceReference.isDirectory:
239 service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
240 if not service is None:
241 if self.servicelist.getRoot() != bouquet: #already in correct bouquet?
242 self.servicelist.clearPath()
243 if self.servicelist.bouquet_root != bouquet:
244 self.servicelist.enterPath(self.servicelist.bouquet_root)
245 self.servicelist.enterPath(bouquet)
246 self.servicelist.setCurrentSelection(service) #select the service in servicelist
247 self.servicelist.zap()
249 config.misc.initialchannelselection = ConfigBoolean(default = True)
251 class InfoBarChannelSelection:
252 """ ChannelSelection - handles the channelSelection dialog and the initial
253 channelChange actions which open the channelSelection dialog """
256 self.servicelist = self.session.instantiateDialog(ChannelSelection)
258 if config.misc.initialchannelselection.value:
259 self.onShown.append(self.firstRun)
261 self["ChannelSelectActions"] = HelpableActionMap(self, "InfobarChannelSelection",
263 "switchChannelUp": (self.switchChannelUp, _("open servicelist(up)")),
264 "switchChannelDown": (self.switchChannelDown, _("open servicelist(down)")),
265 "zapUp": (self.zapUp, _("previous channel")),
266 "zapDown": (self.zapDown, _("next channel")),
267 "historyBack": (self.historyBack, _("previous channel in history")),
268 "historyNext": (self.historyNext, _("next channel in history")),
269 "openServiceList": (self.openServiceList, _("open servicelist")),
272 def showTvChannelList(self, zap=False):
273 self.servicelist.setModeTv()
275 self.servicelist.zap()
276 self.session.execDialog(self.servicelist)
278 def showRadioChannelList(self, zap=False):
279 self.servicelist.setModeRadio()
281 self.servicelist.zap()
282 self.session.execDialog(self.servicelist)
285 self.onShown.remove(self.firstRun)
286 config.misc.initialchannelselection.value = False
287 config.misc.initialchannelselection.save()
288 self.switchChannelDown()
290 def historyBack(self):
291 self.servicelist.historyBack()
293 def historyNext(self):
294 self.servicelist.historyNext()
296 def switchChannelUp(self):
297 self.servicelist.moveUp()
298 self.session.execDialog(self.servicelist)
300 def switchChannelDown(self):
301 self.servicelist.moveDown()
302 self.session.execDialog(self.servicelist)
304 def openServiceList(self):
305 self.session.execDialog(self.servicelist)
308 if self.servicelist.inBouquet():
309 prev = self.servicelist.getCurrentSelection()
311 prev = prev.toString()
313 if config.usage.quickzap_bouquet_change.value:
314 if self.servicelist.atBegin():
315 self.servicelist.prevBouquet()
316 self.servicelist.moveUp()
317 cur = self.servicelist.getCurrentSelection()
318 if not cur or (not (cur.flags & 64)) or cur.toString() == prev:
321 self.servicelist.moveUp()
322 self.servicelist.zap()
325 if self.servicelist.inBouquet():
326 prev = self.servicelist.getCurrentSelection()
328 prev = prev.toString()
330 if config.usage.quickzap_bouquet_change.value and self.servicelist.atEnd():
331 self.servicelist.nextBouquet()
333 self.servicelist.moveDown()
334 cur = self.servicelist.getCurrentSelection()
335 if not cur or (not (cur.flags & 64)) or cur.toString() == prev:
338 self.servicelist.moveDown()
339 self.servicelist.zap()
342 """ Handles a menu action, to open the (main) menu """
344 self["MenuActions"] = HelpableActionMap(self, "InfobarMenuActions",
346 "mainMenu": (self.mainMenu, _("Enter main menu...")),
348 self.session.infobar = None
351 print "loading mainmenu XML..."
352 menu = mdom.getroot()
353 assert menu.tag == "menu", "root element in menu must be 'menu'!"
355 self.session.infobar = self
356 # so we can access the currently active infobar from screens opened from within the mainmenu
357 # at the moment used from the SubserviceSelection
359 self.session.openWithCallback(self.mainMenuClosed, MainMenu, menu)
361 def mainMenuClosed(self, *val):
362 self.session.infobar = None
364 class InfoBarSimpleEventView:
365 """ Opens the Eventview for now/next """
367 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
369 "showEventInfo": (self.openEventView, _("show event details")),
370 "showInfobarOrEpgWhenInfobarAlreadyVisible": self.showEventInfoWhenNotVisible,
373 def showEventInfoWhenNotVisible(self):
380 def openEventView(self):
382 self.epglist = epglist
383 service = self.session.nav.getCurrentService()
384 ref = self.session.nav.getCurrentlyPlayingServiceReference()
385 info = service.info()
393 self.session.open(EventViewSimple, epglist[0], ServiceReference(ref), self.eventViewCallback)
395 def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
396 epglist = self.epglist
399 epglist[0] = epglist[1]
403 class SimpleServicelist:
404 def __init__(self, services):
405 self.services = services
406 self.length = len(services)
409 def selectService(self, service):
415 while self.services[self.current].ref != service:
417 if self.current >= self.length:
421 def nextService(self):
424 if self.current+1 < self.length:
429 def prevService(self):
432 if self.current-1 > -1:
435 self.current = self.length - 1
437 def currentService(self):
438 if not self.length or self.current >= self.length:
440 return self.services[self.current]
443 """ EPG - Opens an EPG list when the showEPGList action fires """
445 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
447 iPlayableService.evUpdatedEventInfo: self.__evEventInfoChanged,
450 self.is_now_next = False
452 self.bouquetSel = None
453 self.eventView = None
454 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
456 "showEventInfo": (self.openEventView, _("show EPG...")),
457 "showEventInfoPlugin": (self.showEventInfoPlugins, _("show single service EPG...")),
458 "showInfobarOrEpgWhenInfobarAlreadyVisible": self.showEventInfoWhenNotVisible,
461 def showEventInfoWhenNotVisible(self):
468 def zapToService(self, service):
469 if not service is None:
470 if self.servicelist.getRoot() != self.epg_bouquet: #already in correct bouquet?
471 self.servicelist.clearPath()
472 if self.servicelist.bouquet_root != self.epg_bouquet:
473 self.servicelist.enterPath(self.servicelist.bouquet_root)
474 self.servicelist.enterPath(self.epg_bouquet)
475 self.servicelist.setCurrentSelection(service) #select the service in servicelist
476 self.servicelist.zap()
478 def getBouquetServices(self, bouquet):
480 servicelist = eServiceCenter.getInstance().list(bouquet)
481 if not servicelist is None:
483 service = servicelist.getNext()
484 if not service.valid(): #check if end of list
486 if service.flags & (eServiceReference.isDirectory | eServiceReference.isMarker): #ignore non playable services
488 services.append(ServiceReference(service))
491 def openBouquetEPG(self, bouquet, withCallback=True):
492 services = self.getBouquetServices(bouquet)
494 self.epg_bouquet = bouquet
496 self.dlg_stack.append(self.session.openWithCallback(self.closed, EPGSelection, services, self.zapToService, None, self.changeBouquetCB))
498 self.session.open(EPGSelection, services, self.zapToService, None, self.changeBouquetCB)
500 def changeBouquetCB(self, direction, epg):
503 self.bouquetSel.down()
506 bouquet = self.bouquetSel.getCurrent()
507 services = self.getBouquetServices(bouquet)
509 self.epg_bouquet = bouquet
510 epg.setServices(services)
512 def closed(self, ret=False):
513 closedScreen = self.dlg_stack.pop()
514 if self.bouquetSel and closedScreen == self.bouquetSel:
515 self.bouquetSel = None
516 elif self.eventView and closedScreen == self.eventView:
517 self.eventView = None
519 dlgs=len(self.dlg_stack)
521 self.dlg_stack[dlgs-1].close(dlgs > 1)
523 def openMultiServiceEPG(self, withCallback=True):
524 bouquets = self.servicelist.getBouquetList()
529 if cnt > 1: # show bouquet list
531 self.bouquetSel = self.session.openWithCallback(self.closed, BouquetSelector, bouquets, self.openBouquetEPG, enableWrapAround=True)
532 self.dlg_stack.append(self.bouquetSel)
534 self.bouquetSel = self.session.open(BouquetSelector, bouquets, self.openBouquetEPG, enableWrapAround=True)
536 self.openBouquetEPG(bouquets[0][1], withCallback)
538 def changeServiceCB(self, direction, epg):
541 self.serviceSel.nextService()
543 self.serviceSel.prevService()
544 epg.setService(self.serviceSel.currentService())
546 def SingleServiceEPGClosed(self, ret=False):
547 self.serviceSel = None
549 def openSingleServiceEPG(self):
550 ref=self.session.nav.getCurrentlyPlayingServiceReference()
552 if self.servicelist.getMutableList() is not None: # bouquet in channellist
553 current_path = self.servicelist.getRoot()
554 services = self.getBouquetServices(current_path)
555 self.serviceSel = SimpleServicelist(services)
556 if self.serviceSel.selectService(ref):
557 self.session.openWithCallback(self.SingleServiceEPGClosed, EPGSelection, ref, serviceChangeCB = self.changeServiceCB)
559 self.session.openWithCallback(self.SingleServiceEPGClosed, EPGSelection, ref)
561 self.session.open(EPGSelection, ref)
563 def showEventInfoPlugins(self):
564 list = [(p.name, boundFunction(self.runPlugin, p)) for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EVENTINFO)]
567 list.append((_("show single service EPG..."), self.openSingleServiceEPG))
568 self.session.openWithCallback(self.EventInfoPluginChosen, ChoiceBox, title=_("Please choose an extension..."), list = list, skin_name = "EPGExtensionsList")
570 self.openSingleServiceEPG()
572 def runPlugin(self, plugin):
573 plugin(session = self.session, servicelist = self.servicelist)
575 def EventInfoPluginChosen(self, answer):
576 if answer is not None:
579 def openSimilarList(self, eventid, refstr):
580 self.session.open(EPGSelection, refstr, None, eventid)
582 def getNowNext(self):
584 service = self.session.nav.getCurrentService()
585 info = service and service.info()
586 ptr = info and info.getEvent(0)
589 ptr = info and info.getEvent(1)
592 self.epglist = epglist
594 def __evEventInfoChanged(self):
595 if self.is_now_next and len(self.dlg_stack) == 1:
597 assert self.eventView
599 self.eventView.setEvent(self.epglist[0])
601 def openEventView(self):
602 ref = self.session.nav.getCurrentlyPlayingServiceReference()
604 epglist = self.epglist
606 self.is_now_next = False
607 epg = eEPGCache.getInstance()
608 ptr = ref and ref.valid() and epg.lookupEventTime(ref, -1)
611 ptr = epg.lookupEventTime(ref, ptr.getBeginTime(), +1)
615 self.is_now_next = True
617 self.eventView = self.session.openWithCallback(self.closed, EventViewEPGSelect, self.epglist[0], ServiceReference(ref), self.eventViewCallback, self.openSingleServiceEPG, self.openMultiServiceEPG, self.openSimilarList)
618 self.dlg_stack.append(self.eventView)
620 print "no epg for the service avail.. so we show multiepg instead of eventinfo"
621 self.openMultiServiceEPG(False)
623 def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
624 epglist = self.epglist
627 epglist[0]=epglist[1]
631 class InfoBarRdsDecoder:
632 """provides RDS and Rass support/display"""
634 self.rds_display = self.session.instantiateDialog(RdsInfoDisplay)
635 self.rass_interactive = None
637 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
639 iPlayableService.evEnd: self.__serviceStopped,
640 iPlayableService.evUpdatedRassSlidePic: self.RassSlidePicChanged
643 self["RdsActions"] = ActionMap(["InfobarRdsActions"],
645 "startRassInteractive": self.startRassInteractive
648 self["RdsActions"].setEnabled(False)
650 self.onLayoutFinish.append(self.rds_display.show)
651 self.rds_display.onRassInteractivePossibilityChanged.append(self.RassInteractivePossibilityChanged)
653 def RassInteractivePossibilityChanged(self, state):
654 self["RdsActions"].setEnabled(state)
656 def RassSlidePicChanged(self):
657 if not self.rass_interactive:
658 service = self.session.nav.getCurrentService()
659 decoder = service and service.rdsDecoder()
661 decoder.showRassSlidePicture()
663 def __serviceStopped(self):
664 if self.rass_interactive is not None:
665 rass_interactive = self.rass_interactive
666 self.rass_interactive = None
667 rass_interactive.close()
669 def startRassInteractive(self):
670 self.rds_display.hide()
671 self.rass_interactive = self.session.openWithCallback(self.RassInteractiveClosed, RassInteractive)
673 def RassInteractiveClosed(self, *val):
674 if self.rass_interactive is not None:
675 self.rass_interactive = None
676 self.RassSlidePicChanged()
677 self.rds_display.show()
680 """handles actions like seeking, pause"""
682 SEEK_STATE_PLAY = (0, 0, 0, ">")
683 SEEK_STATE_PAUSE = (1, 0, 0, "||")
684 SEEK_STATE_EOF = (1, 0, 0, "END")
686 def __init__(self, actionmap = "InfobarSeekActions", useSeekBackHack=True):
687 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
689 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
690 iPlayableService.evStart: self.__serviceStarted,
692 iPlayableService.evEOF: self.__evEOF,
693 iPlayableService.evSOF: self.__evSOF,
696 self.minSpeedBackward = useSeekBackHack and 16 or 0
698 class InfoBarSeekActionMap(HelpableActionMap):
699 def __init__(self, screen, *args, **kwargs):
700 HelpableActionMap.__init__(self, screen, *args, **kwargs)
703 def action(self, contexts, action):
704 print "action:", action
705 if action[:5] == "seek:":
706 time = int(action[5:])
707 self.screen.doSeekRelative(time * 90000)
709 elif action[:8] == "seekdef:":
710 key = int(action[8:])
711 time = (-config.seek.selfdefined_13.value, False, config.seek.selfdefined_13.value,
712 -config.seek.selfdefined_46.value, False, config.seek.selfdefined_46.value,
713 -config.seek.selfdefined_79.value, False, config.seek.selfdefined_79.value)[key-1]
714 self.screen.doSeekRelative(time * 90000)
717 return HelpableActionMap.action(self, contexts, action)
719 self["SeekActions"] = InfoBarSeekActionMap(self, actionmap,
721 "playpauseService": self.playpauseService,
722 "pauseService": (self.pauseService, _("pause")),
723 "unPauseService": (self.unPauseService, _("continue")),
725 "seekFwd": (self.seekFwd, _("skip forward")),
726 "seekFwdManual": (self.seekFwdManual, _("skip forward (enter time)")),
727 "seekBack": (self.seekBack, _("skip backward")),
728 "seekBackManual": (self.seekBackManual, _("skip backward (enter time)"))
730 # give them a little more priority to win over color buttons
732 self["SeekActions"].setEnabled(False)
734 self.seekstate = self.SEEK_STATE_PLAY
735 self.lastseekstate = self.SEEK_STATE_PLAY
737 self.onPlayStateChanged = [ ]
739 self.lockedBecauseOfSkipping = False
741 self.__seekableStatusChanged()
743 def makeStateForward(self, n):
744 minspeed = config.seek.stepwise_minspeed.value
745 repeat = int(config.seek.stepwise_repeat.value)
746 if minspeed != "Never" and n >= int(minspeed) and repeat > 1:
747 return (0, n * repeat, repeat, ">> %dx" % n)
749 return (0, n, 0, ">> %dx" % n)
751 def makeStateBackward(self, n):
752 minspeed = config.seek.stepwise_minspeed.value
753 repeat = int(config.seek.stepwise_repeat.value)
754 if self.minSpeedBackward and n < self.minSpeedBackward:
755 r = (self.minSpeedBackward - 1)/ n + 1
756 if minspeed != "Never" and n >= int(minspeed) and repeat > 1:
758 return (0, -n * r, r, "<< %dx" % n)
759 elif minspeed != "Never" and n >= int(minspeed) and repeat > 1:
760 return (0, -n * repeat, repeat, "<< %dx" % n)
762 return (0, -n, 0, "<< %dx" % n)
764 def makeStateSlowMotion(self, n):
765 return (0, 0, n, "/%d" % n)
767 def isStateForward(self, state):
770 def isStateBackward(self, state):
773 def isStateSlowMotion(self, state):
774 return state[1] == 0 and state[2] > 1
776 def getHigher(self, n, lst):
782 def getLower(self, n, lst):
790 def showAfterSeek(self):
791 if isinstance(self, InfoBarShowHide):
801 service = self.session.nav.getCurrentService()
805 seek = service.seek()
807 if seek is None or not seek.isCurrentlySeekable():
812 def isSeekable(self):
813 if self.getSeek() is None:
817 def __seekableStatusChanged(self):
818 # print "seekable status changed!"
819 if not self.isSeekable():
820 self["SeekActions"].setEnabled(False)
821 # print "not seekable, return to play"
822 self.setSeekState(self.SEEK_STATE_PLAY)
824 self["SeekActions"].setEnabled(True)
827 def __serviceStarted(self):
828 self.seekstate = self.SEEK_STATE_PLAY
829 self.__seekableStatusChanged()
831 def setSeekState(self, state):
832 service = self.session.nav.getCurrentService()
837 if not self.isSeekable():
838 if state not in (self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE):
839 state = self.SEEK_STATE_PLAY
841 pauseable = service.pause()
843 if pauseable is None:
844 print "not pauseable."
845 state = self.SEEK_STATE_PLAY
847 self.seekstate = state
849 if pauseable is not None:
850 if self.seekstate[0]:
851 print "resolved to PAUSE"
853 elif self.seekstate[1]:
854 print "resolved to FAST FORWARD"
855 pauseable.setFastForward(self.seekstate[1])
856 elif self.seekstate[2]:
857 print "resolved to SLOW MOTION"
858 pauseable.setSlowMotion(self.seekstate[2])
860 print "resolved to PLAY"
863 for c in self.onPlayStateChanged:
866 self.checkSkipShowHideLock()
870 def playpauseService(self):
871 if self.seekstate != self.SEEK_STATE_PLAY:
872 self.unPauseService()
876 def pauseService(self):
877 if self.seekstate == self.SEEK_STATE_PAUSE:
878 if config.seek.on_pause.value == "play":
879 self.unPauseService()
880 elif config.seek.on_pause.value == "step":
881 self.doSeekRelative(0)
882 elif config.seek.on_pause.value == "last":
883 self.setSeekState(self.lastseekstate)
884 self.lastseekstate = self.SEEK_STATE_PLAY
886 if self.seekstate != self.SEEK_STATE_EOF:
887 self.lastseekstate = self.seekstate
888 self.setSeekState(self.SEEK_STATE_PAUSE);
890 def unPauseService(self):
892 if self.seekstate == self.SEEK_STATE_PLAY:
894 self.setSeekState(self.SEEK_STATE_PLAY)
896 def doSeek(self, pts):
897 seekable = self.getSeek()
902 def doSeekRelative(self, pts):
903 seekable = self.getSeek()
906 prevstate = self.seekstate
908 if self.seekstate == self.SEEK_STATE_EOF:
909 if prevstate == self.SEEK_STATE_PAUSE:
910 self.setSeekState(self.SEEK_STATE_PAUSE)
912 self.setSeekState(self.SEEK_STATE_PLAY)
913 seekable.seekRelative(pts<0 and -1 or 1, abs(pts))
914 if abs(pts) > 100 and config.usage.show_infobar_on_skip.value:
918 if self.seekstate == self.SEEK_STATE_PLAY:
919 self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
920 elif self.seekstate == self.SEEK_STATE_PAUSE:
921 if len(config.seek.speeds_slowmotion.value):
922 self.setSeekState(self.makeStateSlowMotion(config.seek.speeds_slowmotion.value[-1]))
924 self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
925 elif self.seekstate == self.SEEK_STATE_EOF:
927 elif self.isStateForward(self.seekstate):
928 speed = self.seekstate[1]
929 if self.seekstate[2]:
930 speed /= self.seekstate[2]
931 speed = self.getHigher(speed, config.seek.speeds_forward.value) or config.seek.speeds_forward.value[-1]
932 self.setSeekState(self.makeStateForward(speed))
933 elif self.isStateBackward(self.seekstate):
934 speed = -self.seekstate[1]
935 if self.seekstate[2]:
936 speed /= self.seekstate[2]
937 speed = self.getLower(speed, config.seek.speeds_backward.value)
939 self.setSeekState(self.makeStateBackward(speed))
941 self.setSeekState(self.SEEK_STATE_PLAY)
942 elif self.isStateSlowMotion(self.seekstate):
943 speed = self.getLower(self.seekstate[2], config.seek.speeds_slowmotion.value) or config.seek.speeds_slowmotion.value[0]
944 self.setSeekState(self.makeStateSlowMotion(speed))
947 seekstate = self.seekstate
948 if seekstate == self.SEEK_STATE_PLAY:
949 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
950 elif seekstate == self.SEEK_STATE_EOF:
951 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
952 self.doSeekRelative(-6)
953 elif seekstate == self.SEEK_STATE_PAUSE:
954 self.doSeekRelative(-3)
955 elif self.isStateForward(seekstate):
958 speed /= seekstate[2]
959 speed = self.getLower(speed, config.seek.speeds_forward.value)
961 self.setSeekState(self.makeStateForward(speed))
963 self.setSeekState(self.SEEK_STATE_PLAY)
964 elif self.isStateBackward(seekstate):
965 speed = -seekstate[1]
967 speed /= seekstate[2]
968 speed = self.getHigher(speed, config.seek.speeds_backward.value) or config.seek.speeds_backward.value[-1]
969 self.setSeekState(self.makeStateBackward(speed))
970 elif self.isStateSlowMotion(seekstate):
971 speed = self.getHigher(seekstate[2], config.seek.speeds_slowmotion.value)
973 self.setSeekState(self.makeStateSlowMotion(speed))
975 self.setSeekState(self.SEEK_STATE_PAUSE)
977 def seekFwdManual(self):
978 self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
980 def fwdSeekTo(self, minutes):
981 print "Seek", minutes, "minutes forward"
982 self.doSeekRelative(minutes * 60 * 90000)
984 def seekBackManual(self):
985 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
987 def rwdSeekTo(self, minutes):
989 self.doSeekRelative(-minutes * 60 * 90000)
991 def checkSkipShowHideLock(self):
992 wantlock = self.seekstate != self.SEEK_STATE_PLAY
994 if config.usage.show_infobar_on_skip.value:
995 if self.lockedBecauseOfSkipping and not wantlock:
997 self.lockedBecauseOfSkipping = False
999 if wantlock and not self.lockedBecauseOfSkipping:
1001 self.lockedBecauseOfSkipping = True
1003 def calcRemainingTime(self):
1004 seekable = self.getSeek()
1005 if seekable is not None:
1006 len = seekable.getLength()
1008 tmp = self.cueGetEndCutPosition()
1013 pos = seekable.getPlayPosition()
1014 speednom = self.seekstate[1] or 1
1015 speedden = self.seekstate[2] or 1
1016 if not len[0] and not pos[0]:
1017 if len[1] <= pos[1]:
1019 time = (len[1] - pos[1])*speedden/(90*speednom)
1024 if self.seekstate == self.SEEK_STATE_EOF:
1027 # if we are seeking forward, we try to end up ~1s before the end, and pause there.
1028 seekstate = self.seekstate
1029 if self.seekstate != self.SEEK_STATE_PAUSE:
1030 self.setSeekState(self.SEEK_STATE_EOF)
1032 if seekstate not in (self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE): # if we are seeking
1033 seekable = self.getSeek()
1034 if seekable is not None:
1036 if seekstate == self.SEEK_STATE_PLAY: # regular EOF
1037 self.doEofInternal(True)
1039 self.doEofInternal(False)
1041 def doEofInternal(self, playing):
1042 pass # Defined in subclasses
1045 self.setSeekState(self.SEEK_STATE_PLAY)
1048 from Screens.PVRState import PVRState, TimeshiftState
1050 class InfoBarPVRState:
1051 def __init__(self, screen=PVRState, force_show = False):
1052 self.onPlayStateChanged.append(self.__playStateChanged)
1053 self.pvrStateDialog = self.session.instantiateDialog(screen)
1054 self.onShow.append(self._mayShow)
1055 self.onHide.append(self.pvrStateDialog.hide)
1056 self.force_show = force_show
1059 if self.execing and self.seekstate != self.SEEK_STATE_PLAY:
1060 self.pvrStateDialog.show()
1062 def __playStateChanged(self, state):
1063 playstateString = state[3]
1064 self.pvrStateDialog["state"].setText(playstateString)
1066 # if we return into "PLAY" state, ensure that the dialog gets hidden if there will be no infobar displayed
1067 if not config.usage.show_infobar_on_skip.value and self.seekstate == self.SEEK_STATE_PLAY and not self.force_show:
1068 self.pvrStateDialog.hide()
1073 class InfoBarTimeshiftState(InfoBarPVRState):
1075 InfoBarPVRState.__init__(self, screen=TimeshiftState, force_show = True)
1078 if self.execing and self.timeshift_enabled and self.seekstate != self.SEEK_STATE_PLAY:
1079 self.pvrStateDialog.show()
1081 class InfoBarShowMovies:
1083 # i don't really like this class.
1084 # it calls a not further specified "movie list" on up/down/movieList,
1085 # so this is not more than an action map
1087 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions",
1089 "movieList": (self.showMovies, _("movie list")),
1090 "up": (self.showMovies, _("movie list")),
1091 "down": (self.showMovies, _("movie list"))
1094 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
1098 # Timeshift works the following way:
1099 # demux0 demux1 "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
1100 # - normal playback TUNER unused PLAY enable disable disable
1101 # - user presses "yellow" button. FILE record PAUSE enable disable enable
1102 # - user presess pause again FILE record PLAY enable disable enable
1103 # - user fast forwards FILE record FF enable disable enable
1104 # - end of timeshift buffer reached TUNER record PLAY enable enable disable
1105 # - user backwards FILE record BACK # !! enable disable enable
1109 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
1110 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
1111 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
1112 # - the user can now PVR around
1113 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
1114 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
1116 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
1117 # - if the user rewinds, or press pause, timeshift will be activated again
1119 # note that a timeshift can be enabled ("recording") and
1120 # activated (currently time-shifting).
1122 class InfoBarTimeshift:
1124 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions",
1126 "timeshiftStart": (self.startTimeshift, _("start timeshift")), # the "yellow key"
1127 "timeshiftStop": (self.stopTimeshift, _("stop timeshift")) # currently undefined :), probably 'TV'
1129 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
1131 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "rewind key"
1132 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause # something like "pause key"
1133 }, prio=-1) # priority over record
1135 self.timeshift_enabled = 0
1136 self.timeshift_state = 0
1137 self.ts_rewind_timer = eTimer()
1138 self.ts_rewind_timer.callback.append(self.rewindService)
1140 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1142 iPlayableService.evStart: self.__serviceStarted,
1143 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged
1146 def getTimeshift(self):
1147 service = self.session.nav.getCurrentService()
1148 return service and service.timeshift()
1150 def startTimeshift(self):
1151 print "enable timeshift"
1152 ts = self.getTimeshift()
1154 self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
1155 print "no ts interface"
1158 if self.timeshift_enabled:
1159 print "hu, timeshift already enabled?"
1161 if not ts.startTimeshift():
1162 self.timeshift_enabled = 1
1164 # we remove the "relative time" for now.
1165 #self.pvrStateDialog["timeshift"].setRelative(time.time())
1168 #self.setSeekState(self.SEEK_STATE_PAUSE)
1169 self.activateTimeshiftEnd(False)
1171 # enable the "TimeshiftEnableActions", which will override
1172 # the startTimeshift actions
1173 self.__seekableStatusChanged()
1175 print "timeshift failed"
1177 def stopTimeshift(self):
1178 if not self.timeshift_enabled:
1180 print "disable timeshift"
1181 ts = self.getTimeshift()
1184 self.session.openWithCallback(self.stopTimeshiftConfirmed, MessageBox, _("Stop Timeshift?"), MessageBox.TYPE_YESNO)
1186 def stopTimeshiftConfirmed(self, confirmed):
1190 ts = self.getTimeshift()
1195 self.timeshift_enabled = 0
1198 self.__seekableStatusChanged()
1200 # activates timeshift, and seeks to (almost) the end
1201 def activateTimeshiftEnd(self, back = True):
1202 ts = self.getTimeshift()
1203 print "activateTimeshiftEnd"
1208 if ts.isTimeshiftActive():
1209 print "!! activate timeshift called - but shouldn't this be a normal pause?"
1213 ts.activateTimeshift() # activate timeshift will automatically pause
1214 self.setSeekState(self.SEEK_STATE_PAUSE)
1217 self.doSeek(-5) # seek some gops before end
1218 self.ts_rewind_timer.start(200, 1)
1220 self.doSeek(-1) # seek 1 gop before end
1222 def rewindService(self):
1223 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1225 # same as activateTimeshiftEnd, but pauses afterwards.
1226 def activateTimeshiftEndAndPause(self):
1227 print "activateTimeshiftEndAndPause"
1228 #state = self.seekstate
1229 self.activateTimeshiftEnd(False)
1231 def __seekableStatusChanged(self):
1234 # print "self.isSeekable", self.isSeekable()
1235 # print "self.timeshift_enabled", self.timeshift_enabled
1237 # when this service is not seekable, but timeshift
1238 # is enabled, this means we can activate
1240 if not self.isSeekable() and self.timeshift_enabled:
1243 # print "timeshift activate:", enabled
1244 self["TimeshiftActivateActions"].setEnabled(enabled)
1246 def __serviceStarted(self):
1247 self.timeshift_enabled = False
1248 self.__seekableStatusChanged()
1250 from Screens.PiPSetup import PiPSetup
1252 class InfoBarExtensions:
1253 EXTENSION_SINGLE = 0
1259 self["InstantExtensionsActions"] = HelpableActionMap(self, "InfobarExtensions",
1261 "extensions": (self.showExtensionSelection, _("view extensions...")),
1262 }, 1) # lower priority
1264 def addExtension(self, extension, key = None, type = EXTENSION_SINGLE):
1265 self.list.append((type, extension, key))
1267 def updateExtension(self, extension, key = None):
1268 self.extensionsList.append(extension)
1270 if self.extensionKeys.has_key(key):
1274 for x in self.availableKeys:
1275 if not self.extensionKeys.has_key(x):
1280 self.extensionKeys[key] = len(self.extensionsList) - 1
1282 def updateExtensions(self):
1283 self.extensionsList = []
1284 self.availableKeys = [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "red", "green", "yellow", "blue" ]
1285 self.extensionKeys = {}
1287 if x[0] == self.EXTENSION_SINGLE:
1288 self.updateExtension(x[1], x[2])
1291 self.updateExtension(y[0], y[1])
1294 def showExtensionSelection(self):
1295 self.updateExtensions()
1296 extensionsList = self.extensionsList[:]
1299 for x in self.availableKeys:
1300 if self.extensionKeys.has_key(x):
1301 entry = self.extensionKeys[x]
1302 extension = self.extensionsList[entry]
1304 name = str(extension[0]())
1305 list.append((extension[0](), extension))
1307 extensionsList.remove(extension)
1309 extensionsList.remove(extension)
1310 list.extend([(x[0](), x) for x in extensionsList])
1312 keys += [""] * len(extensionsList)
1313 self.session.openWithCallback(self.extensionCallback, ChoiceBox, title=_("Please choose an extension..."), list = list, keys = keys, skin_name = "ExtensionsList")
1315 def extensionCallback(self, answer):
1316 if answer is not None:
1319 from Tools.BoundFunction import boundFunction
1321 # depends on InfoBarExtensions
1323 class InfoBarPlugins:
1325 self.addExtension(extension = self.getPluginList, type = InfoBarExtensions.EXTENSION_LIST)
1327 def getPluginName(self, name):
1330 def getPluginList(self):
1331 list = [((boundFunction(self.getPluginName, p.name), boundFunction(self.runPlugin, p), lambda: True), None, p.name) for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EXTENSIONSMENU)]
1332 list.sort(key = lambda e: e[2]) # sort by name
1335 def runPlugin(self, plugin):
1336 if isinstance(self, InfoBarChannelSelection):
1337 plugin(session = self.session, servicelist = self.servicelist)
1339 plugin(session = self.session)
1341 from Components.Task import job_manager
1342 class InfoBarJobman:
1344 self.addExtension(extension = self.getJobList, type = InfoBarExtensions.EXTENSION_LIST)
1346 def getJobList(self):
1347 return [((boundFunction(self.getJobName, job), boundFunction(self.showJobView, job), lambda: True), None) for job in job_manager.getPendingJobs()]
1349 def getJobName(self, job):
1350 return "%s: %s (%d%%)" % (job.getStatustext(), job.name, int(100*job.progress/float(job.end)))
1352 def showJobView(self, job):
1353 from Screens.TaskView import JobView
1354 job_manager.in_background = False
1355 self.session.openWithCallback(self.JobViewCB, JobView, job)
1357 def JobViewCB(self, in_background):
1358 job_manager.in_background = in_background
1360 # depends on InfoBarExtensions
1361 class InfoBarSleepTimer:
1363 self.addExtension((self.getSleepTimerName, self.showSleepTimerSetup, lambda: True), "1")
1365 def getSleepTimerName(self):
1366 return _("Sleep Timer")
1368 def showSleepTimerSetup(self):
1369 self.session.open(SleepTimerEdit)
1371 # depends on InfoBarExtensions
1375 self.session.pipshown
1377 self.session.pipshown = False
1378 if SystemInfo.get("NumVideoDecoders", 1) > 1:
1380 self.addExtension((self.getShowHideName, self.showPiP, lambda: True), "blue")
1381 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1382 self.addExtension((self.getSwapName, self.swapPiP, self.pipShown), "yellow")
1384 self.addExtension((self.getShowHideName, self.showPiP, self.pipShown), "blue")
1385 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1388 return self.session.pipshown
1390 def pipHandles0Action(self):
1391 return self.pipShown() and config.usage.pip_zero_button.value != "standard"
1393 def getShowHideName(self):
1394 if self.session.pipshown:
1395 return _("Disable Picture in Picture")
1397 return _("Activate Picture in Picture")
1399 def getSwapName(self):
1400 return _("Swap Services")
1402 def getMoveName(self):
1403 return _("Move Picture in Picture")
1406 if self.session.pipshown:
1407 del self.session.pip
1408 self.session.pipshown = False
1410 self.session.pip = self.session.instantiateDialog(PictureInPicture)
1411 self.session.pip.show()
1412 newservice = self.session.nav.getCurrentlyPlayingServiceReference()
1413 if self.session.pip.playService(newservice):
1414 self.session.pipshown = True
1415 self.session.pip.servicePath = self.servicelist.getCurrentServicePath()
1417 self.session.pipshown = False
1418 del self.session.pip
1419 self.session.nav.playService(newservice)
1422 swapservice = self.session.nav.getCurrentlyPlayingServiceReference()
1423 if self.session.pip.servicePath:
1424 servicepath = self.servicelist.getCurrentServicePath()
1425 ref=servicepath[len(servicepath)-1]
1426 pipref=self.session.pip.getCurrentService()
1427 self.session.pip.playService(swapservice)
1428 self.servicelist.setCurrentServicePath(self.session.pip.servicePath)
1429 if pipref.toString() != ref.toString(): # is a subservice ?
1430 self.session.nav.stopService() # stop portal
1431 self.session.nav.playService(pipref) # start subservice
1432 self.session.pip.servicePath=servicepath
1435 self.session.open(PiPSetup, pip = self.session.pip)
1437 def pipDoHandle0Action(self):
1438 use = config.usage.pip_zero_button.value
1441 elif "swapstop" == use:
1447 from RecordTimer import parseEvent, RecordTimerEntry
1449 class InfoBarInstantRecord:
1450 """Instant Record - handles the instantRecord action in order to
1451 start/stop instant records"""
1453 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
1455 "instantRecord": (self.instantRecord, _("Instant Record...")),
1459 def stopCurrentRecording(self, entry = -1):
1460 if entry is not None and entry != -1:
1461 self.session.nav.RecordTimer.removeEntry(self.recording[entry])
1462 self.recording.remove(self.recording[entry])
1464 def startInstantRecording(self, limitEvent = False):
1465 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
1467 # try to get event info
1470 service = self.session.nav.getCurrentService()
1471 epg = eEPGCache.getInstance()
1472 event = epg.lookupEventTime(serviceref, -1, 0)
1474 info = service.info()
1475 ev = info.getEvent(0)
1481 end = begin + 3600 # dummy
1482 name = "instant record"
1486 if event is not None:
1487 curEvent = parseEvent(event)
1489 description = curEvent[3]
1490 eventid = curEvent[4]
1495 self.session.open(MessageBox, _("No event info found, recording indefinitely."), MessageBox.TYPE_INFO)
1497 if isinstance(serviceref, eServiceReference):
1498 serviceref = ServiceReference(serviceref)
1500 recording = RecordTimerEntry(serviceref, begin, end, name, description, eventid, dirname = preferredInstantRecordPath())
1501 recording.dontSave = True
1503 if event is None or limitEvent == False:
1504 recording.autoincrease = True
1505 if recording.setAutoincreaseEnd():
1506 self.session.nav.RecordTimer.record(recording)
1507 self.recording.append(recording)
1509 simulTimerList = self.session.nav.RecordTimer.record(recording)
1510 if simulTimerList is not None: # conflict with other recording
1511 name = simulTimerList[1].name
1512 name_date = ' '.join((name, strftime('%c', localtime(simulTimerList[1].begin))))
1513 print "[TIMER] conflicts with", name_date
1514 recording.autoincrease = True # start with max available length, then increment
1515 if recording.setAutoincreaseEnd():
1516 self.session.nav.RecordTimer.record(recording)
1517 self.recording.append(recording)
1518 self.session.open(MessageBox, _("Record time limited due to conflicting timer %s") % name_date, MessageBox.TYPE_INFO)
1520 self.session.open(MessageBox, _("Couldn't record due to conflicting timer %s") % name, MessageBox.TYPE_INFO)
1521 recording.autoincrease = False
1523 self.recording.append(recording)
1525 def isInstantRecordRunning(self):
1526 print "self.recording:", self.recording
1528 for x in self.recording:
1533 def recordQuestionCallback(self, answer):
1534 print "pre:\n", self.recording
1536 if answer is None or answer[1] == "no":
1539 recording = self.recording[:]
1541 if not x in self.session.nav.RecordTimer.timer_list:
1542 self.recording.remove(x)
1543 elif x.dontSave and x.isRunning():
1544 list.append((x, False))
1546 if answer[1] == "changeduration":
1547 if len(self.recording) == 1:
1548 self.changeDuration(0)
1550 self.session.openWithCallback(self.changeDuration, TimerSelection, list)
1551 elif answer[1] == "changeendtime":
1552 if len(self.recording) == 1:
1555 self.session.openWithCallback(self.setEndtime, TimerSelection, list)
1556 elif answer[1] == "stop":
1557 if len(self.recording) == 1:
1558 self.stopCurrentRecording(0)
1560 self.session.openWithCallback(self.stopCurrentRecording, TimerSelection, list)
1561 elif answer[1] in ( "indefinitely" , "manualduration", "manualendtime", "event"):
1562 self.startInstantRecording(limitEvent = answer[1] in ("event", "manualendtime") or False)
1563 if answer[1] == "manualduration":
1564 self.changeDuration(len(self.recording)-1)
1565 elif answer[1] == "manualendtime":
1566 self.setEndtime(len(self.recording)-1)
1567 print "after:\n", self.recording
1569 def setEndtime(self, entry):
1570 if entry is not None and entry >= 0:
1571 self.selectedEntry = entry
1572 self.endtime=ConfigClock(default = self.recording[self.selectedEntry].end)
1573 dlg = self.session.openWithCallback(self.TimeDateInputClosed, TimeDateInput, self.endtime)
1574 dlg.setTitle(_("Please change recording endtime"))
1576 def TimeDateInputClosed(self, ret):
1579 localendtime = localtime(ret[1])
1580 print "stopping recording at", strftime("%c", localendtime)
1581 if self.recording[self.selectedEntry].end != ret[1]:
1582 self.recording[self.selectedEntry].autoincrease = False
1583 self.recording[self.selectedEntry].end = ret[1]
1584 self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
1586 def changeDuration(self, entry):
1587 if entry is not None and entry >= 0:
1588 self.selectedEntry = entry
1589 self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1591 def inputCallback(self, value):
1592 if value is not None:
1593 print "stopping recording after", int(value), "minutes."
1594 entry = self.recording[self.selectedEntry]
1596 entry.autoincrease = False
1597 entry.end = int(time()) + 60 * int(value)
1598 self.session.nav.RecordTimer.timeChanged(entry)
1600 def instantRecord(self):
1601 dir = preferredInstantRecordPath()
1602 if not dir or not fileExists(dir, 'w'):
1603 dir = defaultMoviePath()
1607 # XXX: this message is a little odd as we might be recording to a remote device
1608 self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1611 if self.isInstantRecordRunning():
1612 self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1613 title=_("A recording is currently running.\nWhat do you want to do?"), \
1614 list=((_("stop recording"), "stop"), \
1615 (_("add recording (stop after current event)"), "event"), \
1616 (_("add recording (indefinitely)"), "indefinitely"), \
1617 (_("add recording (enter recording duration)"), "manualduration"), \
1618 (_("add recording (enter recording endtime)"), "manualendtime"), \
1619 (_("change recording (duration)"), "changeduration"), \
1620 (_("change recording (endtime)"), "changeendtime"), \
1621 (_("do nothing"), "no")))
1623 self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1624 title=_("Start recording?"), \
1625 list=((_("add recording (stop after current event)"), "event"), \
1626 (_("add recording (indefinitely)"), "indefinitely"), \
1627 (_("add recording (enter recording duration)"), "manualduration"), \
1628 (_("add recording (enter recording endtime)"), "manualendtime"), \
1629 (_("don't record"), "no")))
1631 from Tools.ISO639 import LanguageCodes
1633 class InfoBarAudioSelection:
1635 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions",
1637 "audioSelection": (self.audioSelection, _("Audio Options...")),
1640 def audioSelection(self):
1641 service = self.session.nav.getCurrentService()
1642 self.audioTracks = audio = service and service.audioTracks()
1643 n = audio and audio.getNumberOfTracks() or 0
1646 self.audioChannel = service.audioChannel()
1651 i = audio.getTrackInfo(idx)
1652 languages = i.getLanguage().split('/')
1653 description = i.getDescription()
1656 for lang in languages:
1659 if LanguageCodes.has_key(lang):
1660 language += LanguageCodes[lang][0]
1665 if len(description):
1666 description += " (" + language + ")"
1668 description = language
1670 tlist.append((description, idx))
1673 tlist.sort(key=lambda x: x[0])
1675 selectedAudio = self.audioTracks.getCurrentTrack()
1680 if x[1] != selectedAudio:
1685 if SystemInfo["CanDownmixAC3"]:
1686 tlist = [(_("AC3 downmix") + " - " +(_("Off"), _("On"))[config.av.downmix_ac3.value and 1 or 0], "CALLFUNC", self.changeAC3Downmix),
1687 ((_("Left"), _("Stereo"), _("Right"))[self.audioChannel.getCurrentChannel()], "mode"),
1689 keys = [ "red", "green", "", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"] + [""]*n
1692 tlist = [((_("Left"), _("Stereo"), _("Right"))[self.audioChannel.getCurrentChannel()], "mode"), ("--", "")] + tlist
1693 keys = [ "red", "", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"] + [""]*n
1695 self.session.openWithCallback(self.audioSelected, ChoiceBox, title=_("Select audio track"), list = tlist, selection = selection, keys = keys, skin_name = "AudioTrackSelection")
1697 del self.audioTracks
1699 def changeAC3Downmix(self, arg):
1700 choicelist = self.session.current_dialog["list"]
1701 list = choicelist.list
1703 list[0][1]=(t[0], t[1], t[2], t[3], t[4], t[5], t[6],
1704 _("AC3 downmix") + " - " + (_("On"), _("Off"))[config.av.downmix_ac3.value and 1 or 0])
1705 choicelist.setList(list)
1706 if config.av.downmix_ac3.value:
1707 config.av.downmix_ac3.value = False
1709 config.av.downmix_ac3.value = True
1710 config.av.downmix_ac3.save()
1712 def audioSelected(self, audio):
1713 if audio is not None:
1714 if isinstance(audio[1], str):
1715 if audio[1] == "mode":
1716 keys = ["red", "green", "yellow"]
1717 selection = self.audioChannel.getCurrentChannel()
1718 tlist = ((_("left"), 0), (_("stereo"), 1), (_("right"), 2))
1719 self.session.openWithCallback(self.modeSelected, ChoiceBox, title=_("Select audio mode"), list = tlist, selection = selection, keys = keys, skin_name ="AudioModeSelection")
1721 del self.audioChannel
1722 if self.session.nav.getCurrentService().audioTracks().getNumberOfTracks() > audio[1]:
1723 self.audioTracks.selectTrack(audio[1])
1725 del self.audioChannel
1726 del self.audioTracks
1728 def modeSelected(self, mode):
1729 if mode is not None:
1730 self.audioChannel.selectChannel(mode[1])
1731 del self.audioChannel
1733 class InfoBarSubserviceSelection:
1735 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1737 "subserviceSelection": (self.subserviceSelection, _("Subservice list...")),
1740 self["SubserviceQuickzapAction"] = HelpableActionMap(self, "InfobarSubserviceQuickzapActions",
1742 "nextSubservice": (self.nextSubservice, _("Switch to next subservice")),
1743 "prevSubservice": (self.prevSubservice, _("Switch to previous subservice"))
1745 self["SubserviceQuickzapAction"].setEnabled(False)
1747 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1749 iPlayableService.evUpdatedEventInfo: self.checkSubservicesAvail
1754 def checkSubservicesAvail(self):
1755 service = self.session.nav.getCurrentService()
1756 subservices = service and service.subServices()
1757 if not subservices or subservices.getNumberOfSubservices() == 0:
1758 self["SubserviceQuickzapAction"].setEnabled(False)
1760 def nextSubservice(self):
1761 self.changeSubservice(+1)
1763 def prevSubservice(self):
1764 self.changeSubservice(-1)
1766 def changeSubservice(self, direction):
1767 service = self.session.nav.getCurrentService()
1768 subservices = service and service.subServices()
1769 n = subservices and subservices.getNumberOfSubservices()
1772 ref = self.session.nav.getCurrentlyPlayingServiceReference()
1775 if subservices.getSubservice(idx).toString() == ref.toString():
1780 selection += direction
1785 newservice = subservices.getSubservice(selection)
1786 if newservice.valid():
1789 self.session.nav.playService(newservice, False)
1791 def subserviceSelection(self):
1792 service = self.session.nav.getCurrentService()
1793 subservices = service and service.subServices()
1794 self.bouquets = self.servicelist.getBouquetList()
1795 n = subservices and subservices.getNumberOfSubservices()
1798 ref = self.session.nav.getCurrentlyPlayingServiceReference()
1802 i = subservices.getSubservice(idx)
1803 if i.toString() == ref.toString():
1805 tlist.append((i.getName(), i))
1808 if self.bouquets and len(self.bouquets):
1809 keys = ["red", "blue", "", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1810 if config.usage.multibouquet.value:
1811 tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to bouquet"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1813 tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to favourites"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1816 tlist = [(_("Quickzap"), "quickzap", service.subServices()), ("--", "")] + tlist
1817 keys = ["red", "", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1820 self.session.openWithCallback(self.subserviceSelected, ChoiceBox, title=_("Please select a subservice..."), list = tlist, selection = selection, keys = keys, skin_name = "SubserviceSelection")
1822 def subserviceSelected(self, service):
1824 if not service is None:
1825 if isinstance(service[1], str):
1826 if service[1] == "quickzap":
1827 from Screens.SubservicesQuickzap import SubservicesQuickzap
1828 self.session.open(SubservicesQuickzap, service[2])
1830 self["SubserviceQuickzapAction"].setEnabled(True)
1831 self.session.nav.playService(service[1], False)
1833 def addSubserviceToBouquetCallback(self, service):
1834 if len(service) > 1 and isinstance(service[1], eServiceReference):
1835 self.selectedSubservice = service
1836 if self.bouquets is None:
1839 cnt = len(self.bouquets)
1840 if cnt > 1: # show bouquet list
1841 self.bsel = self.session.openWithCallback(self.bouquetSelClosed, BouquetSelector, self.bouquets, self.addSubserviceToBouquet)
1842 elif cnt == 1: # add to only one existing bouquet
1843 self.addSubserviceToBouquet(self.bouquets[0][1])
1844 self.session.open(MessageBox, _("Service has been added to the favourites."), MessageBox.TYPE_INFO)
1846 def bouquetSelClosed(self, confirmed):
1848 del self.selectedSubservice
1850 self.session.open(MessageBox, _("Service has been added to the selected bouquet."), MessageBox.TYPE_INFO)
1852 def addSubserviceToBouquet(self, dest):
1853 self.servicelist.addServiceToBouquet(dest, self.selectedSubservice[1])
1855 self.bsel.close(True)
1857 del self.selectedSubservice
1859 class InfoBarAdditionalInfo:
1862 self["RecordingPossible"] = Boolean(fixed=harddiskmanager.HDDCount() > 0 and config.misc.rcused.value == 1)
1863 self["TimeshiftPossible"] = self["RecordingPossible"]
1864 self["ShowTimeshiftOnYellow"] = Boolean(fixed=(not config.misc.rcused.value == 0))
1865 self["ShowAudioOnYellow"] = Boolean(fixed=config.misc.rcused.value == 0)
1866 self["ShowRecordOnRed"] = Boolean(fixed=config.misc.rcused.value == 1)
1867 self["ExtensionsAvailable"] = Boolean(fixed=1)
1869 class InfoBarNotifications:
1871 self.onExecBegin.append(self.checkNotifications)
1872 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1873 self.onClose.append(self.__removeNotification)
1875 def __removeNotification(self):
1876 Notifications.notificationAdded.remove(self.checkNotificationsIfExecing)
1878 def checkNotificationsIfExecing(self):
1880 self.checkNotifications()
1882 def checkNotifications(self):
1883 notifications = Notifications.notifications
1885 n = notifications[0]
1887 del notifications[0]
1890 if n[3].has_key("onSessionOpenCallback"):
1891 n[3]["onSessionOpenCallback"]()
1892 del n[3]["onSessionOpenCallback"]
1895 dlg = self.session.openWithCallback(cb, n[1], *n[2], **n[3])
1897 dlg = self.session.open(n[1], *n[2], **n[3])
1899 # remember that this notification is currently active
1901 Notifications.current_notifications.append(d)
1902 dlg.onClose.append(boundFunction(self.__notificationClosed, d))
1904 def __notificationClosed(self, d):
1905 Notifications.current_notifications.remove(d)
1907 class InfoBarServiceNotifications:
1909 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1911 iPlayableService.evEnd: self.serviceHasEnded
1914 def serviceHasEnded(self):
1915 print "service end!"
1918 self.setSeekState(self.SEEK_STATE_PLAY)
1922 class InfoBarCueSheetSupport:
1928 ENABLE_RESUME_SUPPORT = False
1930 def __init__(self, actionmap = "InfobarCueSheetActions"):
1931 self["CueSheetActions"] = HelpableActionMap(self, actionmap,
1933 "jumpPreviousMark": (self.jumpPreviousMark, _("jump to previous marked position")),
1934 "jumpNextMark": (self.jumpNextMark, _("jump to next marked position")),
1935 "toggleMark": (self.toggleMark, _("toggle a cut mark at the current position"))
1939 self.is_closing = False
1940 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1942 iPlayableService.evStart: self.__serviceStarted,
1945 def __serviceStarted(self):
1948 print "new service started! trying to download cuts!"
1949 self.downloadCuesheet()
1951 if self.ENABLE_RESUME_SUPPORT:
1954 for (pts, what) in self.cut_list:
1955 if what == self.CUT_TYPE_LAST:
1958 if last is not None:
1959 self.resume_point = last
1960 if config.usage.on_movie_start.value == "ask":
1961 Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Do you want to resume this playback?"), timeout=10)
1962 elif config.usage.on_movie_start.value == "resume":
1963 # TRANSLATORS: The string "Resuming playback" flashes for a moment
1964 # TRANSLATORS: at the start of a movie, when the user has selected
1965 # TRANSLATORS: "Resume from last position" as start behavior.
1966 # TRANSLATORS: The purpose is to notify the user that the movie starts
1967 # TRANSLATORS: in the middle somewhere and not from the beginning.
1968 # TRANSLATORS: (Some translators seem to have interpreted it as a
1969 # TRANSLATORS: question or a choice, but it is a statement.)
1970 Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Resuming playback"), timeout=2, type=MessageBox.TYPE_INFO)
1972 def playLastCB(self, answer):
1974 self.doSeek(self.resume_point)
1975 self.hideAfterResume()
1977 def hideAfterResume(self):
1978 if isinstance(self, InfoBarShowHide):
1981 def __getSeekable(self):
1982 service = self.session.nav.getCurrentService()
1985 return service.seek()
1987 def cueGetCurrentPosition(self):
1988 seek = self.__getSeekable()
1991 r = seek.getPlayPosition()
1996 def cueGetEndCutPosition(self):
1999 for cp in self.cut_list:
2000 if cp[1] == self.CUT_TYPE_OUT:
2004 elif cp[1] == self.CUT_TYPE_IN:
2008 def jumpPreviousNextMark(self, cmp, start=False):
2009 current_pos = self.cueGetCurrentPosition()
2010 if current_pos is None:
2012 mark = self.getNearestCutPoint(current_pos, cmp=cmp, start=start)
2013 if mark is not None:
2021 def jumpPreviousMark(self):
2022 # we add 2 seconds, so if the play position is <2s after
2023 # the mark, the mark before will be used
2024 self.jumpPreviousNextMark(lambda x: -x-5*90000, start=True)
2026 def jumpNextMark(self):
2027 if not self.jumpPreviousNextMark(lambda x: x):
2030 def getNearestCutPoint(self, pts, cmp=abs, start=False):
2036 bestdiff = cmp(0 - pts)
2038 nearest = [0, False]
2039 for cp in self.cut_list:
2040 if beforecut and cp[1] in (self.CUT_TYPE_IN, self.CUT_TYPE_OUT):
2042 if cp[1] == self.CUT_TYPE_IN: # Start is here, disregard previous marks
2043 diff = cmp(cp[0] - pts)
2049 if cp[1] in (self.CUT_TYPE_MARK, self.CUT_TYPE_LAST):
2050 diff = cmp(cp[0] - pts)
2051 if diff >= 0 and (nearest is None or bestdiff > diff):
2056 def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
2057 current_pos = self.cueGetCurrentPosition()
2058 if current_pos is None:
2059 print "not seekable"
2062 nearest_cutpoint = self.getNearestCutPoint(current_pos)
2064 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
2066 return nearest_cutpoint
2068 self.removeMark(nearest_cutpoint)
2069 elif not onlyremove and not onlyreturn:
2070 self.addMark((current_pos, self.CUT_TYPE_MARK))
2075 def addMark(self, point):
2076 insort(self.cut_list, point)
2077 self.uploadCuesheet()
2078 self.showAfterCuesheetOperation()
2080 def removeMark(self, point):
2081 self.cut_list.remove(point)
2082 self.uploadCuesheet()
2083 self.showAfterCuesheetOperation()
2085 def showAfterCuesheetOperation(self):
2086 if isinstance(self, InfoBarShowHide):
2089 def __getCuesheet(self):
2090 service = self.session.nav.getCurrentService()
2093 return service.cueSheet()
2095 def uploadCuesheet(self):
2096 cue = self.__getCuesheet()
2099 print "upload failed, no cuesheet interface"
2101 cue.setCutList(self.cut_list)
2103 def downloadCuesheet(self):
2104 cue = self.__getCuesheet()
2107 print "download failed, no cuesheet interface"
2110 self.cut_list = cue.getCutList()
2112 class InfoBarSummary(Screen):
2114 <screen position="0,0" size="132,64">
2115 <widget source="global.CurrentTime" render="Label" position="62,46" size="82,18" font="Regular;16" >
2116 <convert type="ClockToText">WithSeconds</convert>
2118 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="82,18" zPosition="1" >
2119 <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2120 <convert type="ConditionalShowHide">Blink</convert>
2122 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2123 <convert type="ServiceName">Name</convert>
2125 <widget source="session.Event_Now" render="Progress" position="6,46" size="46,18" borderWidth="1" >
2126 <convert type="EventTime">Progress</convert>
2130 # for picon: (path="piconlcd" will use LCD picons)
2131 # <widget source="session.CurrentService" render="Picon" position="6,0" size="120,64" path="piconlcd" >
2132 # <convert type="ServiceName">Reference</convert>
2135 class InfoBarSummarySupport:
2139 def createSummary(self):
2140 return InfoBarSummary
2142 class InfoBarMoviePlayerSummary(Screen):
2144 <screen position="0,0" size="132,64">
2145 <widget source="global.CurrentTime" render="Label" position="62,46" size="64,18" font="Regular;16" halign="right" >
2146 <convert type="ClockToText">WithSeconds</convert>
2148 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="64,18" zPosition="1" >
2149 <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2150 <convert type="ConditionalShowHide">Blink</convert>
2152 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2153 <convert type="ServiceName">Name</convert>
2155 <widget source="session.CurrentService" render="Progress" position="6,46" size="56,18" borderWidth="1" >
2156 <convert type="ServicePosition">Position</convert>
2160 class InfoBarMoviePlayerSummarySupport:
2164 def createSummary(self):
2165 return InfoBarMoviePlayerSummary
2167 class InfoBarTeletextPlugin:
2169 self.teletext_plugin = None
2171 for p in plugins.getPlugins(PluginDescriptor.WHERE_TELETEXT):
2172 self.teletext_plugin = p
2174 if self.teletext_plugin is not None:
2175 self["TeletextActions"] = HelpableActionMap(self, "InfobarTeletextActions",
2177 "startTeletext": (self.startTeletext, _("View teletext..."))
2180 print "no teletext plugin found!"
2182 def startTeletext(self):
2183 self.teletext_plugin(session=self.session, service=self.session.nav.getCurrentService())
2185 class InfoBarSubtitleSupport(object):
2187 object.__init__(self)
2188 self.subtitle_window = self.session.instantiateDialog(SubtitleDisplay)
2189 self.__subtitles_enabled = False
2191 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2193 iPlayableService.evEnd: self.__serviceStopped,
2194 iPlayableService.evUpdatedInfo: self.__updatedInfo
2196 self.cached_subtitle_checked = False
2197 self.__selected_subtitle = None
2199 def __serviceStopped(self):
2200 self.cached_subtitle_checked = False
2201 if self.__subtitles_enabled:
2202 self.subtitle_window.hide()
2203 self.__subtitles_enabled = False
2204 self.__selected_subtitle = None
2206 def __updatedInfo(self):
2207 if not self.cached_subtitle_checked:
2208 self.cached_subtitle_checked = True
2209 subtitle = self.getCurrentServiceSubtitle()
2210 self.setSelectedSubtitle(subtitle and subtitle.getCachedSubtitle())
2211 if self.__selected_subtitle:
2212 self.setSubtitlesEnable(True)
2214 def getCurrentServiceSubtitle(self):
2215 service = self.session.nav.getCurrentService()
2216 return service and service.subtitle()
2218 def setSubtitlesEnable(self, enable=True):
2219 subtitle = self.getCurrentServiceSubtitle()
2221 if self.__selected_subtitle:
2222 if subtitle and not self.__subtitles_enabled:
2223 subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
2224 self.subtitle_window.show()
2225 self.__subtitles_enabled = True
2228 subtitle.disableSubtitles(self.subtitle_window.instance)
2229 self.__selected_subtitle = False
2230 self.__subtitles_enabled = False
2231 self.subtitle_window.hide()
2233 def setSelectedSubtitle(self, subtitle):
2234 self.__selected_subtitle = subtitle
2236 subtitles_enabled = property(lambda self: self.__subtitles_enabled, setSubtitlesEnable)
2237 selected_subtitle = property(lambda self: self.__selected_subtitle, setSelectedSubtitle)
2239 class InfoBarServiceErrorPopupSupport:
2241 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2243 iPlayableService.evTuneFailed: self.__tuneFailed,
2244 iPlayableService.evStart: self.__serviceStarted
2246 self.__serviceStarted()
2248 def __serviceStarted(self):
2249 self.last_error = None
2250 Notifications.RemovePopup(id = "ZapError")
2252 def __tuneFailed(self):
2253 service = self.session.nav.getCurrentService()
2254 info = service and service.info()
2255 error = info and info.getInfo(iServiceInformation.sDVBState)
2257 if error == self.last_error:
2260 self.last_error = error
2263 eDVBServicePMTHandler.eventNoResources: _("No free tuner!"),
2264 eDVBServicePMTHandler.eventTuneFailed: _("Tune failed!"),
2265 eDVBServicePMTHandler.eventNoPAT: _("No data on transponder!\n(Timeout reading PAT)"),
2266 eDVBServicePMTHandler.eventNoPATEntry: _("Service not found!\n(SID not found in PAT)"),
2267 eDVBServicePMTHandler.eventNoPMT: _("Service invalid!\n(Timeout reading PMT)"),
2268 eDVBServicePMTHandler.eventNewProgramInfo: None,
2269 eDVBServicePMTHandler.eventTuned: None,
2270 eDVBServicePMTHandler.eventSOF: None,
2271 eDVBServicePMTHandler.eventEOF: None,
2272 eDVBServicePMTHandler.eventMisconfiguration: _("Service unavailable!\nCheck tuner configuration!"),
2273 }.get(error) #this returns None when the key not exist in the dict
2275 if error is not None:
2276 Notifications.AddPopup(text = error, type = MessageBox.TYPE_ERROR, timeout = 5, id = "ZapError")
2278 Notifications.RemovePopup(id = "ZapError")