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 if self.seekstate == self.SEEK_STATE_PLAY:
925 self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
926 elif self.seekstate == self.SEEK_STATE_PAUSE:
927 if len(config.seek.speeds_slowmotion.value):
928 self.setSeekState(self.makeStateSlowMotion(config.seek.speeds_slowmotion.value[-1]))
930 self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
931 elif self.seekstate == self.SEEK_STATE_EOF:
933 elif self.isStateForward(self.seekstate):
934 speed = self.seekstate[1]
935 if self.seekstate[2]:
936 speed /= self.seekstate[2]
937 speed = self.getHigher(speed, config.seek.speeds_forward.value) or config.seek.speeds_forward.value[-1]
938 self.setSeekState(self.makeStateForward(speed))
939 elif self.isStateBackward(self.seekstate):
940 speed = -self.seekstate[1]
941 if self.seekstate[2]:
942 speed /= self.seekstate[2]
943 speed = self.getLower(speed, config.seek.speeds_backward.value)
945 self.setSeekState(self.makeStateBackward(speed))
947 self.setSeekState(self.SEEK_STATE_PLAY)
948 elif self.isStateSlowMotion(self.seekstate):
949 speed = self.getLower(self.seekstate[2], config.seek.speeds_slowmotion.value) or config.seek.speeds_slowmotion.value[0]
950 self.setSeekState(self.makeStateSlowMotion(speed))
953 seek = self.getSeek()
954 if seek and not (seek.isCurrentlySeekable() & 2):
955 if not self.fast_winding_hint_message_showed and (seek.isCurrentlySeekable() & 1):
956 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)
957 self.fast_winding_hint_message_showed = True
959 seekstate = self.seekstate
960 if seekstate == self.SEEK_STATE_PLAY:
961 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
962 elif seekstate == self.SEEK_STATE_EOF:
963 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
964 self.doSeekRelative(-6)
965 elif seekstate == self.SEEK_STATE_PAUSE:
966 self.doSeekRelative(-3)
967 elif self.isStateForward(seekstate):
970 speed /= seekstate[2]
971 speed = self.getLower(speed, config.seek.speeds_forward.value)
973 self.setSeekState(self.makeStateForward(speed))
975 self.setSeekState(self.SEEK_STATE_PLAY)
976 elif self.isStateBackward(seekstate):
977 speed = -seekstate[1]
979 speed /= seekstate[2]
980 speed = self.getHigher(speed, config.seek.speeds_backward.value) or config.seek.speeds_backward.value[-1]
981 self.setSeekState(self.makeStateBackward(speed))
982 elif self.isStateSlowMotion(seekstate):
983 speed = self.getHigher(seekstate[2], config.seek.speeds_slowmotion.value)
985 self.setSeekState(self.makeStateSlowMotion(speed))
987 self.setSeekState(self.SEEK_STATE_PAUSE)
989 def seekFwdManual(self):
990 self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
992 def fwdSeekTo(self, minutes):
993 print "Seek", minutes, "minutes forward"
994 self.doSeekRelative(minutes * 60 * 90000)
996 def seekBackManual(self):
997 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
999 def rwdSeekTo(self, minutes):
1001 self.doSeekRelative(-minutes * 60 * 90000)
1003 def checkSkipShowHideLock(self):
1004 wantlock = self.seekstate != self.SEEK_STATE_PLAY
1006 if config.usage.show_infobar_on_skip.value:
1007 if self.lockedBecauseOfSkipping and not wantlock:
1009 self.lockedBecauseOfSkipping = False
1011 if wantlock and not self.lockedBecauseOfSkipping:
1013 self.lockedBecauseOfSkipping = True
1015 def calcRemainingTime(self):
1016 seekable = self.getSeek()
1017 if seekable is not None:
1018 len = seekable.getLength()
1020 tmp = self.cueGetEndCutPosition()
1025 pos = seekable.getPlayPosition()
1026 speednom = self.seekstate[1] or 1
1027 speedden = self.seekstate[2] or 1
1028 if not len[0] and not pos[0]:
1029 if len[1] <= pos[1]:
1031 time = (len[1] - pos[1])*speedden/(90*speednom)
1036 if self.seekstate == self.SEEK_STATE_EOF:
1039 # if we are seeking forward, we try to end up ~1s before the end, and pause there.
1040 seekstate = self.seekstate
1041 if self.seekstate != self.SEEK_STATE_PAUSE:
1042 self.setSeekState(self.SEEK_STATE_EOF)
1044 if seekstate not in (self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE): # if we are seeking
1045 seekable = self.getSeek()
1046 if seekable is not None:
1048 if seekstate == self.SEEK_STATE_PLAY: # regular EOF
1049 self.doEofInternal(True)
1051 self.doEofInternal(False)
1053 def doEofInternal(self, playing):
1054 pass # Defined in subclasses
1057 self.setSeekState(self.SEEK_STATE_PLAY)
1060 from Screens.PVRState import PVRState, TimeshiftState
1062 class InfoBarPVRState:
1063 def __init__(self, screen=PVRState, force_show = False):
1064 self.onPlayStateChanged.append(self.__playStateChanged)
1065 self.pvrStateDialog = self.session.instantiateDialog(screen)
1066 self.onShow.append(self._mayShow)
1067 self.onHide.append(self.pvrStateDialog.hide)
1068 self.force_show = force_show
1071 if self.execing and self.seekstate != self.SEEK_STATE_PLAY:
1072 self.pvrStateDialog.show()
1074 def __playStateChanged(self, state):
1075 playstateString = state[3]
1076 self.pvrStateDialog["state"].setText(playstateString)
1078 # if we return into "PLAY" state, ensure that the dialog gets hidden if there will be no infobar displayed
1079 if not config.usage.show_infobar_on_skip.value and self.seekstate == self.SEEK_STATE_PLAY and not self.force_show:
1080 self.pvrStateDialog.hide()
1085 class InfoBarTimeshiftState(InfoBarPVRState):
1087 InfoBarPVRState.__init__(self, screen=TimeshiftState, force_show = True)
1090 if self.execing and self.timeshift_enabled and self.seekstate != self.SEEK_STATE_PLAY:
1091 self.pvrStateDialog.show()
1093 class InfoBarShowMovies:
1095 # i don't really like this class.
1096 # it calls a not further specified "movie list" on up/down/movieList,
1097 # so this is not more than an action map
1099 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions",
1101 "movieList": (self.showMovies, _("movie list")),
1102 "up": (self.showMovies, _("movie list")),
1103 "down": (self.showMovies, _("movie list"))
1106 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
1110 # Timeshift works the following way:
1111 # demux0 demux1 "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
1112 # - normal playback TUNER unused PLAY enable disable disable
1113 # - user presses "yellow" button. FILE record PAUSE enable disable enable
1114 # - user presess pause again FILE record PLAY enable disable enable
1115 # - user fast forwards FILE record FF enable disable enable
1116 # - end of timeshift buffer reached TUNER record PLAY enable enable disable
1117 # - user backwards FILE record BACK # !! enable disable enable
1121 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
1122 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
1123 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
1124 # - the user can now PVR around
1125 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
1126 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
1128 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
1129 # - if the user rewinds, or press pause, timeshift will be activated again
1131 # note that a timeshift can be enabled ("recording") and
1132 # activated (currently time-shifting).
1134 class InfoBarTimeshift:
1136 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions",
1138 "timeshiftStart": (self.startTimeshift, _("start timeshift")), # the "yellow key"
1139 "timeshiftStop": (self.stopTimeshift, _("stop timeshift")) # currently undefined :), probably 'TV'
1141 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
1143 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "rewind key"
1144 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause # something like "pause key"
1145 }, prio=-1) # priority over record
1147 self.timeshift_enabled = 0
1148 self.timeshift_state = 0
1149 self.ts_rewind_timer = eTimer()
1150 self.ts_rewind_timer.callback.append(self.rewindService)
1152 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1154 iPlayableService.evStart: self.__serviceStarted,
1155 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged
1158 def getTimeshift(self):
1159 service = self.session.nav.getCurrentService()
1160 return service and service.timeshift()
1162 def startTimeshift(self):
1163 print "enable timeshift"
1164 ts = self.getTimeshift()
1166 self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
1167 print "no ts interface"
1170 if self.timeshift_enabled:
1171 print "hu, timeshift already enabled?"
1173 if not ts.startTimeshift():
1174 self.timeshift_enabled = 1
1176 # we remove the "relative time" for now.
1177 #self.pvrStateDialog["timeshift"].setRelative(time.time())
1180 #self.setSeekState(self.SEEK_STATE_PAUSE)
1181 self.activateTimeshiftEnd(False)
1183 # enable the "TimeshiftEnableActions", which will override
1184 # the startTimeshift actions
1185 self.__seekableStatusChanged()
1187 print "timeshift failed"
1189 def stopTimeshift(self):
1190 if not self.timeshift_enabled:
1192 print "disable timeshift"
1193 ts = self.getTimeshift()
1196 self.session.openWithCallback(self.stopTimeshiftConfirmed, MessageBox, _("Stop Timeshift?"), MessageBox.TYPE_YESNO)
1198 def stopTimeshiftConfirmed(self, confirmed):
1202 ts = self.getTimeshift()
1207 self.timeshift_enabled = 0
1210 self.__seekableStatusChanged()
1212 # activates timeshift, and seeks to (almost) the end
1213 def activateTimeshiftEnd(self, back = True):
1214 ts = self.getTimeshift()
1215 print "activateTimeshiftEnd"
1220 if ts.isTimeshiftActive():
1221 print "!! activate timeshift called - but shouldn't this be a normal pause?"
1225 ts.activateTimeshift() # activate timeshift will automatically pause
1226 self.setSeekState(self.SEEK_STATE_PAUSE)
1229 self.doSeek(-5) # seek some gops before end
1230 self.ts_rewind_timer.start(200, 1)
1232 self.doSeek(-1) # seek 1 gop before end
1234 def rewindService(self):
1235 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1237 # same as activateTimeshiftEnd, but pauses afterwards.
1238 def activateTimeshiftEndAndPause(self):
1239 print "activateTimeshiftEndAndPause"
1240 #state = self.seekstate
1241 self.activateTimeshiftEnd(False)
1243 def __seekableStatusChanged(self):
1246 # print "self.isSeekable", self.isSeekable()
1247 # print "self.timeshift_enabled", self.timeshift_enabled
1249 # when this service is not seekable, but timeshift
1250 # is enabled, this means we can activate
1252 if not self.isSeekable() and self.timeshift_enabled:
1255 # print "timeshift activate:", enabled
1256 self["TimeshiftActivateActions"].setEnabled(enabled)
1258 def __serviceStarted(self):
1259 self.timeshift_enabled = False
1260 self.__seekableStatusChanged()
1262 from Screens.PiPSetup import PiPSetup
1264 class InfoBarExtensions:
1265 EXTENSION_SINGLE = 0
1271 self["InstantExtensionsActions"] = HelpableActionMap(self, "InfobarExtensions",
1273 "extensions": (self.showExtensionSelection, _("view extensions...")),
1274 }, 1) # lower priority
1276 def addExtension(self, extension, key = None, type = EXTENSION_SINGLE):
1277 self.list.append((type, extension, key))
1279 def updateExtension(self, extension, key = None):
1280 self.extensionsList.append(extension)
1282 if self.extensionKeys.has_key(key):
1286 for x in self.availableKeys:
1287 if not self.extensionKeys.has_key(x):
1292 self.extensionKeys[key] = len(self.extensionsList) - 1
1294 def updateExtensions(self):
1295 self.extensionsList = []
1296 self.availableKeys = [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "red", "green", "yellow", "blue" ]
1297 self.extensionKeys = {}
1299 if x[0] == self.EXTENSION_SINGLE:
1300 self.updateExtension(x[1], x[2])
1303 self.updateExtension(y[0], y[1])
1306 def showExtensionSelection(self):
1307 self.updateExtensions()
1308 extensionsList = self.extensionsList[:]
1311 for x in self.availableKeys:
1312 if self.extensionKeys.has_key(x):
1313 entry = self.extensionKeys[x]
1314 extension = self.extensionsList[entry]
1316 name = str(extension[0]())
1317 list.append((extension[0](), extension))
1319 extensionsList.remove(extension)
1321 extensionsList.remove(extension)
1322 list.extend([(x[0](), x) for x in extensionsList])
1324 keys += [""] * len(extensionsList)
1325 self.session.openWithCallback(self.extensionCallback, ChoiceBox, title=_("Please choose an extension..."), list = list, keys = keys, skin_name = "ExtensionsList")
1327 def extensionCallback(self, answer):
1328 if answer is not None:
1331 from Tools.BoundFunction import boundFunction
1333 # depends on InfoBarExtensions
1335 class InfoBarPlugins:
1337 self.addExtension(extension = self.getPluginList, type = InfoBarExtensions.EXTENSION_LIST)
1339 def getPluginName(self, name):
1342 def getPluginList(self):
1343 list = [((boundFunction(self.getPluginName, p.name), boundFunction(self.runPlugin, p), lambda: True), None, p.name) for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EXTENSIONSMENU)]
1344 list.sort(key = lambda e: e[2]) # sort by name
1347 def runPlugin(self, plugin):
1348 if isinstance(self, InfoBarChannelSelection):
1349 plugin(session = self.session, servicelist = self.servicelist)
1351 plugin(session = self.session)
1353 from Components.Task import job_manager
1354 class InfoBarJobman:
1356 self.addExtension(extension = self.getJobList, type = InfoBarExtensions.EXTENSION_LIST)
1358 def getJobList(self):
1359 return [((boundFunction(self.getJobName, job), boundFunction(self.showJobView, job), lambda: True), None) for job in job_manager.getPendingJobs()]
1361 def getJobName(self, job):
1362 return "%s: %s (%d%%)" % (job.getStatustext(), job.name, int(100*job.progress/float(job.end)))
1364 def showJobView(self, job):
1365 from Screens.TaskView import JobView
1366 job_manager.in_background = False
1367 self.session.openWithCallback(self.JobViewCB, JobView, job)
1369 def JobViewCB(self, in_background):
1370 job_manager.in_background = in_background
1372 # depends on InfoBarExtensions
1376 self.session.pipshown
1378 self.session.pipshown = False
1379 if SystemInfo.get("NumVideoDecoders", 1) > 1:
1381 self.addExtension((self.getShowHideName, self.showPiP, lambda: True), "blue")
1382 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1383 self.addExtension((self.getSwapName, self.swapPiP, self.pipShown), "yellow")
1385 self.addExtension((self.getShowHideName, self.showPiP, self.pipShown), "blue")
1386 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1389 return self.session.pipshown
1391 def pipHandles0Action(self):
1392 return self.pipShown() and config.usage.pip_zero_button.value != "standard"
1394 def getShowHideName(self):
1395 if self.session.pipshown:
1396 return _("Disable Picture in Picture")
1398 return _("Activate Picture in Picture")
1400 def getSwapName(self):
1401 return _("Swap Services")
1403 def getMoveName(self):
1404 return _("Move Picture in Picture")
1407 if self.session.pipshown:
1408 del self.session.pip
1409 self.session.pipshown = False
1411 self.session.pip = self.session.instantiateDialog(PictureInPicture)
1412 self.session.pip.show()
1413 newservice = self.session.nav.getCurrentlyPlayingServiceReference()
1414 if self.session.pip.playService(newservice):
1415 self.session.pipshown = True
1416 self.session.pip.servicePath = self.servicelist.getCurrentServicePath()
1418 self.session.pipshown = False
1419 del self.session.pip
1420 self.session.nav.playService(newservice)
1423 swapservice = self.session.nav.getCurrentlyPlayingServiceReference()
1424 if self.session.pip.servicePath:
1425 servicepath = self.servicelist.getCurrentServicePath()
1426 ref=servicepath[len(servicepath)-1]
1427 pipref=self.session.pip.getCurrentService()
1428 self.session.pip.playService(swapservice)
1429 self.servicelist.setCurrentServicePath(self.session.pip.servicePath)
1430 if pipref.toString() != ref.toString(): # is a subservice ?
1431 self.session.nav.stopService() # stop portal
1432 self.session.nav.playService(pipref) # start subservice
1433 self.session.pip.servicePath=servicepath
1436 self.session.open(PiPSetup, pip = self.session.pip)
1438 def pipDoHandle0Action(self):
1439 use = config.usage.pip_zero_button.value
1442 elif "swapstop" == use:
1448 from RecordTimer import parseEvent, RecordTimerEntry
1450 class InfoBarInstantRecord:
1451 """Instant Record - handles the instantRecord action in order to
1452 start/stop instant records"""
1454 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
1456 "instantRecord": (self.instantRecord, _("Instant Record...")),
1460 def stopCurrentRecording(self, entry = -1):
1461 if entry is not None and entry != -1:
1462 self.session.nav.RecordTimer.removeEntry(self.recording[entry])
1463 self.recording.remove(self.recording[entry])
1465 def startInstantRecording(self, limitEvent = False):
1466 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
1468 # try to get event info
1471 service = self.session.nav.getCurrentService()
1472 epg = eEPGCache.getInstance()
1473 event = epg.lookupEventTime(serviceref, -1, 0)
1475 info = service.info()
1476 ev = info.getEvent(0)
1482 end = begin + 3600 # dummy
1483 name = "instant record"
1487 if event is not None:
1488 curEvent = parseEvent(event)
1490 description = curEvent[3]
1491 eventid = curEvent[4]
1496 self.session.open(MessageBox, _("No event info found, recording indefinitely."), MessageBox.TYPE_INFO)
1498 if isinstance(serviceref, eServiceReference):
1499 serviceref = ServiceReference(serviceref)
1501 recording = RecordTimerEntry(serviceref, begin, end, name, description, eventid, dirname = config.movielist.last_videodir.value)
1502 recording.dontSave = True
1504 if event is None or limitEvent == False:
1505 recording.autoincrease = True
1506 if recording.setAutoincreaseEnd():
1507 self.session.nav.RecordTimer.record(recording)
1508 self.recording.append(recording)
1510 simulTimerList = self.session.nav.RecordTimer.record(recording)
1511 if simulTimerList is not None: # conflict with other recording
1512 name = simulTimerList[1].name
1513 name_date = ' '.join((name, strftime('%c', localtime(simulTimerList[1].begin))))
1514 print "[TIMER] conflicts with", name_date
1515 recording.autoincrease = True # start with max available length, then increment
1516 if recording.setAutoincreaseEnd():
1517 self.session.nav.RecordTimer.record(recording)
1518 self.recording.append(recording)
1519 self.session.open(MessageBox, _("Record time limited due to conflicting timer %s") % name_date, MessageBox.TYPE_INFO)
1521 self.session.open(MessageBox, _("Couldn't record due to conflicting timer %s") % name, MessageBox.TYPE_INFO)
1522 recording.autoincrease = False
1524 self.recording.append(recording)
1526 def isInstantRecordRunning(self):
1527 print "self.recording:", self.recording
1529 for x in self.recording:
1534 def recordQuestionCallback(self, answer):
1535 print "pre:\n", self.recording
1537 if answer is None or answer[1] == "no":
1540 recording = self.recording[:]
1542 if not x in self.session.nav.RecordTimer.timer_list:
1543 self.recording.remove(x)
1544 elif x.dontSave and x.isRunning():
1545 list.append((x, False))
1547 if answer[1] == "changeduration":
1548 if len(self.recording) == 1:
1549 self.changeDuration(0)
1551 self.session.openWithCallback(self.changeDuration, TimerSelection, list)
1552 elif answer[1] == "changeendtime":
1553 if len(self.recording) == 1:
1556 self.session.openWithCallback(self.setEndtime, TimerSelection, list)
1557 elif answer[1] == "stop":
1558 if len(self.recording) == 1:
1559 self.stopCurrentRecording(0)
1561 self.session.openWithCallback(self.stopCurrentRecording, TimerSelection, list)
1562 elif answer[1] in ( "indefinitely" , "manualduration", "manualendtime", "event"):
1563 self.startInstantRecording(limitEvent = answer[1] in ("event", "manualendtime") or False)
1564 if answer[1] == "manualduration":
1565 self.changeDuration(len(self.recording)-1)
1566 elif answer[1] == "manualendtime":
1567 self.setEndtime(len(self.recording)-1)
1568 print "after:\n", self.recording
1570 def setEndtime(self, entry):
1571 if entry is not None and entry >= 0:
1572 self.selectedEntry = entry
1573 self.endtime=ConfigClock(default = self.recording[self.selectedEntry].end)
1574 dlg = self.session.openWithCallback(self.TimeDateInputClosed, TimeDateInput, self.endtime)
1575 dlg.setTitle(_("Please change recording endtime"))
1577 def TimeDateInputClosed(self, ret):
1580 localendtime = localtime(ret[1])
1581 print "stopping recording at", strftime("%c", localendtime)
1582 if self.recording[self.selectedEntry].end != ret[1]:
1583 self.recording[self.selectedEntry].autoincrease = False
1584 self.recording[self.selectedEntry].end = ret[1]
1585 self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
1587 def changeDuration(self, entry):
1588 if entry is not None and entry >= 0:
1589 self.selectedEntry = entry
1590 self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1592 def inputCallback(self, value):
1593 if value is not None:
1594 print "stopping recording after", int(value), "minutes."
1595 entry = self.recording[self.selectedEntry]
1597 entry.autoincrease = False
1598 entry.end = int(time()) + 60 * int(value)
1599 self.session.nav.RecordTimer.timeChanged(entry)
1601 def instantRecord(self):
1602 dir = config.movielist.last_videodir.value
1603 if not fileExists(dir, 'w'):
1604 dir = resolveFilename(SCOPE_HDD)
1608 # XXX: this message is a little odd as we might be recording to a remote device
1609 self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1612 if self.isInstantRecordRunning():
1613 self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1614 title=_("A recording is currently running.\nWhat do you want to do?"), \
1615 list=((_("stop recording"), "stop"), \
1616 (_("add recording (stop after current event)"), "event"), \
1617 (_("add recording (indefinitely)"), "indefinitely"), \
1618 (_("add recording (enter recording duration)"), "manualduration"), \
1619 (_("add recording (enter recording endtime)"), "manualendtime"), \
1620 (_("change recording (duration)"), "changeduration"), \
1621 (_("change recording (endtime)"), "changeendtime"), \
1622 (_("do nothing"), "no")))
1624 self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1625 title=_("Start recording?"), \
1626 list=((_("add recording (stop after current event)"), "event"), \
1627 (_("add recording (indefinitely)"), "indefinitely"), \
1628 (_("add recording (enter recording duration)"), "manualduration"), \
1629 (_("add recording (enter recording endtime)"), "manualendtime"), \
1630 (_("don't record"), "no")))
1632 from Tools.ISO639 import LanguageCodes
1634 class InfoBarAudioSelection:
1636 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions",
1638 "audioSelection": (self.audioSelection, _("Audio Options...")),
1641 def audioSelection(self):
1642 service = self.session.nav.getCurrentService()
1643 self.audioTracks = audio = service and service.audioTracks()
1644 n = audio and audio.getNumberOfTracks() or 0
1647 self.audioChannel = service.audioChannel()
1652 i = audio.getTrackInfo(idx)
1653 languages = i.getLanguage().split('/')
1654 description = i.getDescription()
1657 for lang in languages:
1660 if LanguageCodes.has_key(lang):
1661 language += LanguageCodes[lang][0]
1666 if len(description):
1667 description += " (" + language + ")"
1669 description = language
1671 tlist.append((description, idx))
1674 tlist.sort(key=lambda x: x[0])
1676 selectedAudio = self.audioTracks.getCurrentTrack()
1681 if x[1] != selectedAudio:
1686 if SystemInfo["CanDownmixAC3"]:
1687 tlist = [(_("AC3 downmix") + " - " +(_("Off"), _("On"))[config.av.downmix_ac3.value and 1 or 0], "CALLFUNC", self.changeAC3Downmix),
1688 ((_("Left"), _("Stereo"), _("Right"))[self.audioChannel.getCurrentChannel()], "mode"),
1690 keys = [ "red", "green", "", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"] + [""]*n
1693 tlist = [((_("Left"), _("Stereo"), _("Right"))[self.audioChannel.getCurrentChannel()], "mode"), ("--", "")] + tlist
1694 keys = [ "red", "", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"] + [""]*n
1696 self.session.openWithCallback(self.audioSelected, ChoiceBox, title=_("Select audio track"), list = tlist, selection = selection, keys = keys, skin_name = "AudioTrackSelection")
1698 del self.audioTracks
1700 def changeAC3Downmix(self, arg):
1701 choicelist = self.session.current_dialog["list"]
1702 list = choicelist.list
1704 list[0][1]=(t[0], t[1], t[2], t[3], t[4], t[5], t[6],
1705 _("AC3 downmix") + " - " + (_("On"), _("Off"))[config.av.downmix_ac3.value and 1 or 0])
1706 choicelist.setList(list)
1707 if config.av.downmix_ac3.value:
1708 config.av.downmix_ac3.value = False
1710 config.av.downmix_ac3.value = True
1711 config.av.downmix_ac3.save()
1713 def audioSelected(self, audio):
1714 if audio is not None:
1715 if isinstance(audio[1], str):
1716 if audio[1] == "mode":
1717 keys = ["red", "green", "yellow"]
1718 selection = self.audioChannel.getCurrentChannel()
1719 tlist = ((_("left"), 0), (_("stereo"), 1), (_("right"), 2))
1720 self.session.openWithCallback(self.modeSelected, ChoiceBox, title=_("Select audio mode"), list = tlist, selection = selection, keys = keys, skin_name ="AudioModeSelection")
1722 del self.audioChannel
1723 if self.session.nav.getCurrentService().audioTracks().getNumberOfTracks() > audio[1]:
1724 self.audioTracks.selectTrack(audio[1])
1726 del self.audioChannel
1727 del self.audioTracks
1729 def modeSelected(self, mode):
1730 if mode is not None:
1731 self.audioChannel.selectChannel(mode[1])
1732 del self.audioChannel
1734 class InfoBarSubserviceSelection:
1736 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1738 "subserviceSelection": (self.subserviceSelection, _("Subservice list...")),
1741 self["SubserviceQuickzapAction"] = HelpableActionMap(self, "InfobarSubserviceQuickzapActions",
1743 "nextSubservice": (self.nextSubservice, _("Switch to next subservice")),
1744 "prevSubservice": (self.prevSubservice, _("Switch to previous subservice"))
1746 self["SubserviceQuickzapAction"].setEnabled(False)
1748 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1750 iPlayableService.evUpdatedEventInfo: self.checkSubservicesAvail
1755 def checkSubservicesAvail(self):
1756 service = self.session.nav.getCurrentService()
1757 subservices = service and service.subServices()
1758 if not subservices or subservices.getNumberOfSubservices() == 0:
1759 self["SubserviceQuickzapAction"].setEnabled(False)
1761 def nextSubservice(self):
1762 self.changeSubservice(+1)
1764 def prevSubservice(self):
1765 self.changeSubservice(-1)
1767 def changeSubservice(self, direction):
1768 service = self.session.nav.getCurrentService()
1769 subservices = service and service.subServices()
1770 n = subservices and subservices.getNumberOfSubservices()
1773 ref = self.session.nav.getCurrentlyPlayingServiceReference()
1776 if subservices.getSubservice(idx).toString() == ref.toString():
1781 selection += direction
1786 newservice = subservices.getSubservice(selection)
1787 if newservice.valid():
1790 self.session.nav.playService(newservice, False)
1792 def subserviceSelection(self):
1793 service = self.session.nav.getCurrentService()
1794 subservices = service and service.subServices()
1795 self.bouquets = self.servicelist.getBouquetList()
1796 n = subservices and subservices.getNumberOfSubservices()
1799 ref = self.session.nav.getCurrentlyPlayingServiceReference()
1803 i = subservices.getSubservice(idx)
1804 if i.toString() == ref.toString():
1806 tlist.append((i.getName(), i))
1809 if self.bouquets and len(self.bouquets):
1810 keys = ["red", "blue", "", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1811 if config.usage.multibouquet.value:
1812 tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to bouquet"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1814 tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to favourites"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1817 tlist = [(_("Quickzap"), "quickzap", service.subServices()), ("--", "")] + tlist
1818 keys = ["red", "", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1821 self.session.openWithCallback(self.subserviceSelected, ChoiceBox, title=_("Please select a subservice..."), list = tlist, selection = selection, keys = keys, skin_name = "SubserviceSelection")
1823 def subserviceSelected(self, service):
1825 if not service is None:
1826 if isinstance(service[1], str):
1827 if service[1] == "quickzap":
1828 from Screens.SubservicesQuickzap import SubservicesQuickzap
1829 self.session.open(SubservicesQuickzap, service[2])
1831 self["SubserviceQuickzapAction"].setEnabled(True)
1832 self.session.nav.playService(service[1], False)
1834 def addSubserviceToBouquetCallback(self, service):
1835 if len(service) > 1 and isinstance(service[1], eServiceReference):
1836 self.selectedSubservice = service
1837 if self.bouquets is None:
1840 cnt = len(self.bouquets)
1841 if cnt > 1: # show bouquet list
1842 self.bsel = self.session.openWithCallback(self.bouquetSelClosed, BouquetSelector, self.bouquets, self.addSubserviceToBouquet)
1843 elif cnt == 1: # add to only one existing bouquet
1844 self.addSubserviceToBouquet(self.bouquets[0][1])
1845 self.session.open(MessageBox, _("Service has been added to the favourites."), MessageBox.TYPE_INFO)
1847 def bouquetSelClosed(self, confirmed):
1849 del self.selectedSubservice
1851 self.session.open(MessageBox, _("Service has been added to the selected bouquet."), MessageBox.TYPE_INFO)
1853 def addSubserviceToBouquet(self, dest):
1854 self.servicelist.addServiceToBouquet(dest, self.selectedSubservice[1])
1856 self.bsel.close(True)
1858 del self.selectedSubservice
1860 class InfoBarAdditionalInfo:
1863 self["RecordingPossible"] = Boolean(fixed=harddiskmanager.HDDCount() > 0 and config.misc.rcused.value == 1)
1864 self["TimeshiftPossible"] = self["RecordingPossible"]
1865 self["ShowTimeshiftOnYellow"] = Boolean(fixed=(not config.misc.rcused.value == 0))
1866 self["ShowAudioOnYellow"] = Boolean(fixed=config.misc.rcused.value == 0)
1867 self["ShowRecordOnRed"] = Boolean(fixed=config.misc.rcused.value == 1)
1868 self["ExtensionsAvailable"] = Boolean(fixed=1)
1870 class InfoBarNotifications:
1872 self.onExecBegin.append(self.checkNotifications)
1873 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1874 self.onClose.append(self.__removeNotification)
1876 def __removeNotification(self):
1877 Notifications.notificationAdded.remove(self.checkNotificationsIfExecing)
1879 def checkNotificationsIfExecing(self):
1881 self.checkNotifications()
1883 def checkNotifications(self):
1884 notifications = Notifications.notifications
1886 n = notifications[0]
1888 del notifications[0]
1891 if n[3].has_key("onSessionOpenCallback"):
1892 n[3]["onSessionOpenCallback"]()
1893 del n[3]["onSessionOpenCallback"]
1896 dlg = self.session.openWithCallback(cb, n[1], *n[2], **n[3])
1898 dlg = self.session.open(n[1], *n[2], **n[3])
1900 # remember that this notification is currently active
1902 Notifications.current_notifications.append(d)
1903 dlg.onClose.append(boundFunction(self.__notificationClosed, d))
1905 def __notificationClosed(self, d):
1906 Notifications.current_notifications.remove(d)
1908 class InfoBarServiceNotifications:
1910 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1912 iPlayableService.evEnd: self.serviceHasEnded
1915 def serviceHasEnded(self):
1916 print "service end!"
1919 self.setSeekState(self.SEEK_STATE_PLAY)
1923 class InfoBarCueSheetSupport:
1929 ENABLE_RESUME_SUPPORT = False
1931 def __init__(self, actionmap = "InfobarCueSheetActions"):
1932 self["CueSheetActions"] = HelpableActionMap(self, actionmap,
1934 "jumpPreviousMark": (self.jumpPreviousMark, _("jump to previous marked position")),
1935 "jumpNextMark": (self.jumpNextMark, _("jump to next marked position")),
1936 "toggleMark": (self.toggleMark, _("toggle a cut mark at the current position"))
1940 self.is_closing = False
1941 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1943 iPlayableService.evStart: self.__serviceStarted,
1946 def __serviceStarted(self):
1949 print "new service started! trying to download cuts!"
1950 self.downloadCuesheet()
1952 if self.ENABLE_RESUME_SUPPORT:
1955 for (pts, what) in self.cut_list:
1956 if what == self.CUT_TYPE_LAST:
1959 if last is not None:
1960 self.resume_point = last
1961 if config.usage.on_movie_start.value == "ask":
1962 Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Do you want to resume this playback?"), timeout=10)
1963 elif config.usage.on_movie_start.value == "resume":
1964 # TRANSLATORS: The string "Resuming playback" flashes for a moment
1965 # TRANSLATORS: at the start of a movie, when the user has selected
1966 # TRANSLATORS: "Resume from last position" as start behavior.
1967 # TRANSLATORS: The purpose is to notify the user that the movie starts
1968 # TRANSLATORS: in the middle somewhere and not from the beginning.
1969 # TRANSLATORS: (Some translators seem to have interpreted it as a
1970 # TRANSLATORS: question or a choice, but it is a statement.)
1971 Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Resuming playback"), timeout=2, type=MessageBox.TYPE_INFO)
1973 def playLastCB(self, answer):
1975 self.doSeek(self.resume_point)
1976 self.hideAfterResume()
1978 def hideAfterResume(self):
1979 if isinstance(self, InfoBarShowHide):
1982 def __getSeekable(self):
1983 service = self.session.nav.getCurrentService()
1986 return service.seek()
1988 def cueGetCurrentPosition(self):
1989 seek = self.__getSeekable()
1992 r = seek.getPlayPosition()
1997 def cueGetEndCutPosition(self):
2000 for cp in self.cut_list:
2001 if cp[1] == self.CUT_TYPE_OUT:
2005 elif cp[1] == self.CUT_TYPE_IN:
2009 def jumpPreviousNextMark(self, cmp, start=False):
2010 current_pos = self.cueGetCurrentPosition()
2011 if current_pos is None:
2013 mark = self.getNearestCutPoint(current_pos, cmp=cmp, start=start)
2014 if mark is not None:
2022 def jumpPreviousMark(self):
2023 # we add 2 seconds, so if the play position is <2s after
2024 # the mark, the mark before will be used
2025 self.jumpPreviousNextMark(lambda x: -x-5*90000, start=True)
2027 def jumpNextMark(self):
2028 if not self.jumpPreviousNextMark(lambda x: x):
2031 def getNearestCutPoint(self, pts, cmp=abs, start=False):
2037 bestdiff = cmp(0 - pts)
2039 nearest = [0, False]
2040 for cp in self.cut_list:
2041 if beforecut and cp[1] in (self.CUT_TYPE_IN, self.CUT_TYPE_OUT):
2043 if cp[1] == self.CUT_TYPE_IN: # Start is here, disregard previous marks
2044 diff = cmp(cp[0] - pts)
2050 if cp[1] in (self.CUT_TYPE_MARK, self.CUT_TYPE_LAST):
2051 diff = cmp(cp[0] - pts)
2052 if diff >= 0 and (nearest is None or bestdiff > diff):
2057 def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
2058 current_pos = self.cueGetCurrentPosition()
2059 if current_pos is None:
2060 print "not seekable"
2063 nearest_cutpoint = self.getNearestCutPoint(current_pos)
2065 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
2067 return nearest_cutpoint
2069 self.removeMark(nearest_cutpoint)
2070 elif not onlyremove and not onlyreturn:
2071 self.addMark((current_pos, self.CUT_TYPE_MARK))
2076 def addMark(self, point):
2077 insort(self.cut_list, point)
2078 self.uploadCuesheet()
2079 self.showAfterCuesheetOperation()
2081 def removeMark(self, point):
2082 self.cut_list.remove(point)
2083 self.uploadCuesheet()
2084 self.showAfterCuesheetOperation()
2086 def showAfterCuesheetOperation(self):
2087 if isinstance(self, InfoBarShowHide):
2090 def __getCuesheet(self):
2091 service = self.session.nav.getCurrentService()
2094 return service.cueSheet()
2096 def uploadCuesheet(self):
2097 cue = self.__getCuesheet()
2100 print "upload failed, no cuesheet interface"
2102 cue.setCutList(self.cut_list)
2104 def downloadCuesheet(self):
2105 cue = self.__getCuesheet()
2108 print "download failed, no cuesheet interface"
2111 self.cut_list = cue.getCutList()
2113 class InfoBarSummary(Screen):
2115 <screen position="0,0" size="132,64">
2116 <widget source="global.CurrentTime" render="Label" position="62,46" size="82,18" font="Regular;16" >
2117 <convert type="ClockToText">WithSeconds</convert>
2119 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="82,18" zPosition="1" >
2120 <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2121 <convert type="ConditionalShowHide">Blink</convert>
2123 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2124 <convert type="ServiceName">Name</convert>
2126 <widget source="session.Event_Now" render="Progress" position="6,46" size="46,18" borderWidth="1" >
2127 <convert type="EventTime">Progress</convert>
2131 # for picon: (path="piconlcd" will use LCD picons)
2132 # <widget source="session.CurrentService" render="Picon" position="6,0" size="120,64" path="piconlcd" >
2133 # <convert type="ServiceName">Reference</convert>
2136 class InfoBarSummarySupport:
2140 def createSummary(self):
2141 return InfoBarSummary
2143 class InfoBarMoviePlayerSummary(Screen):
2145 <screen position="0,0" size="132,64">
2146 <widget source="global.CurrentTime" render="Label" position="62,46" size="64,18" font="Regular;16" halign="right" >
2147 <convert type="ClockToText">WithSeconds</convert>
2149 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="64,18" zPosition="1" >
2150 <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2151 <convert type="ConditionalShowHide">Blink</convert>
2153 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2154 <convert type="ServiceName">Name</convert>
2156 <widget source="session.CurrentService" render="Progress" position="6,46" size="56,18" borderWidth="1" >
2157 <convert type="ServicePosition">Position</convert>
2161 class InfoBarMoviePlayerSummarySupport:
2165 def createSummary(self):
2166 return InfoBarMoviePlayerSummary
2168 class InfoBarTeletextPlugin:
2170 self.teletext_plugin = None
2172 for p in plugins.getPlugins(PluginDescriptor.WHERE_TELETEXT):
2173 self.teletext_plugin = p
2175 if self.teletext_plugin is not None:
2176 self["TeletextActions"] = HelpableActionMap(self, "InfobarTeletextActions",
2178 "startTeletext": (self.startTeletext, _("View teletext..."))
2181 print "no teletext plugin found!"
2183 def startTeletext(self):
2184 self.teletext_plugin(session=self.session, service=self.session.nav.getCurrentService())
2186 class InfoBarSubtitleSupport(object):
2188 object.__init__(self)
2189 self.subtitle_window = self.session.instantiateDialog(SubtitleDisplay)
2190 self.__subtitles_enabled = False
2192 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2194 iPlayableService.evEnd: self.__serviceStopped,
2195 iPlayableService.evUpdatedInfo: self.__updatedInfo
2197 self.cached_subtitle_checked = False
2198 self.__selected_subtitle = None
2200 def __serviceStopped(self):
2201 self.cached_subtitle_checked = False
2202 if self.__subtitles_enabled:
2203 self.subtitle_window.hide()
2204 self.__subtitles_enabled = False
2205 self.__selected_subtitle = None
2207 def __updatedInfo(self):
2208 if not self.cached_subtitle_checked:
2209 self.cached_subtitle_checked = True
2210 subtitle = self.getCurrentServiceSubtitle()
2211 self.setSelectedSubtitle(subtitle and subtitle.getCachedSubtitle())
2212 if self.__selected_subtitle:
2213 self.setSubtitlesEnable(True)
2215 def getCurrentServiceSubtitle(self):
2216 service = self.session.nav.getCurrentService()
2217 return service and service.subtitle()
2219 def setSubtitlesEnable(self, enable=True):
2220 subtitle = self.getCurrentServiceSubtitle()
2222 if self.__selected_subtitle:
2223 if subtitle and not self.__subtitles_enabled:
2224 subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
2225 self.subtitle_window.show()
2226 self.__subtitles_enabled = True
2229 subtitle.disableSubtitles(self.subtitle_window.instance)
2230 self.__selected_subtitle = False
2231 self.__subtitles_enabled = False
2232 self.subtitle_window.hide()
2234 def setSelectedSubtitle(self, subtitle):
2235 self.__selected_subtitle = subtitle
2237 subtitles_enabled = property(lambda self: self.__subtitles_enabled, setSubtitlesEnable)
2238 selected_subtitle = property(lambda self: self.__selected_subtitle, setSelectedSubtitle)
2240 class InfoBarServiceErrorPopupSupport:
2242 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2244 iPlayableService.evTuneFailed: self.__tuneFailed,
2245 iPlayableService.evStart: self.__serviceStarted
2247 self.__serviceStarted()
2249 def __serviceStarted(self):
2250 self.last_error = None
2251 Notifications.RemovePopup(id = "ZapError")
2253 def __tuneFailed(self):
2254 service = self.session.nav.getCurrentService()
2255 info = service and service.info()
2256 error = info and info.getInfo(iServiceInformation.sDVBState)
2258 if error == self.last_error:
2261 self.last_error = error
2264 eDVBServicePMTHandler.eventNoResources: _("No free tuner!"),
2265 eDVBServicePMTHandler.eventTuneFailed: _("Tune failed!"),
2266 eDVBServicePMTHandler.eventNoPAT: _("No data on transponder!\n(Timeout reading PAT)"),
2267 eDVBServicePMTHandler.eventNoPATEntry: _("Service not found!\n(SID not found in PAT)"),
2268 eDVBServicePMTHandler.eventNoPMT: _("Service invalid!\n(Timeout reading PMT)"),
2269 eDVBServicePMTHandler.eventNewProgramInfo: None,
2270 eDVBServicePMTHandler.eventTuned: None,
2271 eDVBServicePMTHandler.eventSOF: None,
2272 eDVBServicePMTHandler.eventEOF: None,
2273 eDVBServicePMTHandler.eventMisconfiguration: _("Service unavailable!\nCheck tuner configuration!"),
2274 }.get(error) #this returns None when the key not exist in the dict
2276 if error is not None:
2277 Notifications.AddPopup(text = error, type = MessageBox.TYPE_ERROR, timeout = 5, id = "ZapError")
2279 Notifications.RemovePopup(id = "ZapError")