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,
693 self.fast_winding_hint_message_showed = False
695 self.minSpeedBackward = useSeekBackHack and 16 or 0
697 class InfoBarSeekActionMap(HelpableActionMap):
698 def __init__(self, screen, *args, **kwargs):
699 HelpableActionMap.__init__(self, screen, *args, **kwargs)
702 def action(self, contexts, action):
703 print "action:", action
704 if action[:5] == "seek:":
705 time = int(action[5:])
706 self.screen.doSeekRelative(time * 90000)
708 elif action[:8] == "seekdef:":
709 key = int(action[8:])
710 time = (-config.seek.selfdefined_13.value, False, config.seek.selfdefined_13.value,
711 -config.seek.selfdefined_46.value, False, config.seek.selfdefined_46.value,
712 -config.seek.selfdefined_79.value, False, config.seek.selfdefined_79.value)[key-1]
713 self.screen.doSeekRelative(time * 90000)
716 return HelpableActionMap.action(self, contexts, action)
718 self["SeekActions"] = InfoBarSeekActionMap(self, actionmap,
720 "playpauseService": self.playpauseService,
721 "pauseService": (self.pauseService, _("pause")),
722 "unPauseService": (self.unPauseService, _("continue")),
724 "seekFwd": (self.seekFwd, _("skip forward")),
725 "seekFwdManual": (self.seekFwdManual, _("skip forward (enter time)")),
726 "seekBack": (self.seekBack, _("skip backward")),
727 "seekBackManual": (self.seekBackManual, _("skip backward (enter time)"))
729 # give them a little more priority to win over color buttons
731 self["SeekActions"].setEnabled(False)
733 self.seekstate = self.SEEK_STATE_PLAY
734 self.lastseekstate = self.SEEK_STATE_PLAY
736 self.onPlayStateChanged = [ ]
738 self.lockedBecauseOfSkipping = False
740 self.__seekableStatusChanged()
742 def makeStateForward(self, n):
743 minspeed = config.seek.stepwise_minspeed.value
744 repeat = int(config.seek.stepwise_repeat.value)
745 if minspeed != "Never" and n >= int(minspeed) and repeat > 1:
746 return (0, n * repeat, repeat, ">> %dx" % n)
748 return (0, n, 0, ">> %dx" % n)
750 def makeStateBackward(self, n):
751 minspeed = config.seek.stepwise_minspeed.value
752 repeat = int(config.seek.stepwise_repeat.value)
753 if self.minSpeedBackward and n < self.minSpeedBackward:
754 r = (self.minSpeedBackward - 1)/ n + 1
755 if minspeed != "Never" and n >= int(minspeed) and repeat > 1:
757 return (0, -n * r, r, "<< %dx" % n)
758 elif minspeed != "Never" and n >= int(minspeed) and repeat > 1:
759 return (0, -n * repeat, repeat, "<< %dx" % n)
761 return (0, -n, 0, "<< %dx" % n)
763 def makeStateSlowMotion(self, n):
764 return (0, 0, n, "/%d" % n)
766 def isStateForward(self, state):
769 def isStateBackward(self, state):
772 def isStateSlowMotion(self, state):
773 return state[1] == 0 and state[2] > 1
775 def getHigher(self, n, lst):
781 def getLower(self, n, lst):
789 def showAfterSeek(self):
790 if isinstance(self, InfoBarShowHide):
800 service = self.session.nav.getCurrentService()
804 seek = service.seek()
806 if seek is None or not seek.isCurrentlySeekable():
811 def isSeekable(self):
812 if self.getSeek() is None:
816 def __seekableStatusChanged(self):
817 # print "seekable status changed!"
818 if not self.isSeekable():
819 self["SeekActions"].setEnabled(False)
820 # print "not seekable, return to play"
821 self.setSeekState(self.SEEK_STATE_PLAY)
823 self["SeekActions"].setEnabled(True)
826 def __serviceStarted(self):
827 self.fast_winding_hint_message_showed = False
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 seek = self.getSeek()
919 if seek and not (seek.isCurrentlySeekable() & 2):
920 if not self.fast_winding_hint_message_showed and (seek.isCurrentlySeekable() & 1):
921 self.session.open(MessageBox, _("No fast winding possible yet.. but you can use the number buttons to skip forward/backward!"), MessageBox.TYPE_INFO, timeout=10)
922 self.fast_winding_hint_message_showed = True
924 return 0 # trade as unhandled action
925 if self.seekstate == self.SEEK_STATE_PLAY:
926 self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
927 elif self.seekstate == self.SEEK_STATE_PAUSE:
928 if len(config.seek.speeds_slowmotion.value):
929 self.setSeekState(self.makeStateSlowMotion(config.seek.speeds_slowmotion.value[-1]))
931 self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
932 elif self.seekstate == self.SEEK_STATE_EOF:
934 elif self.isStateForward(self.seekstate):
935 speed = self.seekstate[1]
936 if self.seekstate[2]:
937 speed /= self.seekstate[2]
938 speed = self.getHigher(speed, config.seek.speeds_forward.value) or config.seek.speeds_forward.value[-1]
939 self.setSeekState(self.makeStateForward(speed))
940 elif self.isStateBackward(self.seekstate):
941 speed = -self.seekstate[1]
942 if self.seekstate[2]:
943 speed /= self.seekstate[2]
944 speed = self.getLower(speed, config.seek.speeds_backward.value)
946 self.setSeekState(self.makeStateBackward(speed))
948 self.setSeekState(self.SEEK_STATE_PLAY)
949 elif self.isStateSlowMotion(self.seekstate):
950 speed = self.getLower(self.seekstate[2], config.seek.speeds_slowmotion.value) or config.seek.speeds_slowmotion.value[0]
951 self.setSeekState(self.makeStateSlowMotion(speed))
954 seek = self.getSeek()
955 if seek and not (seek.isCurrentlySeekable() & 2):
956 if not self.fast_winding_hint_message_showed and (seek.isCurrentlySeekable() & 1):
957 self.session.open(MessageBox, _("No fast winding possible yet.. but you can use the number buttons to skip forward/backward!"), MessageBox.TYPE_INFO, timeout=10)
958 self.fast_winding_hint_message_showed = True
960 return 0 # trade as unhandled action
961 seekstate = self.seekstate
962 if seekstate == self.SEEK_STATE_PLAY:
963 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
964 elif seekstate == self.SEEK_STATE_EOF:
965 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
966 self.doSeekRelative(-6)
967 elif seekstate == self.SEEK_STATE_PAUSE:
968 self.doSeekRelative(-3)
969 elif self.isStateForward(seekstate):
972 speed /= seekstate[2]
973 speed = self.getLower(speed, config.seek.speeds_forward.value)
975 self.setSeekState(self.makeStateForward(speed))
977 self.setSeekState(self.SEEK_STATE_PLAY)
978 elif self.isStateBackward(seekstate):
979 speed = -seekstate[1]
981 speed /= seekstate[2]
982 speed = self.getHigher(speed, config.seek.speeds_backward.value) or config.seek.speeds_backward.value[-1]
983 self.setSeekState(self.makeStateBackward(speed))
984 elif self.isStateSlowMotion(seekstate):
985 speed = self.getHigher(seekstate[2], config.seek.speeds_slowmotion.value)
987 self.setSeekState(self.makeStateSlowMotion(speed))
989 self.setSeekState(self.SEEK_STATE_PAUSE)
991 def seekFwdManual(self):
992 self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
994 def fwdSeekTo(self, minutes):
995 print "Seek", minutes, "minutes forward"
996 self.doSeekRelative(minutes * 60 * 90000)
998 def seekBackManual(self):
999 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
1001 def rwdSeekTo(self, minutes):
1003 self.doSeekRelative(-minutes * 60 * 90000)
1005 def checkSkipShowHideLock(self):
1006 wantlock = self.seekstate != self.SEEK_STATE_PLAY
1008 if config.usage.show_infobar_on_skip.value:
1009 if self.lockedBecauseOfSkipping and not wantlock:
1011 self.lockedBecauseOfSkipping = False
1013 if wantlock and not self.lockedBecauseOfSkipping:
1015 self.lockedBecauseOfSkipping = True
1017 def calcRemainingTime(self):
1018 seekable = self.getSeek()
1019 if seekable is not None:
1020 len = seekable.getLength()
1022 tmp = self.cueGetEndCutPosition()
1027 pos = seekable.getPlayPosition()
1028 speednom = self.seekstate[1] or 1
1029 speedden = self.seekstate[2] or 1
1030 if not len[0] and not pos[0]:
1031 if len[1] <= pos[1]:
1033 time = (len[1] - pos[1])*speedden/(90*speednom)
1038 if self.seekstate == self.SEEK_STATE_EOF:
1041 # if we are seeking forward, we try to end up ~1s before the end, and pause there.
1042 seekstate = self.seekstate
1043 if self.seekstate != self.SEEK_STATE_PAUSE:
1044 self.setSeekState(self.SEEK_STATE_EOF)
1046 if seekstate not in (self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE): # if we are seeking
1047 seekable = self.getSeek()
1048 if seekable is not None:
1050 if seekstate == self.SEEK_STATE_PLAY: # regular EOF
1051 self.doEofInternal(True)
1053 self.doEofInternal(False)
1055 def doEofInternal(self, playing):
1056 pass # Defined in subclasses
1059 self.setSeekState(self.SEEK_STATE_PLAY)
1062 from Screens.PVRState import PVRState, TimeshiftState
1064 class InfoBarPVRState:
1065 def __init__(self, screen=PVRState, force_show = False):
1066 self.onPlayStateChanged.append(self.__playStateChanged)
1067 self.pvrStateDialog = self.session.instantiateDialog(screen)
1068 self.onShow.append(self._mayShow)
1069 self.onHide.append(self.pvrStateDialog.hide)
1070 self.force_show = force_show
1073 if self.execing and self.seekstate != self.SEEK_STATE_PLAY:
1074 self.pvrStateDialog.show()
1076 def __playStateChanged(self, state):
1077 playstateString = state[3]
1078 self.pvrStateDialog["state"].setText(playstateString)
1080 # if we return into "PLAY" state, ensure that the dialog gets hidden if there will be no infobar displayed
1081 if not config.usage.show_infobar_on_skip.value and self.seekstate == self.SEEK_STATE_PLAY and not self.force_show:
1082 self.pvrStateDialog.hide()
1087 class InfoBarTimeshiftState(InfoBarPVRState):
1089 InfoBarPVRState.__init__(self, screen=TimeshiftState, force_show = True)
1092 if self.execing and self.timeshift_enabled and self.seekstate != self.SEEK_STATE_PLAY:
1093 self.pvrStateDialog.show()
1095 class InfoBarShowMovies:
1097 # i don't really like this class.
1098 # it calls a not further specified "movie list" on up/down/movieList,
1099 # so this is not more than an action map
1101 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions",
1103 "movieList": (self.showMovies, _("movie list")),
1104 "up": (self.showMovies, _("movie list")),
1105 "down": (self.showMovies, _("movie list"))
1108 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
1112 # Timeshift works the following way:
1113 # demux0 demux1 "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
1114 # - normal playback TUNER unused PLAY enable disable disable
1115 # - user presses "yellow" button. FILE record PAUSE enable disable enable
1116 # - user presess pause again FILE record PLAY enable disable enable
1117 # - user fast forwards FILE record FF enable disable enable
1118 # - end of timeshift buffer reached TUNER record PLAY enable enable disable
1119 # - user backwards FILE record BACK # !! enable disable enable
1123 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
1124 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
1125 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
1126 # - the user can now PVR around
1127 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
1128 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
1130 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
1131 # - if the user rewinds, or press pause, timeshift will be activated again
1133 # note that a timeshift can be enabled ("recording") and
1134 # activated (currently time-shifting).
1136 class InfoBarTimeshift:
1138 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions",
1140 "timeshiftStart": (self.startTimeshift, _("start timeshift")), # the "yellow key"
1141 "timeshiftStop": (self.stopTimeshift, _("stop timeshift")) # currently undefined :), probably 'TV'
1143 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
1145 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "rewind key"
1146 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause # something like "pause key"
1147 }, prio=-1) # priority over record
1149 self.timeshift_enabled = 0
1150 self.timeshift_state = 0
1151 self.ts_rewind_timer = eTimer()
1152 self.ts_rewind_timer.callback.append(self.rewindService)
1154 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1156 iPlayableService.evStart: self.__serviceStarted,
1157 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged
1160 def getTimeshift(self):
1161 service = self.session.nav.getCurrentService()
1162 return service and service.timeshift()
1164 def startTimeshift(self):
1165 print "enable timeshift"
1166 ts = self.getTimeshift()
1168 self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
1169 print "no ts interface"
1172 if self.timeshift_enabled:
1173 print "hu, timeshift already enabled?"
1175 if not ts.startTimeshift():
1176 self.timeshift_enabled = 1
1178 # we remove the "relative time" for now.
1179 #self.pvrStateDialog["timeshift"].setRelative(time.time())
1182 #self.setSeekState(self.SEEK_STATE_PAUSE)
1183 self.activateTimeshiftEnd(False)
1185 # enable the "TimeshiftEnableActions", which will override
1186 # the startTimeshift actions
1187 self.__seekableStatusChanged()
1189 print "timeshift failed"
1191 def stopTimeshift(self):
1192 if not self.timeshift_enabled:
1194 print "disable timeshift"
1195 ts = self.getTimeshift()
1198 self.session.openWithCallback(self.stopTimeshiftConfirmed, MessageBox, _("Stop Timeshift?"), MessageBox.TYPE_YESNO)
1200 def stopTimeshiftConfirmed(self, confirmed):
1204 ts = self.getTimeshift()
1209 self.timeshift_enabled = 0
1212 self.__seekableStatusChanged()
1214 # activates timeshift, and seeks to (almost) the end
1215 def activateTimeshiftEnd(self, back = True):
1216 ts = self.getTimeshift()
1217 print "activateTimeshiftEnd"
1222 if ts.isTimeshiftActive():
1223 print "!! activate timeshift called - but shouldn't this be a normal pause?"
1227 ts.activateTimeshift() # activate timeshift will automatically pause
1228 self.setSeekState(self.SEEK_STATE_PAUSE)
1231 self.doSeek(-5) # seek some gops before end
1232 self.ts_rewind_timer.start(200, 1)
1234 self.doSeek(-1) # seek 1 gop before end
1236 def rewindService(self):
1237 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1239 # same as activateTimeshiftEnd, but pauses afterwards.
1240 def activateTimeshiftEndAndPause(self):
1241 print "activateTimeshiftEndAndPause"
1242 #state = self.seekstate
1243 self.activateTimeshiftEnd(False)
1245 def __seekableStatusChanged(self):
1248 # print "self.isSeekable", self.isSeekable()
1249 # print "self.timeshift_enabled", self.timeshift_enabled
1251 # when this service is not seekable, but timeshift
1252 # is enabled, this means we can activate
1254 if not self.isSeekable() and self.timeshift_enabled:
1257 # print "timeshift activate:", enabled
1258 self["TimeshiftActivateActions"].setEnabled(enabled)
1260 def __serviceStarted(self):
1261 self.timeshift_enabled = False
1262 self.__seekableStatusChanged()
1264 from Screens.PiPSetup import PiPSetup
1266 class InfoBarExtensions:
1267 EXTENSION_SINGLE = 0
1273 self["InstantExtensionsActions"] = HelpableActionMap(self, "InfobarExtensions",
1275 "extensions": (self.showExtensionSelection, _("view extensions...")),
1276 }, 1) # lower priority
1278 def addExtension(self, extension, key = None, type = EXTENSION_SINGLE):
1279 self.list.append((type, extension, key))
1281 def updateExtension(self, extension, key = None):
1282 self.extensionsList.append(extension)
1284 if self.extensionKeys.has_key(key):
1288 for x in self.availableKeys:
1289 if not self.extensionKeys.has_key(x):
1294 self.extensionKeys[key] = len(self.extensionsList) - 1
1296 def updateExtensions(self):
1297 self.extensionsList = []
1298 self.availableKeys = [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "red", "green", "yellow", "blue" ]
1299 self.extensionKeys = {}
1301 if x[0] == self.EXTENSION_SINGLE:
1302 self.updateExtension(x[1], x[2])
1305 self.updateExtension(y[0], y[1])
1308 def showExtensionSelection(self):
1309 self.updateExtensions()
1310 extensionsList = self.extensionsList[:]
1313 for x in self.availableKeys:
1314 if self.extensionKeys.has_key(x):
1315 entry = self.extensionKeys[x]
1316 extension = self.extensionsList[entry]
1318 name = str(extension[0]())
1319 list.append((extension[0](), extension))
1321 extensionsList.remove(extension)
1323 extensionsList.remove(extension)
1324 list.extend([(x[0](), x) for x in extensionsList])
1326 keys += [""] * len(extensionsList)
1327 self.session.openWithCallback(self.extensionCallback, ChoiceBox, title=_("Please choose an extension..."), list = list, keys = keys, skin_name = "ExtensionsList")
1329 def extensionCallback(self, answer):
1330 if answer is not None:
1333 from Tools.BoundFunction import boundFunction
1335 # depends on InfoBarExtensions
1337 class InfoBarPlugins:
1339 self.addExtension(extension = self.getPluginList, type = InfoBarExtensions.EXTENSION_LIST)
1341 def getPluginName(self, name):
1344 def getPluginList(self):
1345 list = [((boundFunction(self.getPluginName, p.name), boundFunction(self.runPlugin, p), lambda: True), None, p.name) for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EXTENSIONSMENU)]
1346 list.sort(key = lambda e: e[2]) # sort by name
1349 def runPlugin(self, plugin):
1350 if isinstance(self, InfoBarChannelSelection):
1351 plugin(session = self.session, servicelist = self.servicelist)
1353 plugin(session = self.session)
1355 from Components.Task import job_manager
1356 class InfoBarJobman:
1358 self.addExtension(extension = self.getJobList, type = InfoBarExtensions.EXTENSION_LIST)
1360 def getJobList(self):
1361 return [((boundFunction(self.getJobName, job), boundFunction(self.showJobView, job), lambda: True), None) for job in job_manager.getPendingJobs()]
1363 def getJobName(self, job):
1364 return "%s: %s (%d%%)" % (job.getStatustext(), job.name, int(100*job.progress/float(job.end)))
1366 def showJobView(self, job):
1367 from Screens.TaskView import JobView
1368 job_manager.in_background = False
1369 self.session.openWithCallback(self.JobViewCB, JobView, job)
1371 def JobViewCB(self, in_background):
1372 job_manager.in_background = in_background
1374 # depends on InfoBarExtensions
1378 self.session.pipshown
1380 self.session.pipshown = False
1381 if SystemInfo.get("NumVideoDecoders", 1) > 1:
1383 self.addExtension((self.getShowHideName, self.showPiP, lambda: True), "blue")
1384 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1385 self.addExtension((self.getSwapName, self.swapPiP, self.pipShown), "yellow")
1387 self.addExtension((self.getShowHideName, self.showPiP, self.pipShown), "blue")
1388 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1391 return self.session.pipshown
1393 def pipHandles0Action(self):
1394 return self.pipShown() and config.usage.pip_zero_button.value != "standard"
1396 def getShowHideName(self):
1397 if self.session.pipshown:
1398 return _("Disable Picture in Picture")
1400 return _("Activate Picture in Picture")
1402 def getSwapName(self):
1403 return _("Swap Services")
1405 def getMoveName(self):
1406 return _("Move Picture in Picture")
1409 if self.session.pipshown:
1410 del self.session.pip
1411 self.session.pipshown = False
1413 self.session.pip = self.session.instantiateDialog(PictureInPicture)
1414 self.session.pip.show()
1415 newservice = self.session.nav.getCurrentlyPlayingServiceReference()
1416 if self.session.pip.playService(newservice):
1417 self.session.pipshown = True
1418 self.session.pip.servicePath = self.servicelist.getCurrentServicePath()
1420 self.session.pipshown = False
1421 del self.session.pip
1422 self.session.nav.playService(newservice)
1425 swapservice = self.session.nav.getCurrentlyPlayingServiceReference()
1426 if self.session.pip.servicePath:
1427 servicepath = self.servicelist.getCurrentServicePath()
1428 ref=servicepath[len(servicepath)-1]
1429 pipref=self.session.pip.getCurrentService()
1430 self.session.pip.playService(swapservice)
1431 self.servicelist.setCurrentServicePath(self.session.pip.servicePath)
1432 if pipref.toString() != ref.toString(): # is a subservice ?
1433 self.session.nav.stopService() # stop portal
1434 self.session.nav.playService(pipref) # start subservice
1435 self.session.pip.servicePath=servicepath
1438 self.session.open(PiPSetup, pip = self.session.pip)
1440 def pipDoHandle0Action(self):
1441 use = config.usage.pip_zero_button.value
1444 elif "swapstop" == use:
1450 from RecordTimer import parseEvent, RecordTimerEntry
1452 class InfoBarInstantRecord:
1453 """Instant Record - handles the instantRecord action in order to
1454 start/stop instant records"""
1456 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
1458 "instantRecord": (self.instantRecord, _("Instant Record...")),
1462 def stopCurrentRecording(self, entry = -1):
1463 if entry is not None and entry != -1:
1464 self.session.nav.RecordTimer.removeEntry(self.recording[entry])
1465 self.recording.remove(self.recording[entry])
1467 def startInstantRecording(self, limitEvent = False):
1468 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
1470 # try to get event info
1473 service = self.session.nav.getCurrentService()
1474 epg = eEPGCache.getInstance()
1475 event = epg.lookupEventTime(serviceref, -1, 0)
1477 info = service.info()
1478 ev = info.getEvent(0)
1484 end = begin + 3600 # dummy
1485 name = "instant record"
1489 if event is not None:
1490 curEvent = parseEvent(event)
1492 description = curEvent[3]
1493 eventid = curEvent[4]
1498 self.session.open(MessageBox, _("No event info found, recording indefinitely."), MessageBox.TYPE_INFO)
1500 if isinstance(serviceref, eServiceReference):
1501 serviceref = ServiceReference(serviceref)
1503 recording = RecordTimerEntry(serviceref, begin, end, name, description, eventid, dirname = config.movielist.last_videodir.value)
1504 recording.dontSave = True
1506 if event is None or limitEvent == False:
1507 recording.autoincrease = True
1508 if recording.setAutoincreaseEnd():
1509 self.session.nav.RecordTimer.record(recording)
1510 self.recording.append(recording)
1512 simulTimerList = self.session.nav.RecordTimer.record(recording)
1513 if simulTimerList is not None: # conflict with other recording
1514 name = simulTimerList[1].name
1515 name_date = ' '.join((name, strftime('%c', localtime(simulTimerList[1].begin))))
1516 print "[TIMER] conflicts with", name_date
1517 recording.autoincrease = True # start with max available length, then increment
1518 if recording.setAutoincreaseEnd():
1519 self.session.nav.RecordTimer.record(recording)
1520 self.recording.append(recording)
1521 self.session.open(MessageBox, _("Record time limited due to conflicting timer %s") % name_date, MessageBox.TYPE_INFO)
1523 self.session.open(MessageBox, _("Couldn't record due to conflicting timer %s") % name, MessageBox.TYPE_INFO)
1524 recording.autoincrease = False
1526 self.recording.append(recording)
1528 def isInstantRecordRunning(self):
1529 print "self.recording:", self.recording
1531 for x in self.recording:
1536 def recordQuestionCallback(self, answer):
1537 print "pre:\n", self.recording
1539 if answer is None or answer[1] == "no":
1542 recording = self.recording[:]
1544 if not x in self.session.nav.RecordTimer.timer_list:
1545 self.recording.remove(x)
1546 elif x.dontSave and x.isRunning():
1547 list.append((x, False))
1549 if answer[1] == "changeduration":
1550 if len(self.recording) == 1:
1551 self.changeDuration(0)
1553 self.session.openWithCallback(self.changeDuration, TimerSelection, list)
1554 elif answer[1] == "changeendtime":
1555 if len(self.recording) == 1:
1558 self.session.openWithCallback(self.setEndtime, TimerSelection, list)
1559 elif answer[1] == "stop":
1560 if len(self.recording) == 1:
1561 self.stopCurrentRecording(0)
1563 self.session.openWithCallback(self.stopCurrentRecording, TimerSelection, list)
1564 elif answer[1] in ( "indefinitely" , "manualduration", "manualendtime", "event"):
1565 self.startInstantRecording(limitEvent = answer[1] in ("event", "manualendtime") or False)
1566 if answer[1] == "manualduration":
1567 self.changeDuration(len(self.recording)-1)
1568 elif answer[1] == "manualendtime":
1569 self.setEndtime(len(self.recording)-1)
1570 print "after:\n", self.recording
1572 def setEndtime(self, entry):
1573 if entry is not None and entry >= 0:
1574 self.selectedEntry = entry
1575 self.endtime=ConfigClock(default = self.recording[self.selectedEntry].end)
1576 dlg = self.session.openWithCallback(self.TimeDateInputClosed, TimeDateInput, self.endtime)
1577 dlg.setTitle(_("Please change recording endtime"))
1579 def TimeDateInputClosed(self, ret):
1582 localendtime = localtime(ret[1])
1583 print "stopping recording at", strftime("%c", localendtime)
1584 if self.recording[self.selectedEntry].end != ret[1]:
1585 self.recording[self.selectedEntry].autoincrease = False
1586 self.recording[self.selectedEntry].end = ret[1]
1587 self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
1589 def changeDuration(self, entry):
1590 if entry is not None and entry >= 0:
1591 self.selectedEntry = entry
1592 self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1594 def inputCallback(self, value):
1595 if value is not None:
1596 print "stopping recording after", int(value), "minutes."
1597 entry = self.recording[self.selectedEntry]
1599 entry.autoincrease = False
1600 entry.end = int(time()) + 60 * int(value)
1601 self.session.nav.RecordTimer.timeChanged(entry)
1603 def instantRecord(self):
1604 dir = config.movielist.last_videodir.value
1605 if not fileExists(dir, 'w'):
1606 dir = resolveFilename(SCOPE_HDD)
1610 # XXX: this message is a little odd as we might be recording to a remote device
1611 self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1614 if self.isInstantRecordRunning():
1615 self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1616 title=_("A recording is currently running.\nWhat do you want to do?"), \
1617 list=((_("stop recording"), "stop"), \
1618 (_("add recording (stop after current event)"), "event"), \
1619 (_("add recording (indefinitely)"), "indefinitely"), \
1620 (_("add recording (enter recording duration)"), "manualduration"), \
1621 (_("add recording (enter recording endtime)"), "manualendtime"), \
1622 (_("change recording (duration)"), "changeduration"), \
1623 (_("change recording (endtime)"), "changeendtime"), \
1624 (_("do nothing"), "no")))
1626 self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1627 title=_("Start recording?"), \
1628 list=((_("add recording (stop after current event)"), "event"), \
1629 (_("add recording (indefinitely)"), "indefinitely"), \
1630 (_("add recording (enter recording duration)"), "manualduration"), \
1631 (_("add recording (enter recording endtime)"), "manualendtime"), \
1632 (_("don't record"), "no")))
1634 from Tools.ISO639 import LanguageCodes
1636 class InfoBarAudioSelection:
1638 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions",
1640 "audioSelection": (self.audioSelection, _("Audio Options...")),
1643 def audioSelection(self):
1644 service = self.session.nav.getCurrentService()
1645 self.audioTracks = audio = service and service.audioTracks()
1646 n = audio and audio.getNumberOfTracks() or 0
1649 self.audioChannel = service.audioChannel()
1654 i = audio.getTrackInfo(idx)
1655 languages = i.getLanguage().split('/')
1656 description = i.getDescription()
1659 for lang in languages:
1662 if LanguageCodes.has_key(lang):
1663 language += LanguageCodes[lang][0]
1668 if len(description):
1669 description += " (" + language + ")"
1671 description = language
1673 tlist.append((description, idx))
1676 tlist.sort(key=lambda x: x[0])
1678 selectedAudio = self.audioTracks.getCurrentTrack()
1683 if x[1] != selectedAudio:
1688 if SystemInfo["CanDownmixAC3"]:
1689 tlist = [(_("AC3 downmix") + " - " +(_("Off"), _("On"))[config.av.downmix_ac3.value and 1 or 0], "CALLFUNC", self.changeAC3Downmix),
1690 ((_("Left"), _("Stereo"), _("Right"))[self.audioChannel.getCurrentChannel()], "mode"),
1692 keys = [ "red", "green", "", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"] + [""]*n
1695 tlist = [((_("Left"), _("Stereo"), _("Right"))[self.audioChannel.getCurrentChannel()], "mode"), ("--", "")] + tlist
1696 keys = [ "red", "", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"] + [""]*n
1698 self.session.openWithCallback(self.audioSelected, ChoiceBox, title=_("Select audio track"), list = tlist, selection = selection, keys = keys, skin_name = "AudioTrackSelection")
1700 del self.audioTracks
1702 def changeAC3Downmix(self, arg):
1703 choicelist = self.session.current_dialog["list"]
1704 list = choicelist.list
1706 list[0][1]=(t[0], t[1], t[2], t[3], t[4], t[5], t[6],
1707 _("AC3 downmix") + " - " + (_("On"), _("Off"))[config.av.downmix_ac3.value and 1 or 0])
1708 choicelist.setList(list)
1709 if config.av.downmix_ac3.value:
1710 config.av.downmix_ac3.value = False
1712 config.av.downmix_ac3.value = True
1713 config.av.downmix_ac3.save()
1715 def audioSelected(self, audio):
1716 if audio is not None:
1717 if isinstance(audio[1], str):
1718 if audio[1] == "mode":
1719 keys = ["red", "green", "yellow"]
1720 selection = self.audioChannel.getCurrentChannel()
1721 tlist = ((_("left"), 0), (_("stereo"), 1), (_("right"), 2))
1722 self.session.openWithCallback(self.modeSelected, ChoiceBox, title=_("Select audio mode"), list = tlist, selection = selection, keys = keys, skin_name ="AudioModeSelection")
1724 del self.audioChannel
1725 if self.session.nav.getCurrentService().audioTracks().getNumberOfTracks() > audio[1]:
1726 self.audioTracks.selectTrack(audio[1])
1728 del self.audioChannel
1729 del self.audioTracks
1731 def modeSelected(self, mode):
1732 if mode is not None:
1733 self.audioChannel.selectChannel(mode[1])
1734 del self.audioChannel
1736 class InfoBarSubserviceSelection:
1738 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1740 "subserviceSelection": (self.subserviceSelection, _("Subservice list...")),
1743 self["SubserviceQuickzapAction"] = HelpableActionMap(self, "InfobarSubserviceQuickzapActions",
1745 "nextSubservice": (self.nextSubservice, _("Switch to next subservice")),
1746 "prevSubservice": (self.prevSubservice, _("Switch to previous subservice"))
1748 self["SubserviceQuickzapAction"].setEnabled(False)
1750 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1752 iPlayableService.evUpdatedEventInfo: self.checkSubservicesAvail
1757 def checkSubservicesAvail(self):
1758 service = self.session.nav.getCurrentService()
1759 subservices = service and service.subServices()
1760 if not subservices or subservices.getNumberOfSubservices() == 0:
1761 self["SubserviceQuickzapAction"].setEnabled(False)
1763 def nextSubservice(self):
1764 self.changeSubservice(+1)
1766 def prevSubservice(self):
1767 self.changeSubservice(-1)
1769 def changeSubservice(self, direction):
1770 service = self.session.nav.getCurrentService()
1771 subservices = service and service.subServices()
1772 n = subservices and subservices.getNumberOfSubservices()
1775 ref = self.session.nav.getCurrentlyPlayingServiceReference()
1778 if subservices.getSubservice(idx).toString() == ref.toString():
1783 selection += direction
1788 newservice = subservices.getSubservice(selection)
1789 if newservice.valid():
1792 self.session.nav.playService(newservice, False)
1794 def subserviceSelection(self):
1795 service = self.session.nav.getCurrentService()
1796 subservices = service and service.subServices()
1797 self.bouquets = self.servicelist.getBouquetList()
1798 n = subservices and subservices.getNumberOfSubservices()
1801 ref = self.session.nav.getCurrentlyPlayingServiceReference()
1805 i = subservices.getSubservice(idx)
1806 if i.toString() == ref.toString():
1808 tlist.append((i.getName(), i))
1811 if self.bouquets and len(self.bouquets):
1812 keys = ["red", "blue", "", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1813 if config.usage.multibouquet.value:
1814 tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to bouquet"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1816 tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to favourites"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1819 tlist = [(_("Quickzap"), "quickzap", service.subServices()), ("--", "")] + tlist
1820 keys = ["red", "", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1823 self.session.openWithCallback(self.subserviceSelected, ChoiceBox, title=_("Please select a subservice..."), list = tlist, selection = selection, keys = keys, skin_name = "SubserviceSelection")
1825 def subserviceSelected(self, service):
1827 if not service is None:
1828 if isinstance(service[1], str):
1829 if service[1] == "quickzap":
1830 from Screens.SubservicesQuickzap import SubservicesQuickzap
1831 self.session.open(SubservicesQuickzap, service[2])
1833 self["SubserviceQuickzapAction"].setEnabled(True)
1834 self.session.nav.playService(service[1], False)
1836 def addSubserviceToBouquetCallback(self, service):
1837 if len(service) > 1 and isinstance(service[1], eServiceReference):
1838 self.selectedSubservice = service
1839 if self.bouquets is None:
1842 cnt = len(self.bouquets)
1843 if cnt > 1: # show bouquet list
1844 self.bsel = self.session.openWithCallback(self.bouquetSelClosed, BouquetSelector, self.bouquets, self.addSubserviceToBouquet)
1845 elif cnt == 1: # add to only one existing bouquet
1846 self.addSubserviceToBouquet(self.bouquets[0][1])
1847 self.session.open(MessageBox, _("Service has been added to the favourites."), MessageBox.TYPE_INFO)
1849 def bouquetSelClosed(self, confirmed):
1851 del self.selectedSubservice
1853 self.session.open(MessageBox, _("Service has been added to the selected bouquet."), MessageBox.TYPE_INFO)
1855 def addSubserviceToBouquet(self, dest):
1856 self.servicelist.addServiceToBouquet(dest, self.selectedSubservice[1])
1858 self.bsel.close(True)
1860 del self.selectedSubservice
1862 class InfoBarAdditionalInfo:
1865 self["RecordingPossible"] = Boolean(fixed=harddiskmanager.HDDCount() > 0 and config.misc.rcused.value == 1)
1866 self["TimeshiftPossible"] = self["RecordingPossible"]
1867 self["ShowTimeshiftOnYellow"] = Boolean(fixed=(not config.misc.rcused.value == 0))
1868 self["ShowAudioOnYellow"] = Boolean(fixed=config.misc.rcused.value == 0)
1869 self["ShowRecordOnRed"] = Boolean(fixed=config.misc.rcused.value == 1)
1870 self["ExtensionsAvailable"] = Boolean(fixed=1)
1872 class InfoBarNotifications:
1874 self.onExecBegin.append(self.checkNotifications)
1875 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1876 self.onClose.append(self.__removeNotification)
1878 def __removeNotification(self):
1879 Notifications.notificationAdded.remove(self.checkNotificationsIfExecing)
1881 def checkNotificationsIfExecing(self):
1883 self.checkNotifications()
1885 def checkNotifications(self):
1886 notifications = Notifications.notifications
1888 n = notifications[0]
1890 del notifications[0]
1893 if n[3].has_key("onSessionOpenCallback"):
1894 n[3]["onSessionOpenCallback"]()
1895 del n[3]["onSessionOpenCallback"]
1898 dlg = self.session.openWithCallback(cb, n[1], *n[2], **n[3])
1900 dlg = self.session.open(n[1], *n[2], **n[3])
1902 # remember that this notification is currently active
1904 Notifications.current_notifications.append(d)
1905 dlg.onClose.append(boundFunction(self.__notificationClosed, d))
1907 def __notificationClosed(self, d):
1908 Notifications.current_notifications.remove(d)
1910 class InfoBarServiceNotifications:
1912 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1914 iPlayableService.evEnd: self.serviceHasEnded
1917 def serviceHasEnded(self):
1918 print "service end!"
1921 self.setSeekState(self.SEEK_STATE_PLAY)
1925 class InfoBarCueSheetSupport:
1931 ENABLE_RESUME_SUPPORT = False
1933 def __init__(self, actionmap = "InfobarCueSheetActions"):
1934 self["CueSheetActions"] = HelpableActionMap(self, actionmap,
1936 "jumpPreviousMark": (self.jumpPreviousMark, _("jump to previous marked position")),
1937 "jumpNextMark": (self.jumpNextMark, _("jump to next marked position")),
1938 "toggleMark": (self.toggleMark, _("toggle a cut mark at the current position"))
1942 self.is_closing = False
1943 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1945 iPlayableService.evStart: self.__serviceStarted,
1948 def __serviceStarted(self):
1951 print "new service started! trying to download cuts!"
1952 self.downloadCuesheet()
1954 if self.ENABLE_RESUME_SUPPORT:
1957 for (pts, what) in self.cut_list:
1958 if what == self.CUT_TYPE_LAST:
1961 if last is not None:
1962 self.resume_point = last
1963 if config.usage.on_movie_start.value == "ask":
1964 Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Do you want to resume this playback?"), timeout=10)
1965 elif config.usage.on_movie_start.value == "resume":
1966 # TRANSLATORS: The string "Resuming playback" flashes for a moment
1967 # TRANSLATORS: at the start of a movie, when the user has selected
1968 # TRANSLATORS: "Resume from last position" as start behavior.
1969 # TRANSLATORS: The purpose is to notify the user that the movie starts
1970 # TRANSLATORS: in the middle somewhere and not from the beginning.
1971 # TRANSLATORS: (Some translators seem to have interpreted it as a
1972 # TRANSLATORS: question or a choice, but it is a statement.)
1973 Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Resuming playback"), timeout=2, type=MessageBox.TYPE_INFO)
1975 def playLastCB(self, answer):
1977 self.doSeek(self.resume_point)
1978 self.hideAfterResume()
1980 def hideAfterResume(self):
1981 if isinstance(self, InfoBarShowHide):
1984 def __getSeekable(self):
1985 service = self.session.nav.getCurrentService()
1988 return service.seek()
1990 def cueGetCurrentPosition(self):
1991 seek = self.__getSeekable()
1994 r = seek.getPlayPosition()
1999 def cueGetEndCutPosition(self):
2002 for cp in self.cut_list:
2003 if cp[1] == self.CUT_TYPE_OUT:
2007 elif cp[1] == self.CUT_TYPE_IN:
2011 def jumpPreviousNextMark(self, cmp, start=False):
2012 current_pos = self.cueGetCurrentPosition()
2013 if current_pos is None:
2015 mark = self.getNearestCutPoint(current_pos, cmp=cmp, start=start)
2016 if mark is not None:
2024 def jumpPreviousMark(self):
2025 # we add 2 seconds, so if the play position is <2s after
2026 # the mark, the mark before will be used
2027 self.jumpPreviousNextMark(lambda x: -x-5*90000, start=True)
2029 def jumpNextMark(self):
2030 if not self.jumpPreviousNextMark(lambda x: x):
2033 def getNearestCutPoint(self, pts, cmp=abs, start=False):
2039 bestdiff = cmp(0 - pts)
2041 nearest = [0, False]
2042 for cp in self.cut_list:
2043 if beforecut and cp[1] in (self.CUT_TYPE_IN, self.CUT_TYPE_OUT):
2045 if cp[1] == self.CUT_TYPE_IN: # Start is here, disregard previous marks
2046 diff = cmp(cp[0] - pts)
2052 if cp[1] in (self.CUT_TYPE_MARK, self.CUT_TYPE_LAST):
2053 diff = cmp(cp[0] - pts)
2054 if diff >= 0 and (nearest is None or bestdiff > diff):
2059 def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
2060 current_pos = self.cueGetCurrentPosition()
2061 if current_pos is None:
2062 print "not seekable"
2065 nearest_cutpoint = self.getNearestCutPoint(current_pos)
2067 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
2069 return nearest_cutpoint
2071 self.removeMark(nearest_cutpoint)
2072 elif not onlyremove and not onlyreturn:
2073 self.addMark((current_pos, self.CUT_TYPE_MARK))
2078 def addMark(self, point):
2079 insort(self.cut_list, point)
2080 self.uploadCuesheet()
2081 self.showAfterCuesheetOperation()
2083 def removeMark(self, point):
2084 self.cut_list.remove(point)
2085 self.uploadCuesheet()
2086 self.showAfterCuesheetOperation()
2088 def showAfterCuesheetOperation(self):
2089 if isinstance(self, InfoBarShowHide):
2092 def __getCuesheet(self):
2093 service = self.session.nav.getCurrentService()
2096 return service.cueSheet()
2098 def uploadCuesheet(self):
2099 cue = self.__getCuesheet()
2102 print "upload failed, no cuesheet interface"
2104 cue.setCutList(self.cut_list)
2106 def downloadCuesheet(self):
2107 cue = self.__getCuesheet()
2110 print "download failed, no cuesheet interface"
2113 self.cut_list = cue.getCutList()
2115 class InfoBarSummary(Screen):
2117 <screen position="0,0" size="132,64">
2118 <widget source="global.CurrentTime" render="Label" position="62,46" size="82,18" font="Regular;16" >
2119 <convert type="ClockToText">WithSeconds</convert>
2121 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="82,18" zPosition="1" >
2122 <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2123 <convert type="ConditionalShowHide">Blink</convert>
2125 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2126 <convert type="ServiceName">Name</convert>
2128 <widget source="session.Event_Now" render="Progress" position="6,46" size="46,18" borderWidth="1" >
2129 <convert type="EventTime">Progress</convert>
2133 # for picon: (path="piconlcd" will use LCD picons)
2134 # <widget source="session.CurrentService" render="Picon" position="6,0" size="120,64" path="piconlcd" >
2135 # <convert type="ServiceName">Reference</convert>
2138 class InfoBarSummarySupport:
2142 def createSummary(self):
2143 return InfoBarSummary
2145 class InfoBarMoviePlayerSummary(Screen):
2147 <screen position="0,0" size="132,64">
2148 <widget source="global.CurrentTime" render="Label" position="62,46" size="64,18" font="Regular;16" halign="right" >
2149 <convert type="ClockToText">WithSeconds</convert>
2151 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="64,18" zPosition="1" >
2152 <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2153 <convert type="ConditionalShowHide">Blink</convert>
2155 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2156 <convert type="ServiceName">Name</convert>
2158 <widget source="session.CurrentService" render="Progress" position="6,46" size="56,18" borderWidth="1" >
2159 <convert type="ServicePosition">Position</convert>
2163 class InfoBarMoviePlayerSummarySupport:
2167 def createSummary(self):
2168 return InfoBarMoviePlayerSummary
2170 class InfoBarTeletextPlugin:
2172 self.teletext_plugin = None
2174 for p in plugins.getPlugins(PluginDescriptor.WHERE_TELETEXT):
2175 self.teletext_plugin = p
2177 if self.teletext_plugin is not None:
2178 self["TeletextActions"] = HelpableActionMap(self, "InfobarTeletextActions",
2180 "startTeletext": (self.startTeletext, _("View teletext..."))
2183 print "no teletext plugin found!"
2185 def startTeletext(self):
2186 self.teletext_plugin(session=self.session, service=self.session.nav.getCurrentService())
2188 class InfoBarSubtitleSupport(object):
2190 object.__init__(self)
2191 self.subtitle_window = self.session.instantiateDialog(SubtitleDisplay)
2192 self.__subtitles_enabled = False
2194 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2196 iPlayableService.evEnd: self.__serviceStopped,
2197 iPlayableService.evUpdatedInfo: self.__updatedInfo
2199 self.cached_subtitle_checked = False
2200 self.__selected_subtitle = None
2202 def __serviceStopped(self):
2203 self.cached_subtitle_checked = False
2204 if self.__subtitles_enabled:
2205 self.subtitle_window.hide()
2206 self.__subtitles_enabled = False
2207 self.__selected_subtitle = None
2209 def __updatedInfo(self):
2210 if not self.cached_subtitle_checked:
2211 self.cached_subtitle_checked = True
2212 subtitle = self.getCurrentServiceSubtitle()
2213 self.setSelectedSubtitle(subtitle and subtitle.getCachedSubtitle())
2214 if self.__selected_subtitle:
2215 self.setSubtitlesEnable(True)
2217 def getCurrentServiceSubtitle(self):
2218 service = self.session.nav.getCurrentService()
2219 return service and service.subtitle()
2221 def setSubtitlesEnable(self, enable=True):
2222 subtitle = self.getCurrentServiceSubtitle()
2224 if self.__selected_subtitle:
2225 if subtitle and not self.__subtitles_enabled:
2226 subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
2227 self.subtitle_window.show()
2228 self.__subtitles_enabled = True
2231 subtitle.disableSubtitles(self.subtitle_window.instance)
2232 self.__selected_subtitle = False
2233 self.__subtitles_enabled = False
2234 self.subtitle_window.hide()
2236 def setSelectedSubtitle(self, subtitle):
2237 self.__selected_subtitle = subtitle
2239 subtitles_enabled = property(lambda self: self.__subtitles_enabled, setSubtitlesEnable)
2240 selected_subtitle = property(lambda self: self.__selected_subtitle, setSelectedSubtitle)
2242 class InfoBarServiceErrorPopupSupport:
2244 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2246 iPlayableService.evTuneFailed: self.__tuneFailed,
2247 iPlayableService.evStart: self.__serviceStarted
2249 self.__serviceStarted()
2251 def __serviceStarted(self):
2252 self.last_error = None
2253 Notifications.RemovePopup(id = "ZapError")
2255 def __tuneFailed(self):
2256 service = self.session.nav.getCurrentService()
2257 info = service and service.info()
2258 error = info and info.getInfo(iServiceInformation.sDVBState)
2260 if error == self.last_error:
2263 self.last_error = error
2266 eDVBServicePMTHandler.eventNoResources: _("No free tuner!"),
2267 eDVBServicePMTHandler.eventTuneFailed: _("Tune failed!"),
2268 eDVBServicePMTHandler.eventNoPAT: _("No data on transponder!\n(Timeout reading PAT)"),
2269 eDVBServicePMTHandler.eventNoPATEntry: _("Service not found!\n(SID not found in PAT)"),
2270 eDVBServicePMTHandler.eventNoPMT: _("Service invalid!\n(Timeout reading PMT)"),
2271 eDVBServicePMTHandler.eventNewProgramInfo: None,
2272 eDVBServicePMTHandler.eventTuned: None,
2273 eDVBServicePMTHandler.eventSOF: None,
2274 eDVBServicePMTHandler.eventEOF: None,
2275 eDVBServicePMTHandler.eventMisconfiguration: _("Service unavailable!\nCheck tuner configuration!"),
2276 }.get(error) #this returns None when the key not exist in the dict
2278 if error is not None:
2279 Notifications.AddPopup(text = error, type = MessageBox.TYPE_ERROR, timeout = 5, id = "ZapError")
2281 Notifications.RemovePopup(id = "ZapError")