1 from ChannelSelection import ChannelSelection, BouquetSelector
3 from Components.ActionMap import ActionMap, HelpableActionMap
4 from Components.ActionMap import NumberActionMap
5 from Components.Harddisk import harddiskmanager
6 from Components.Input import Input
7 from Components.Label import Label
8 from Components.PluginComponent import plugins
9 from Components.ServiceEventTracker import ServiceEventTracker
10 from Components.Sources.Boolean import Boolean
11 from Components.config import config, ConfigBoolean, ConfigClock
12 from Components.SystemInfo import SystemInfo
13 from Components.UsageConfig import preferredInstantRecordPath, defaultMoviePath
14 from EpgSelection import EPGSelection
15 from Plugins.Plugin import PluginDescriptor
17 from Screen import Screen
18 from Screens.ChoiceBox import ChoiceBox
19 from Screens.Dish import Dish
20 from Screens.EventView import EventViewEPGSelect, EventViewSimple
21 from Screens.InputBox import InputBox
22 from Screens.MessageBox import MessageBox
23 from Screens.MinuteInput import MinuteInput
24 from Screens.TimerSelection import TimerSelection
25 from Screens.PictureInPicture import PictureInPicture
26 from Screens.SubtitleDisplay import SubtitleDisplay
27 from Screens.RdsDisplay import RdsInfoDisplay, RassInteractive
28 from Screens.TimeDateInput import TimeDateInput
29 from Screens.UnhandledKey import UnhandledKey
30 from ServiceReference import ServiceReference
32 from Tools import Notifications
33 from Tools.Directories import fileExists
35 from enigma import eTimer, eServiceCenter, eDVBServicePMTHandler, iServiceInformation, \
36 iPlayableService, eServiceReference, eEPGCache, eActionMap
38 from time import time, localtime, strftime
39 from os import stat as os_stat
40 from bisect import insort
42 from RecordTimer import RecordTimerEntry, RecordTimer
45 from Menu import MainMenu, mdom
49 self.dishDialog = self.session.instantiateDialog(Dish)
51 class InfoBarUnhandledKey:
53 self.unhandledKeyDialog = self.session.instantiateDialog(UnhandledKey)
54 self.hideTimer = eTimer()
55 self.hideTimer.callback.append(self.unhandledKeyDialog.hide)
56 self.checkUnusedTimer = eTimer()
57 self.checkUnusedTimer.callback.append(self.checkUnused)
58 self.onLayoutFinish.append(self.unhandledKeyDialog.hide)
59 eActionMap.getInstance().bindAction('', -0x7FFFFFFF, self.actionA) #highest prio
60 eActionMap.getInstance().bindAction('', 0x7FFFFFFF, self.actionB) #lowest prio
65 #this function is called on every keypress!
66 def actionA(self, key, flag):
69 if self.checkUnusedTimer.isActive():
70 self.checkUnusedTimer.stop()
73 self.flags = self.uflags = 0
74 self.flags |= (1<<flag)
76 self.checkUnusedTimer.start(0, True)
79 #this function is only called when no other action has handled this key
80 def actionB(self, key, flag):
82 self.uflags |= (1<<flag)
84 def checkUnused(self):
85 if self.flags == self.uflags:
86 self.unhandledKeyDialog.show()
87 self.hideTimer.start(2000, True)
89 class InfoBarShowHide:
90 """ InfoBar show/hide control, accepts toggleShow and hide actions, might start
98 self["ShowHideActions"] = ActionMap( ["InfobarShowHideActions"] ,
100 "toggleShow": self.toggleShow,
102 }, 1) # lower prio to make it possible to override ok and cancel..
104 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
106 iPlayableService.evStart: self.serviceStarted,
109 self.__state = self.STATE_SHOWN
112 self.hideTimer = eTimer()
113 self.hideTimer.callback.append(self.doTimerHide)
114 self.hideTimer.start(5000, True)
116 self.onShow.append(self.__onShow)
117 self.onHide.append(self.__onHide)
119 def serviceStarted(self):
121 if config.usage.show_infobar_on_zap.value:
125 self.__state = self.STATE_SHOWN
126 self.startHideTimer()
128 def startHideTimer(self):
129 if self.__state == self.STATE_SHOWN and not self.__locked:
130 idx = config.usage.infobar_timeout.index
132 self.hideTimer.start(idx*1000, True)
135 self.__state = self.STATE_HIDDEN
139 self.startHideTimer()
141 def doTimerHide(self):
142 self.hideTimer.stop()
143 if self.__state == self.STATE_SHOWN:
146 def toggleShow(self):
147 if self.__state == self.STATE_SHOWN:
149 self.hideTimer.stop()
150 elif self.__state == self.STATE_HIDDEN:
154 self.__locked = self.__locked + 1
157 self.hideTimer.stop()
159 def unlockShow(self):
160 self.__locked = self.__locked - 1
162 self.startHideTimer()
164 # def startShow(self):
165 # self.instance.m_animation.startMoveAnimation(ePoint(0, 600), ePoint(0, 380), 100)
166 # self.__state = self.STATE_SHOWN
168 # def startHide(self):
169 # self.instance.m_animation.startMoveAnimation(ePoint(0, 380), ePoint(0, 600), 100)
170 # self.__state = self.STATE_HIDDEN
172 class NumberZap(Screen):
179 self.close(int(self["number"].getText()))
181 def keyNumberGlobal(self, number):
182 self.Timer.start(3000, True) #reset timer
183 self.field = self.field + str(number)
184 self["number"].setText(self.field)
185 if len(self.field) >= 4:
188 def __init__(self, session, number):
189 Screen.__init__(self, session)
190 self.field = str(number)
192 self["channel"] = Label(_("Channel:"))
194 self["number"] = Label(self.field)
196 self["actions"] = NumberActionMap( [ "SetupActions" ],
200 "1": self.keyNumberGlobal,
201 "2": self.keyNumberGlobal,
202 "3": self.keyNumberGlobal,
203 "4": self.keyNumberGlobal,
204 "5": self.keyNumberGlobal,
205 "6": self.keyNumberGlobal,
206 "7": self.keyNumberGlobal,
207 "8": self.keyNumberGlobal,
208 "9": self.keyNumberGlobal,
209 "0": self.keyNumberGlobal
212 self.Timer = eTimer()
213 self.Timer.callback.append(self.keyOK)
214 self.Timer.start(3000, True)
216 class InfoBarNumberZap:
217 """ Handles an initial number for NumberZapping """
219 self["NumberActions"] = NumberActionMap( [ "NumberActions"],
221 "1": self.keyNumberGlobal,
222 "2": self.keyNumberGlobal,
223 "3": self.keyNumberGlobal,
224 "4": self.keyNumberGlobal,
225 "5": self.keyNumberGlobal,
226 "6": self.keyNumberGlobal,
227 "7": self.keyNumberGlobal,
228 "8": self.keyNumberGlobal,
229 "9": self.keyNumberGlobal,
230 "0": self.keyNumberGlobal,
233 def keyNumberGlobal(self, number):
234 # print "You pressed number " + str(number)
236 if isinstance(self, InfoBarPiP) and self.pipHandles0Action():
237 self.pipDoHandle0Action()
239 self.servicelist.recallPrevService()
241 if self.has_key("TimeshiftActions") and not self.timeshift_enabled:
242 self.session.openWithCallback(self.numberEntered, NumberZap, number)
244 def numberEntered(self, retval):
245 # print self.servicelist
247 self.zapToNumber(retval)
249 def searchNumberHelper(self, serviceHandler, num, bouquet):
250 servicelist = serviceHandler.list(bouquet)
251 if not servicelist is None:
253 serviceIterator = servicelist.getNext()
254 if not serviceIterator.valid(): #check end of list
256 playable = not (serviceIterator.flags & (eServiceReference.isMarker|eServiceReference.isDirectory))
259 if not num: #found service with searched number ?
260 return serviceIterator, 0
263 def zapToNumber(self, number):
264 bouquet = self.servicelist.bouquet_root
266 serviceHandler = eServiceCenter.getInstance()
267 if not config.usage.multibouquet.value:
268 service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
270 bouquetlist = serviceHandler.list(bouquet)
271 if not bouquetlist is None:
273 bouquet = bouquetlist.getNext()
274 if not bouquet.valid(): #check end of list
276 if bouquet.flags & eServiceReference.isDirectory:
277 service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
278 if not service is None:
279 if self.servicelist.getRoot() != bouquet: #already in correct bouquet?
280 self.servicelist.clearPath()
281 if self.servicelist.bouquet_root != bouquet:
282 self.servicelist.enterPath(self.servicelist.bouquet_root)
283 self.servicelist.enterPath(bouquet)
284 self.servicelist.setCurrentSelection(service) #select the service in servicelist
285 self.servicelist.zap()
287 config.misc.initialchannelselection = ConfigBoolean(default = True)
289 class InfoBarChannelSelection:
290 """ ChannelSelection - handles the channelSelection dialog and the initial
291 channelChange actions which open the channelSelection dialog """
294 self.servicelist = self.session.instantiateDialog(ChannelSelection)
296 if config.misc.initialchannelselection.value:
297 self.onShown.append(self.firstRun)
299 self["ChannelSelectActions"] = HelpableActionMap(self, "InfobarChannelSelection",
301 "switchChannelUp": (self.switchChannelUp, _("open servicelist(up)")),
302 "switchChannelDown": (self.switchChannelDown, _("open servicelist(down)")),
303 "zapUp": (self.zapUp, _("previous channel")),
304 "zapDown": (self.zapDown, _("next channel")),
305 "historyBack": (self.historyBack, _("previous channel in history")),
306 "historyNext": (self.historyNext, _("next channel in history")),
307 "openServiceList": (self.openServiceList, _("open servicelist")),
310 def showTvChannelList(self, zap=False):
311 self.servicelist.setModeTv()
313 self.servicelist.zap()
314 self.session.execDialog(self.servicelist)
316 def showRadioChannelList(self, zap=False):
317 self.servicelist.setModeRadio()
319 self.servicelist.zap()
320 self.session.execDialog(self.servicelist)
323 self.onShown.remove(self.firstRun)
324 config.misc.initialchannelselection.value = False
325 config.misc.initialchannelselection.save()
326 self.switchChannelDown()
328 def historyBack(self):
329 self.servicelist.historyBack()
331 def historyNext(self):
332 self.servicelist.historyNext()
334 def switchChannelUp(self):
335 self.servicelist.moveUp()
336 self.session.execDialog(self.servicelist)
338 def switchChannelDown(self):
339 self.servicelist.moveDown()
340 self.session.execDialog(self.servicelist)
342 def openServiceList(self):
343 self.session.execDialog(self.servicelist)
346 if self.servicelist.inBouquet():
347 prev = self.servicelist.getCurrentSelection()
349 prev = prev.toString()
351 if config.usage.quickzap_bouquet_change.value:
352 if self.servicelist.atBegin():
353 self.servicelist.prevBouquet()
354 self.servicelist.moveUp()
355 cur = self.servicelist.getCurrentSelection()
356 if not cur or (not (cur.flags & 64)) or cur.toString() == prev:
359 self.servicelist.moveUp()
360 self.servicelist.zap()
363 if self.servicelist.inBouquet():
364 prev = self.servicelist.getCurrentSelection()
366 prev = prev.toString()
368 if config.usage.quickzap_bouquet_change.value and self.servicelist.atEnd():
369 self.servicelist.nextBouquet()
371 self.servicelist.moveDown()
372 cur = self.servicelist.getCurrentSelection()
373 if not cur or (not (cur.flags & 64)) or cur.toString() == prev:
376 self.servicelist.moveDown()
377 self.servicelist.zap()
380 """ Handles a menu action, to open the (main) menu """
382 self["MenuActions"] = HelpableActionMap(self, "InfobarMenuActions",
384 "mainMenu": (self.mainMenu, _("Enter main menu...")),
386 self.session.infobar = None
389 print "loading mainmenu XML..."
390 menu = mdom.getroot()
391 assert menu.tag == "menu", "root element in menu must be 'menu'!"
393 self.session.infobar = self
394 # so we can access the currently active infobar from screens opened from within the mainmenu
395 # at the moment used from the SubserviceSelection
397 self.session.openWithCallback(self.mainMenuClosed, MainMenu, menu)
399 def mainMenuClosed(self, *val):
400 self.session.infobar = None
402 class InfoBarSimpleEventView:
403 """ Opens the Eventview for now/next """
405 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
407 "showEventInfo": (self.openEventView, _("show event details")),
408 "showInfobarOrEpgWhenInfobarAlreadyVisible": self.showEventInfoWhenNotVisible,
411 def showEventInfoWhenNotVisible(self):
418 def openEventView(self):
420 self.epglist = epglist
421 service = self.session.nav.getCurrentService()
422 ref = self.session.nav.getCurrentlyPlayingServiceReference()
423 info = service.info()
431 self.session.open(EventViewSimple, epglist[0], ServiceReference(ref), self.eventViewCallback)
433 def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
434 epglist = self.epglist
437 epglist[0] = epglist[1]
441 class SimpleServicelist:
442 def __init__(self, services):
443 self.services = services
444 self.length = len(services)
447 def selectService(self, service):
453 while self.services[self.current].ref != service:
455 if self.current >= self.length:
459 def nextService(self):
462 if self.current+1 < self.length:
467 def prevService(self):
470 if self.current-1 > -1:
473 self.current = self.length - 1
475 def currentService(self):
476 if not self.length or self.current >= self.length:
478 return self.services[self.current]
481 """ EPG - Opens an EPG list when the showEPGList action fires """
483 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
485 iPlayableService.evUpdatedEventInfo: self.__evEventInfoChanged,
488 self.is_now_next = False
490 self.bouquetSel = None
491 self.eventView = None
492 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
494 "showEventInfo": (self.openEventView, _("show EPG...")),
495 "showEventInfoPlugin": (self.showEventInfoPlugins, _("show single service EPG...")),
496 "showInfobarOrEpgWhenInfobarAlreadyVisible": self.showEventInfoWhenNotVisible,
499 def showEventInfoWhenNotVisible(self):
506 def zapToService(self, service):
507 if not service is None:
508 if self.servicelist.getRoot() != self.epg_bouquet: #already in correct bouquet?
509 self.servicelist.clearPath()
510 if self.servicelist.bouquet_root != self.epg_bouquet:
511 self.servicelist.enterPath(self.servicelist.bouquet_root)
512 self.servicelist.enterPath(self.epg_bouquet)
513 self.servicelist.setCurrentSelection(service) #select the service in servicelist
514 self.servicelist.zap()
516 def getBouquetServices(self, bouquet):
518 servicelist = eServiceCenter.getInstance().list(bouquet)
519 if not servicelist is None:
521 service = servicelist.getNext()
522 if not service.valid(): #check if end of list
524 if service.flags & (eServiceReference.isDirectory | eServiceReference.isMarker): #ignore non playable services
526 services.append(ServiceReference(service))
529 def openBouquetEPG(self, bouquet, withCallback=True):
530 services = self.getBouquetServices(bouquet)
532 self.epg_bouquet = bouquet
534 self.dlg_stack.append(self.session.openWithCallback(self.closed, EPGSelection, services, self.zapToService, None, self.changeBouquetCB))
536 self.session.open(EPGSelection, services, self.zapToService, None, self.changeBouquetCB)
538 def changeBouquetCB(self, direction, epg):
541 self.bouquetSel.down()
544 bouquet = self.bouquetSel.getCurrent()
545 services = self.getBouquetServices(bouquet)
547 self.epg_bouquet = bouquet
548 epg.setServices(services)
550 def closed(self, ret=False):
551 closedScreen = self.dlg_stack.pop()
552 if self.bouquetSel and closedScreen == self.bouquetSel:
553 self.bouquetSel = None
554 elif self.eventView and closedScreen == self.eventView:
555 self.eventView = None
557 dlgs=len(self.dlg_stack)
559 self.dlg_stack[dlgs-1].close(dlgs > 1)
561 def openMultiServiceEPG(self, withCallback=True):
562 bouquets = self.servicelist.getBouquetList()
567 if cnt > 1: # show bouquet list
569 self.bouquetSel = self.session.openWithCallback(self.closed, BouquetSelector, bouquets, self.openBouquetEPG, enableWrapAround=True)
570 self.dlg_stack.append(self.bouquetSel)
572 self.bouquetSel = self.session.open(BouquetSelector, bouquets, self.openBouquetEPG, enableWrapAround=True)
574 self.openBouquetEPG(bouquets[0][1], withCallback)
576 def changeServiceCB(self, direction, epg):
579 self.serviceSel.nextService()
581 self.serviceSel.prevService()
582 epg.setService(self.serviceSel.currentService())
584 def SingleServiceEPGClosed(self, ret=False):
585 self.serviceSel = None
587 def openSingleServiceEPG(self):
588 ref=self.session.nav.getCurrentlyPlayingServiceReference()
590 if self.servicelist.getMutableList() is not None: # bouquet in channellist
591 current_path = self.servicelist.getRoot()
592 services = self.getBouquetServices(current_path)
593 self.serviceSel = SimpleServicelist(services)
594 if self.serviceSel.selectService(ref):
595 self.session.openWithCallback(self.SingleServiceEPGClosed, EPGSelection, ref, serviceChangeCB = self.changeServiceCB)
597 self.session.openWithCallback(self.SingleServiceEPGClosed, EPGSelection, ref)
599 self.session.open(EPGSelection, ref)
601 def showEventInfoPlugins(self):
602 list = [(p.name, boundFunction(self.runPlugin, p)) for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EVENTINFO)]
605 list.append((_("show single service EPG..."), self.openSingleServiceEPG))
606 self.session.openWithCallback(self.EventInfoPluginChosen, ChoiceBox, title=_("Please choose an extension..."), list = list, skin_name = "EPGExtensionsList")
608 self.openSingleServiceEPG()
610 def runPlugin(self, plugin):
611 plugin(session = self.session, servicelist = self.servicelist)
613 def EventInfoPluginChosen(self, answer):
614 if answer is not None:
617 def openSimilarList(self, eventid, refstr):
618 self.session.open(EPGSelection, refstr, None, eventid)
620 def getNowNext(self):
622 service = self.session.nav.getCurrentService()
623 info = service and service.info()
624 ptr = info and info.getEvent(0)
627 ptr = info and info.getEvent(1)
630 self.epglist = epglist
632 def __evEventInfoChanged(self):
633 if self.is_now_next and len(self.dlg_stack) == 1:
635 assert self.eventView
637 self.eventView.setEvent(self.epglist[0])
639 def openEventView(self):
640 ref = self.session.nav.getCurrentlyPlayingServiceReference()
642 epglist = self.epglist
644 self.is_now_next = False
645 epg = eEPGCache.getInstance()
646 ptr = ref and ref.valid() and epg.lookupEventTime(ref, -1)
649 ptr = epg.lookupEventTime(ref, ptr.getBeginTime(), +1)
653 self.is_now_next = True
655 self.eventView = self.session.openWithCallback(self.closed, EventViewEPGSelect, self.epglist[0], ServiceReference(ref), self.eventViewCallback, self.openSingleServiceEPG, self.openMultiServiceEPG, self.openSimilarList)
656 self.dlg_stack.append(self.eventView)
658 print "no epg for the service avail.. so we show multiepg instead of eventinfo"
659 self.openMultiServiceEPG(False)
661 def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
662 epglist = self.epglist
665 epglist[0]=epglist[1]
669 class InfoBarRdsDecoder:
670 """provides RDS and Rass support/display"""
672 self.rds_display = self.session.instantiateDialog(RdsInfoDisplay)
673 self.rass_interactive = None
675 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
677 iPlayableService.evEnd: self.__serviceStopped,
678 iPlayableService.evUpdatedRassSlidePic: self.RassSlidePicChanged
681 self["RdsActions"] = ActionMap(["InfobarRdsActions"],
683 "startRassInteractive": self.startRassInteractive
686 self["RdsActions"].setEnabled(False)
688 self.onLayoutFinish.append(self.rds_display.show)
689 self.rds_display.onRassInteractivePossibilityChanged.append(self.RassInteractivePossibilityChanged)
691 def RassInteractivePossibilityChanged(self, state):
692 self["RdsActions"].setEnabled(state)
694 def RassSlidePicChanged(self):
695 if not self.rass_interactive:
696 service = self.session.nav.getCurrentService()
697 decoder = service and service.rdsDecoder()
699 decoder.showRassSlidePicture()
701 def __serviceStopped(self):
702 if self.rass_interactive is not None:
703 rass_interactive = self.rass_interactive
704 self.rass_interactive = None
705 rass_interactive.close()
707 def startRassInteractive(self):
708 self.rds_display.hide()
709 self.rass_interactive = self.session.openWithCallback(self.RassInteractiveClosed, RassInteractive)
711 def RassInteractiveClosed(self, *val):
712 if self.rass_interactive is not None:
713 self.rass_interactive = None
714 self.RassSlidePicChanged()
715 self.rds_display.show()
718 """handles actions like seeking, pause"""
720 SEEK_STATE_PLAY = (0, 0, 0, ">")
721 SEEK_STATE_PAUSE = (1, 0, 0, "||")
722 SEEK_STATE_EOF = (1, 0, 0, "END")
724 def __init__(self, actionmap = "InfobarSeekActions", useSeekBackHack=True):
725 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
727 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
728 iPlayableService.evStart: self.__serviceStarted,
730 iPlayableService.evEOF: self.__evEOF,
731 iPlayableService.evSOF: self.__evSOF,
733 self.fast_winding_hint_message_showed = False
735 class InfoBarSeekActionMap(HelpableActionMap):
736 def __init__(self, screen, *args, **kwargs):
737 HelpableActionMap.__init__(self, screen, *args, **kwargs)
740 def action(self, contexts, action):
741 print "action:", action
742 if action[:5] == "seek:":
743 time = int(action[5:])
744 self.screen.doSeekRelative(time * 90000)
746 elif action[:8] == "seekdef:":
747 key = int(action[8:])
748 time = (-config.seek.selfdefined_13.value, False, config.seek.selfdefined_13.value,
749 -config.seek.selfdefined_46.value, False, config.seek.selfdefined_46.value,
750 -config.seek.selfdefined_79.value, False, config.seek.selfdefined_79.value)[key-1]
751 self.screen.doSeekRelative(time * 90000)
754 return HelpableActionMap.action(self, contexts, action)
756 self["SeekActions"] = InfoBarSeekActionMap(self, actionmap,
758 "playpauseService": self.playpauseService,
759 "pauseService": (self.pauseService, _("pause")),
760 "unPauseService": (self.unPauseService, _("continue")),
762 "seekFwd": (self.seekFwd, _("skip forward")),
763 "seekFwdManual": (self.seekFwdManual, _("skip forward (enter time)")),
764 "seekBack": (self.seekBack, _("skip backward")),
765 "seekBackManual": (self.seekBackManual, _("skip backward (enter time)"))
767 # give them a little more priority to win over color buttons
769 self["SeekActions"].setEnabled(False)
771 self.seekstate = self.SEEK_STATE_PLAY
772 self.lastseekstate = self.SEEK_STATE_PLAY
774 self.onPlayStateChanged = [ ]
776 self.lockedBecauseOfSkipping = False
778 self.__seekableStatusChanged()
780 def makeStateForward(self, n):
781 # minspeed = config.seek.stepwise_minspeed.value
782 # repeat = int(config.seek.stepwise_repeat.value)
783 # if minspeed != "Never" and n >= int(minspeed) and repeat > 1:
784 # return (0, n * repeat, repeat, ">> %dx" % n)
786 return (0, n, 0, ">> %dx" % n)
788 def makeStateBackward(self, n):
789 # minspeed = config.seek.stepwise_minspeed.value
790 # repeat = int(config.seek.stepwise_repeat.value)
791 # if minspeed != "Never" and n >= int(minspeed) and repeat > 1:
792 # return (0, -n * repeat, repeat, "<< %dx" % n)
794 return (0, -n, 0, "<< %dx" % n)
796 def makeStateSlowMotion(self, n):
797 return (0, 0, n, "/%d" % n)
799 def isStateForward(self, state):
802 def isStateBackward(self, state):
805 def isStateSlowMotion(self, state):
806 return state[1] == 0 and state[2] > 1
808 def getHigher(self, n, lst):
814 def getLower(self, n, lst):
822 def showAfterSeek(self):
823 if isinstance(self, InfoBarShowHide):
833 service = self.session.nav.getCurrentService()
837 seek = service.seek()
839 if seek is None or not seek.isCurrentlySeekable():
844 def isSeekable(self):
845 if self.getSeek() is None:
849 def __seekableStatusChanged(self):
850 # print "seekable status changed!"
851 if not self.isSeekable():
852 self["SeekActions"].setEnabled(False)
853 # print "not seekable, return to play"
854 self.setSeekState(self.SEEK_STATE_PLAY)
856 self["SeekActions"].setEnabled(True)
859 def __serviceStarted(self):
860 self.fast_winding_hint_message_showed = False
861 self.seekstate = self.SEEK_STATE_PLAY
862 self.__seekableStatusChanged()
864 def setSeekState(self, state):
865 service = self.session.nav.getCurrentService()
870 if not self.isSeekable():
871 if state not in (self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE):
872 state = self.SEEK_STATE_PLAY
874 pauseable = service.pause()
876 if pauseable is None:
877 print "not pauseable."
878 state = self.SEEK_STATE_PLAY
880 self.seekstate = state
882 if pauseable is not None:
883 if self.seekstate[0]:
884 print "resolved to PAUSE"
886 elif self.seekstate[1]:
887 print "resolved to FAST FORWARD"
888 pauseable.setFastForward(self.seekstate[1])
889 elif self.seekstate[2]:
890 print "resolved to SLOW MOTION"
891 pauseable.setSlowMotion(self.seekstate[2])
893 print "resolved to PLAY"
896 for c in self.onPlayStateChanged:
899 self.checkSkipShowHideLock()
903 def playpauseService(self):
904 if self.seekstate != self.SEEK_STATE_PLAY:
905 self.unPauseService()
909 def pauseService(self):
910 if self.seekstate == self.SEEK_STATE_PAUSE:
911 if config.seek.on_pause.value == "play":
912 self.unPauseService()
913 elif config.seek.on_pause.value == "step":
914 self.doSeekRelative(1)
915 elif config.seek.on_pause.value == "last":
916 self.setSeekState(self.lastseekstate)
917 self.lastseekstate = self.SEEK_STATE_PLAY
919 if self.seekstate != self.SEEK_STATE_EOF:
920 self.lastseekstate = self.seekstate
921 self.setSeekState(self.SEEK_STATE_PAUSE);
923 def unPauseService(self):
925 if self.seekstate == self.SEEK_STATE_PLAY:
927 self.setSeekState(self.SEEK_STATE_PLAY)
929 def doSeek(self, pts):
930 seekable = self.getSeek()
935 def doSeekRelative(self, pts):
936 seekable = self.getSeek()
939 prevstate = self.seekstate
941 if self.seekstate == self.SEEK_STATE_EOF:
942 if prevstate == self.SEEK_STATE_PAUSE:
943 self.setSeekState(self.SEEK_STATE_PAUSE)
945 self.setSeekState(self.SEEK_STATE_PLAY)
946 seekable.seekRelative(pts<0 and -1 or 1, abs(pts))
947 if abs(pts) > 100 and config.usage.show_infobar_on_skip.value:
951 seek = self.getSeek()
952 if seek and not (seek.isCurrentlySeekable() & 2):
953 if not self.fast_winding_hint_message_showed and (seek.isCurrentlySeekable() & 1):
954 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)
955 self.fast_winding_hint_message_showed = True
957 return 0 # trade as unhandled action
958 if self.seekstate == self.SEEK_STATE_PLAY:
959 self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
960 elif self.seekstate == self.SEEK_STATE_PAUSE:
961 if len(config.seek.speeds_slowmotion.value):
962 self.setSeekState(self.makeStateSlowMotion(config.seek.speeds_slowmotion.value[-1]))
964 self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
965 elif self.seekstate == self.SEEK_STATE_EOF:
967 elif self.isStateForward(self.seekstate):
968 speed = self.seekstate[1]
969 if self.seekstate[2]:
970 speed /= self.seekstate[2]
971 speed = self.getHigher(speed, config.seek.speeds_forward.value) or config.seek.speeds_forward.value[-1]
972 self.setSeekState(self.makeStateForward(speed))
973 elif self.isStateBackward(self.seekstate):
974 speed = -self.seekstate[1]
975 if self.seekstate[2]:
976 speed /= self.seekstate[2]
977 speed = self.getLower(speed, config.seek.speeds_backward.value)
979 self.setSeekState(self.makeStateBackward(speed))
981 self.setSeekState(self.SEEK_STATE_PLAY)
982 elif self.isStateSlowMotion(self.seekstate):
983 speed = self.getLower(self.seekstate[2], config.seek.speeds_slowmotion.value) or config.seek.speeds_slowmotion.value[0]
984 self.setSeekState(self.makeStateSlowMotion(speed))
987 seek = self.getSeek()
988 if seek and not (seek.isCurrentlySeekable() & 2):
989 if not self.fast_winding_hint_message_showed and (seek.isCurrentlySeekable() & 1):
990 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)
991 self.fast_winding_hint_message_showed = True
993 return 0 # trade as unhandled action
994 seekstate = self.seekstate
995 if seekstate == self.SEEK_STATE_PLAY:
996 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
997 elif seekstate == self.SEEK_STATE_EOF:
998 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
999 self.doSeekRelative(-6)
1000 elif seekstate == self.SEEK_STATE_PAUSE:
1001 self.doSeekRelative(-1)
1002 elif self.isStateForward(seekstate):
1003 speed = seekstate[1]
1005 speed /= seekstate[2]
1006 speed = self.getLower(speed, config.seek.speeds_forward.value)
1008 self.setSeekState(self.makeStateForward(speed))
1010 self.setSeekState(self.SEEK_STATE_PLAY)
1011 elif self.isStateBackward(seekstate):
1012 speed = -seekstate[1]
1014 speed /= seekstate[2]
1015 speed = self.getHigher(speed, config.seek.speeds_backward.value) or config.seek.speeds_backward.value[-1]
1016 self.setSeekState(self.makeStateBackward(speed))
1017 elif self.isStateSlowMotion(seekstate):
1018 speed = self.getHigher(seekstate[2], config.seek.speeds_slowmotion.value)
1020 self.setSeekState(self.makeStateSlowMotion(speed))
1022 self.setSeekState(self.SEEK_STATE_PAUSE)
1024 def seekFwdManual(self):
1025 self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
1027 def fwdSeekTo(self, minutes):
1028 print "Seek", minutes, "minutes forward"
1029 self.doSeekRelative(minutes * 60 * 90000)
1031 def seekBackManual(self):
1032 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
1034 def rwdSeekTo(self, minutes):
1036 self.doSeekRelative(-minutes * 60 * 90000)
1038 def checkSkipShowHideLock(self):
1039 wantlock = self.seekstate != self.SEEK_STATE_PLAY
1041 if config.usage.show_infobar_on_skip.value:
1042 if self.lockedBecauseOfSkipping and not wantlock:
1044 self.lockedBecauseOfSkipping = False
1046 if wantlock and not self.lockedBecauseOfSkipping:
1048 self.lockedBecauseOfSkipping = True
1050 def calcRemainingTime(self):
1051 seekable = self.getSeek()
1052 if seekable is not None:
1053 len = seekable.getLength()
1055 tmp = self.cueGetEndCutPosition()
1060 pos = seekable.getPlayPosition()
1061 speednom = self.seekstate[1] or 1
1062 speedden = self.seekstate[2] or 1
1063 if not len[0] and not pos[0]:
1064 if len[1] <= pos[1]:
1066 time = (len[1] - pos[1])*speedden/(90*speednom)
1071 if self.seekstate == self.SEEK_STATE_EOF:
1074 # if we are seeking forward, we try to end up ~1s before the end, and pause there.
1075 seekstate = self.seekstate
1076 if self.seekstate != self.SEEK_STATE_PAUSE:
1077 self.setSeekState(self.SEEK_STATE_EOF)
1079 if seekstate not in (self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE): # if we are seeking
1080 seekable = self.getSeek()
1081 if seekable is not None:
1083 if seekstate == self.SEEK_STATE_PLAY: # regular EOF
1084 self.doEofInternal(True)
1086 self.doEofInternal(False)
1088 def doEofInternal(self, playing):
1089 pass # Defined in subclasses
1092 self.setSeekState(self.SEEK_STATE_PLAY)
1095 from Screens.PVRState import PVRState, TimeshiftState
1097 class InfoBarPVRState:
1098 def __init__(self, screen=PVRState, force_show = False):
1099 self.onPlayStateChanged.append(self.__playStateChanged)
1100 self.pvrStateDialog = self.session.instantiateDialog(screen)
1101 self.onShow.append(self._mayShow)
1102 self.onHide.append(self.pvrStateDialog.hide)
1103 self.force_show = force_show
1106 if self.execing and self.seekstate != self.SEEK_STATE_PLAY:
1107 self.pvrStateDialog.show()
1109 def __playStateChanged(self, state):
1110 playstateString = state[3]
1111 self.pvrStateDialog["state"].setText(playstateString)
1113 # if we return into "PLAY" state, ensure that the dialog gets hidden if there will be no infobar displayed
1114 if not config.usage.show_infobar_on_skip.value and self.seekstate == self.SEEK_STATE_PLAY and not self.force_show:
1115 self.pvrStateDialog.hide()
1120 class InfoBarTimeshiftState(InfoBarPVRState):
1122 InfoBarPVRState.__init__(self, screen=TimeshiftState, force_show = True)
1125 if self.execing and self.timeshift_enabled and self.seekstate != self.SEEK_STATE_PLAY:
1126 self.pvrStateDialog.show()
1128 class InfoBarShowMovies:
1130 # i don't really like this class.
1131 # it calls a not further specified "movie list" on up/down/movieList,
1132 # so this is not more than an action map
1134 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions",
1136 "movieList": (self.showMovies, _("movie list")),
1137 "up": (self.showMovies, _("movie list")),
1138 "down": (self.showMovies, _("movie list"))
1141 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
1145 # Timeshift works the following way:
1146 # demux0 demux1 "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
1147 # - normal playback TUNER unused PLAY enable disable disable
1148 # - user presses "yellow" button. FILE record PAUSE enable disable enable
1149 # - user presess pause again FILE record PLAY enable disable enable
1150 # - user fast forwards FILE record FF enable disable enable
1151 # - end of timeshift buffer reached TUNER record PLAY enable enable disable
1152 # - user backwards FILE record BACK # !! enable disable enable
1156 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
1157 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
1158 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
1159 # - the user can now PVR around
1160 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
1161 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
1163 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
1164 # - if the user rewinds, or press pause, timeshift will be activated again
1166 # note that a timeshift can be enabled ("recording") and
1167 # activated (currently time-shifting).
1169 class InfoBarTimeshift:
1171 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions",
1173 "timeshiftStart": (self.startTimeshift, _("start timeshift")), # the "yellow key"
1174 "timeshiftStop": (self.stopTimeshift, _("stop timeshift")) # currently undefined :), probably 'TV'
1176 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
1178 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "rewind key"
1179 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause # something like "pause key"
1180 }, prio=-1) # priority over record
1182 self.timeshift_enabled = 0
1183 self.timeshift_state = 0
1184 self.ts_rewind_timer = eTimer()
1185 self.ts_rewind_timer.callback.append(self.rewindService)
1187 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1189 iPlayableService.evStart: self.__serviceStarted,
1190 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged
1193 def getTimeshift(self):
1194 service = self.session.nav.getCurrentService()
1195 return service and service.timeshift()
1197 def startTimeshift(self):
1198 print "enable timeshift"
1199 ts = self.getTimeshift()
1201 self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
1202 print "no ts interface"
1205 if self.timeshift_enabled:
1206 print "hu, timeshift already enabled?"
1208 if not ts.startTimeshift():
1209 self.timeshift_enabled = 1
1211 # we remove the "relative time" for now.
1212 #self.pvrStateDialog["timeshift"].setRelative(time.time())
1215 #self.setSeekState(self.SEEK_STATE_PAUSE)
1216 self.activateTimeshiftEnd(False)
1218 # enable the "TimeshiftEnableActions", which will override
1219 # the startTimeshift actions
1220 self.__seekableStatusChanged()
1222 print "timeshift failed"
1224 def stopTimeshift(self):
1225 if not self.timeshift_enabled:
1227 print "disable timeshift"
1228 ts = self.getTimeshift()
1231 self.session.openWithCallback(self.stopTimeshiftConfirmed, MessageBox, _("Stop Timeshift?"), MessageBox.TYPE_YESNO)
1233 def stopTimeshiftConfirmed(self, confirmed):
1237 ts = self.getTimeshift()
1242 self.timeshift_enabled = 0
1245 self.__seekableStatusChanged()
1247 # activates timeshift, and seeks to (almost) the end
1248 def activateTimeshiftEnd(self, back = True):
1249 ts = self.getTimeshift()
1250 print "activateTimeshiftEnd"
1255 if ts.isTimeshiftActive():
1256 print "!! activate timeshift called - but shouldn't this be a normal pause?"
1260 ts.activateTimeshift() # activate timeshift will automatically pause
1261 self.setSeekState(self.SEEK_STATE_PAUSE)
1264 self.ts_rewind_timer.start(200, 1)
1266 def rewindService(self):
1267 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1269 # same as activateTimeshiftEnd, but pauses afterwards.
1270 def activateTimeshiftEndAndPause(self):
1271 print "activateTimeshiftEndAndPause"
1272 #state = self.seekstate
1273 self.activateTimeshiftEnd(False)
1275 def __seekableStatusChanged(self):
1278 # print "self.isSeekable", self.isSeekable()
1279 # print "self.timeshift_enabled", self.timeshift_enabled
1281 # when this service is not seekable, but timeshift
1282 # is enabled, this means we can activate
1284 if not self.isSeekable() and self.timeshift_enabled:
1287 # print "timeshift activate:", enabled
1288 self["TimeshiftActivateActions"].setEnabled(enabled)
1290 def __serviceStarted(self):
1291 self.timeshift_enabled = False
1292 self.__seekableStatusChanged()
1294 from Screens.PiPSetup import PiPSetup
1296 class InfoBarExtensions:
1297 EXTENSION_SINGLE = 0
1303 self["InstantExtensionsActions"] = HelpableActionMap(self, "InfobarExtensions",
1305 "extensions": (self.showExtensionSelection, _("view extensions...")),
1306 }, 1) # lower priority
1308 def addExtension(self, extension, key = None, type = EXTENSION_SINGLE):
1309 self.list.append((type, extension, key))
1311 def updateExtension(self, extension, key = None):
1312 self.extensionsList.append(extension)
1314 if self.extensionKeys.has_key(key):
1318 for x in self.availableKeys:
1319 if not self.extensionKeys.has_key(x):
1324 self.extensionKeys[key] = len(self.extensionsList) - 1
1326 def updateExtensions(self):
1327 self.extensionsList = []
1328 self.availableKeys = [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "red", "green", "yellow", "blue" ]
1329 self.extensionKeys = {}
1331 if x[0] == self.EXTENSION_SINGLE:
1332 self.updateExtension(x[1], x[2])
1335 self.updateExtension(y[0], y[1])
1338 def showExtensionSelection(self):
1339 self.updateExtensions()
1340 extensionsList = self.extensionsList[:]
1343 for x in self.availableKeys:
1344 if self.extensionKeys.has_key(x):
1345 entry = self.extensionKeys[x]
1346 extension = self.extensionsList[entry]
1348 name = str(extension[0]())
1349 list.append((extension[0](), extension))
1351 extensionsList.remove(extension)
1353 extensionsList.remove(extension)
1354 list.extend([(x[0](), x) for x in extensionsList])
1356 keys += [""] * len(extensionsList)
1357 self.session.openWithCallback(self.extensionCallback, ChoiceBox, title=_("Please choose an extension..."), list = list, keys = keys, skin_name = "ExtensionsList")
1359 def extensionCallback(self, answer):
1360 if answer is not None:
1363 from Tools.BoundFunction import boundFunction
1365 # depends on InfoBarExtensions
1367 class InfoBarPlugins:
1369 self.addExtension(extension = self.getPluginList, type = InfoBarExtensions.EXTENSION_LIST)
1371 def getPluginName(self, name):
1374 def getPluginList(self):
1375 list = [((boundFunction(self.getPluginName, p.name), boundFunction(self.runPlugin, p), lambda: True), None, p.name) for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EXTENSIONSMENU)]
1376 list.sort(key = lambda e: e[2]) # sort by name
1379 def runPlugin(self, plugin):
1380 if isinstance(self, InfoBarChannelSelection):
1381 plugin(session = self.session, servicelist = self.servicelist)
1383 plugin(session = self.session)
1385 from Components.Task import job_manager
1386 class InfoBarJobman:
1388 self.addExtension(extension = self.getJobList, type = InfoBarExtensions.EXTENSION_LIST)
1390 def getJobList(self):
1391 return [((boundFunction(self.getJobName, job), boundFunction(self.showJobView, job), lambda: True), None) for job in job_manager.getPendingJobs()]
1393 def getJobName(self, job):
1394 return "%s: %s (%d%%)" % (job.getStatustext(), job.name, int(100*job.progress/float(job.end)))
1396 def showJobView(self, job):
1397 from Screens.TaskView import JobView
1398 job_manager.in_background = False
1399 self.session.openWithCallback(self.JobViewCB, JobView, job)
1401 def JobViewCB(self, in_background):
1402 job_manager.in_background = in_background
1404 # depends on InfoBarExtensions
1408 self.session.pipshown
1410 self.session.pipshown = False
1411 if SystemInfo.get("NumVideoDecoders", 1) > 1:
1413 self.addExtension((self.getShowHideName, self.showPiP, lambda: True), "blue")
1414 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1415 self.addExtension((self.getSwapName, self.swapPiP, self.pipShown), "yellow")
1417 self.addExtension((self.getShowHideName, self.showPiP, self.pipShown), "blue")
1418 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1421 return self.session.pipshown
1423 def pipHandles0Action(self):
1424 return self.pipShown() and config.usage.pip_zero_button.value != "standard"
1426 def getShowHideName(self):
1427 if self.session.pipshown:
1428 return _("Disable Picture in Picture")
1430 return _("Activate Picture in Picture")
1432 def getSwapName(self):
1433 return _("Swap Services")
1435 def getMoveName(self):
1436 return _("Move Picture in Picture")
1439 if self.session.pipshown:
1440 del self.session.pip
1441 self.session.pipshown = False
1443 self.session.pip = self.session.instantiateDialog(PictureInPicture)
1444 self.session.pip.show()
1445 newservice = self.session.nav.getCurrentlyPlayingServiceReference()
1446 if self.session.pip.playService(newservice):
1447 self.session.pipshown = True
1448 self.session.pip.servicePath = self.servicelist.getCurrentServicePath()
1450 self.session.pipshown = False
1451 del self.session.pip
1452 self.session.nav.playService(newservice)
1455 swapservice = self.session.nav.getCurrentlyPlayingServiceReference()
1456 if self.session.pip.servicePath:
1457 servicepath = self.servicelist.getCurrentServicePath()
1458 ref=servicepath[len(servicepath)-1]
1459 pipref=self.session.pip.getCurrentService()
1460 self.session.pip.playService(swapservice)
1461 self.servicelist.setCurrentServicePath(self.session.pip.servicePath)
1462 if pipref.toString() != ref.toString(): # is a subservice ?
1463 self.session.nav.stopService() # stop portal
1464 self.session.nav.playService(pipref) # start subservice
1465 self.session.pip.servicePath=servicepath
1468 self.session.open(PiPSetup, pip = self.session.pip)
1470 def pipDoHandle0Action(self):
1471 use = config.usage.pip_zero_button.value
1474 elif "swapstop" == use:
1480 from RecordTimer import parseEvent, RecordTimerEntry
1482 class InfoBarInstantRecord:
1483 """Instant Record - handles the instantRecord action in order to
1484 start/stop instant records"""
1486 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
1488 "instantRecord": (self.instantRecord, _("Instant Record...")),
1492 def stopCurrentRecording(self, entry = -1):
1493 if entry is not None and entry != -1:
1494 self.session.nav.RecordTimer.removeEntry(self.recording[entry])
1495 self.recording.remove(self.recording[entry])
1497 def startInstantRecording(self, limitEvent = False):
1498 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
1500 # try to get event info
1503 service = self.session.nav.getCurrentService()
1504 epg = eEPGCache.getInstance()
1505 event = epg.lookupEventTime(serviceref, -1, 0)
1507 info = service.info()
1508 ev = info.getEvent(0)
1514 end = begin + 3600 # dummy
1515 name = "instant record"
1519 if event is not None:
1520 curEvent = parseEvent(event)
1522 description = curEvent[3]
1523 eventid = curEvent[4]
1528 self.session.open(MessageBox, _("No event info found, recording indefinitely."), MessageBox.TYPE_INFO)
1530 if isinstance(serviceref, eServiceReference):
1531 serviceref = ServiceReference(serviceref)
1533 recording = RecordTimerEntry(serviceref, begin, end, name, description, eventid, dirname = preferredInstantRecordPath())
1534 recording.dontSave = True
1536 if event is None or limitEvent == False:
1537 recording.autoincrease = True
1538 if recording.setAutoincreaseEnd():
1539 self.session.nav.RecordTimer.record(recording)
1540 self.recording.append(recording)
1542 simulTimerList = self.session.nav.RecordTimer.record(recording)
1543 if simulTimerList is not None: # conflict with other recording
1544 name = simulTimerList[1].name
1545 name_date = ' '.join((name, strftime('%c', localtime(simulTimerList[1].begin))))
1546 print "[TIMER] conflicts with", name_date
1547 recording.autoincrease = True # start with max available length, then increment
1548 if recording.setAutoincreaseEnd():
1549 self.session.nav.RecordTimer.record(recording)
1550 self.recording.append(recording)
1551 self.session.open(MessageBox, _("Record time limited due to conflicting timer %s") % name_date, MessageBox.TYPE_INFO)
1553 self.session.open(MessageBox, _("Couldn't record due to conflicting timer %s") % name, MessageBox.TYPE_INFO)
1554 recording.autoincrease = False
1556 self.recording.append(recording)
1558 def isInstantRecordRunning(self):
1559 print "self.recording:", self.recording
1561 for x in self.recording:
1566 def recordQuestionCallback(self, answer):
1567 print "pre:\n", self.recording
1569 if answer is None or answer[1] == "no":
1572 recording = self.recording[:]
1574 if not x in self.session.nav.RecordTimer.timer_list:
1575 self.recording.remove(x)
1576 elif x.dontSave and x.isRunning():
1577 list.append((x, False))
1579 if answer[1] == "changeduration":
1580 if len(self.recording) == 1:
1581 self.changeDuration(0)
1583 self.session.openWithCallback(self.changeDuration, TimerSelection, list)
1584 elif answer[1] == "changeendtime":
1585 if len(self.recording) == 1:
1588 self.session.openWithCallback(self.setEndtime, TimerSelection, list)
1589 elif answer[1] == "stop":
1590 if len(self.recording) == 1:
1591 self.stopCurrentRecording(0)
1593 self.session.openWithCallback(self.stopCurrentRecording, TimerSelection, list)
1594 elif answer[1] in ( "indefinitely" , "manualduration", "manualendtime", "event"):
1595 self.startInstantRecording(limitEvent = answer[1] in ("event", "manualendtime") or False)
1596 if answer[1] == "manualduration":
1597 self.changeDuration(len(self.recording)-1)
1598 elif answer[1] == "manualendtime":
1599 self.setEndtime(len(self.recording)-1)
1600 print "after:\n", self.recording
1602 def setEndtime(self, entry):
1603 if entry is not None and entry >= 0:
1604 self.selectedEntry = entry
1605 self.endtime=ConfigClock(default = self.recording[self.selectedEntry].end)
1606 dlg = self.session.openWithCallback(self.TimeDateInputClosed, TimeDateInput, self.endtime)
1607 dlg.setTitle(_("Please change recording endtime"))
1609 def TimeDateInputClosed(self, ret):
1612 localendtime = localtime(ret[1])
1613 print "stopping recording at", strftime("%c", localendtime)
1614 if self.recording[self.selectedEntry].end != ret[1]:
1615 self.recording[self.selectedEntry].autoincrease = False
1616 self.recording[self.selectedEntry].end = ret[1]
1617 self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
1619 def changeDuration(self, entry):
1620 if entry is not None and entry >= 0:
1621 self.selectedEntry = entry
1622 self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1624 def inputCallback(self, value):
1625 if value is not None:
1626 print "stopping recording after", int(value), "minutes."
1627 entry = self.recording[self.selectedEntry]
1629 entry.autoincrease = False
1630 entry.end = int(time()) + 60 * int(value)
1631 self.session.nav.RecordTimer.timeChanged(entry)
1633 def instantRecord(self):
1634 dir = preferredInstantRecordPath()
1635 if not dir or not fileExists(dir, 'w'):
1636 dir = defaultMoviePath()
1640 # XXX: this message is a little odd as we might be recording to a remote device
1641 self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1644 if self.isInstantRecordRunning():
1645 self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1646 title=_("A recording is currently running.\nWhat do you want to do?"), \
1647 list=((_("stop recording"), "stop"), \
1648 (_("add recording (stop after current event)"), "event"), \
1649 (_("add recording (indefinitely)"), "indefinitely"), \
1650 (_("add recording (enter recording duration)"), "manualduration"), \
1651 (_("add recording (enter recording endtime)"), "manualendtime"), \
1652 (_("change recording (duration)"), "changeduration"), \
1653 (_("change recording (endtime)"), "changeendtime"), \
1654 (_("do nothing"), "no")))
1656 self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1657 title=_("Start recording?"), \
1658 list=((_("add recording (stop after current event)"), "event"), \
1659 (_("add recording (indefinitely)"), "indefinitely"), \
1660 (_("add recording (enter recording duration)"), "manualduration"), \
1661 (_("add recording (enter recording endtime)"), "manualendtime"), \
1662 (_("don't record"), "no")))
1664 from Tools.ISO639 import LanguageCodes
1666 class InfoBarAudioSelection:
1668 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions",
1670 "audioSelection": (self.audioSelection, _("Audio Options...")),
1673 def audioSelection(self):
1674 service = self.session.nav.getCurrentService()
1675 self.audioTracks = audio = service and service.audioTracks()
1676 n = audio and audio.getNumberOfTracks() or 0
1679 self.audioChannel = service.audioChannel()
1684 i = audio.getTrackInfo(idx)
1685 languages = i.getLanguage().split('/')
1686 description = i.getDescription()
1689 for lang in languages:
1692 if LanguageCodes.has_key(lang):
1693 language += LanguageCodes[lang][0]
1698 if len(description):
1699 description += " (" + language + ")"
1701 description = language
1703 tlist.append((description, idx))
1706 tlist.sort(key=lambda x: x[0])
1708 selectedAudio = self.audioTracks.getCurrentTrack()
1713 if x[1] != selectedAudio:
1721 if SystemInfo["CanDownmixAC3"]:
1722 flist = [(_("AC3 downmix") + " - " +(_("Off"), _("On"))[config.av.downmix_ac3.value and 1 or 0], "CALLFUNC", self.changeAC3Downmix),
1723 ((_("Left"), _("Stereo"), _("Right"))[self.audioChannel.getCurrentChannel()], "mode")]
1724 usedKeys.extend(["red", "green"])
1725 availableKeys.extend(["yellow", "blue"])
1728 flist = [((_("Left"), _("Stereo"), _("Right"))[self.audioChannel.getCurrentChannel()], "mode")]
1729 usedKeys.extend(["red"])
1730 availableKeys.extend(["green", "yellow", "blue"])
1733 if hasattr(self, "runPlugin"):
1735 def __init__(self, fnc, *args):
1738 def __call__(self, *args, **kwargs):
1739 self.fnc(*self.args)
1741 Plugins = [ (p.name, PluginCaller(self.runPlugin, p)) for p in plugins.getPlugins(where = PluginDescriptor.WHERE_AUDIOMENU) ]
1745 flist.append((p[0], "CALLFUNC", p[1]))
1747 usedKeys.append(availableKeys[0])
1748 del availableKeys[0]
1752 flist.append(("--", ""))
1756 keys = usedKeys + [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "0" ] + [""] * n
1757 self.session.openWithCallback(self.audioSelected, ChoiceBox, title=_("Select audio track"), list = flist + tlist, selection = selection, keys = keys, skin_name = "AudioTrackSelection")
1759 del self.audioTracks
1761 def changeAC3Downmix(self, arg):
1762 choicelist = self.session.current_dialog["list"]
1763 list = choicelist.list
1765 list[0][1]=(t[0], t[1], t[2], t[3], t[4], t[5], t[6],
1766 _("AC3 downmix") + " - " + (_("On"), _("Off"))[config.av.downmix_ac3.value and 1 or 0])
1767 choicelist.setList(list)
1768 if config.av.downmix_ac3.value:
1769 config.av.downmix_ac3.value = False
1771 config.av.downmix_ac3.value = True
1772 config.av.downmix_ac3.save()
1774 def audioSelected(self, audio):
1775 if audio is not None:
1776 if isinstance(audio[1], str):
1777 if audio[1] == "mode":
1778 keys = ["red", "green", "yellow"]
1779 selection = self.audioChannel.getCurrentChannel()
1780 tlist = ((_("left"), 0), (_("stereo"), 1), (_("right"), 2))
1781 self.session.openWithCallback(self.modeSelected, ChoiceBox, title=_("Select audio mode"), list = tlist, selection = selection, keys = keys, skin_name ="AudioModeSelection")
1783 del self.audioChannel
1784 if self.session.nav.getCurrentService().audioTracks().getNumberOfTracks() > audio[1]:
1785 self.audioTracks.selectTrack(audio[1])
1787 del self.audioChannel
1788 del self.audioTracks
1790 def modeSelected(self, mode):
1791 if mode is not None:
1792 self.audioChannel.selectChannel(mode[1])
1793 del self.audioChannel
1795 class InfoBarSubserviceSelection:
1797 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1799 "subserviceSelection": (self.subserviceSelection, _("Subservice list...")),
1802 self["SubserviceQuickzapAction"] = HelpableActionMap(self, "InfobarSubserviceQuickzapActions",
1804 "nextSubservice": (self.nextSubservice, _("Switch to next subservice")),
1805 "prevSubservice": (self.prevSubservice, _("Switch to previous subservice"))
1807 self["SubserviceQuickzapAction"].setEnabled(False)
1809 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1811 iPlayableService.evUpdatedEventInfo: self.checkSubservicesAvail
1816 def checkSubservicesAvail(self):
1817 service = self.session.nav.getCurrentService()
1818 subservices = service and service.subServices()
1819 if not subservices or subservices.getNumberOfSubservices() == 0:
1820 self["SubserviceQuickzapAction"].setEnabled(False)
1822 def nextSubservice(self):
1823 self.changeSubservice(+1)
1825 def prevSubservice(self):
1826 self.changeSubservice(-1)
1828 def changeSubservice(self, direction):
1829 service = self.session.nav.getCurrentService()
1830 subservices = service and service.subServices()
1831 n = subservices and subservices.getNumberOfSubservices()
1834 ref = self.session.nav.getCurrentlyPlayingServiceReference()
1837 if subservices.getSubservice(idx).toString() == ref.toString():
1842 selection += direction
1847 newservice = subservices.getSubservice(selection)
1848 if newservice.valid():
1851 self.session.nav.playService(newservice, False)
1853 def subserviceSelection(self):
1854 service = self.session.nav.getCurrentService()
1855 subservices = service and service.subServices()
1856 self.bouquets = self.servicelist.getBouquetList()
1857 n = subservices and subservices.getNumberOfSubservices()
1860 ref = self.session.nav.getCurrentlyPlayingServiceReference()
1864 i = subservices.getSubservice(idx)
1865 if i.toString() == ref.toString():
1867 tlist.append((i.getName(), i))
1870 if self.bouquets and len(self.bouquets):
1871 keys = ["red", "blue", "", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1872 if config.usage.multibouquet.value:
1873 tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to bouquet"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1875 tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to favourites"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1878 tlist = [(_("Quickzap"), "quickzap", service.subServices()), ("--", "")] + tlist
1879 keys = ["red", "", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1882 self.session.openWithCallback(self.subserviceSelected, ChoiceBox, title=_("Please select a subservice..."), list = tlist, selection = selection, keys = keys, skin_name = "SubserviceSelection")
1884 def subserviceSelected(self, service):
1886 if not service is None:
1887 if isinstance(service[1], str):
1888 if service[1] == "quickzap":
1889 from Screens.SubservicesQuickzap import SubservicesQuickzap
1890 self.session.open(SubservicesQuickzap, service[2])
1892 self["SubserviceQuickzapAction"].setEnabled(True)
1893 self.session.nav.playService(service[1], False)
1895 def addSubserviceToBouquetCallback(self, service):
1896 if len(service) > 1 and isinstance(service[1], eServiceReference):
1897 self.selectedSubservice = service
1898 if self.bouquets is None:
1901 cnt = len(self.bouquets)
1902 if cnt > 1: # show bouquet list
1903 self.bsel = self.session.openWithCallback(self.bouquetSelClosed, BouquetSelector, self.bouquets, self.addSubserviceToBouquet)
1904 elif cnt == 1: # add to only one existing bouquet
1905 self.addSubserviceToBouquet(self.bouquets[0][1])
1906 self.session.open(MessageBox, _("Service has been added to the favourites."), MessageBox.TYPE_INFO)
1908 def bouquetSelClosed(self, confirmed):
1910 del self.selectedSubservice
1912 self.session.open(MessageBox, _("Service has been added to the selected bouquet."), MessageBox.TYPE_INFO)
1914 def addSubserviceToBouquet(self, dest):
1915 self.servicelist.addServiceToBouquet(dest, self.selectedSubservice[1])
1917 self.bsel.close(True)
1919 del self.selectedSubservice
1921 class InfoBarAdditionalInfo:
1924 self["RecordingPossible"] = Boolean(fixed=harddiskmanager.HDDCount() > 0 and config.misc.rcused.value == 1)
1925 self["TimeshiftPossible"] = self["RecordingPossible"]
1926 self["ShowTimeshiftOnYellow"] = Boolean(fixed=(not config.misc.rcused.value == 0))
1927 self["ShowAudioOnYellow"] = Boolean(fixed=config.misc.rcused.value == 0)
1928 self["ShowRecordOnRed"] = Boolean(fixed=config.misc.rcused.value == 1)
1929 self["ExtensionsAvailable"] = Boolean(fixed=1)
1931 class InfoBarNotifications:
1933 self.onExecBegin.append(self.checkNotifications)
1934 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1935 self.onClose.append(self.__removeNotification)
1937 def __removeNotification(self):
1938 Notifications.notificationAdded.remove(self.checkNotificationsIfExecing)
1940 def checkNotificationsIfExecing(self):
1942 self.checkNotifications()
1944 def checkNotifications(self):
1945 notifications = Notifications.notifications
1947 n = notifications[0]
1949 del notifications[0]
1952 if n[3].has_key("onSessionOpenCallback"):
1953 n[3]["onSessionOpenCallback"]()
1954 del n[3]["onSessionOpenCallback"]
1957 dlg = self.session.openWithCallback(cb, n[1], *n[2], **n[3])
1959 dlg = self.session.open(n[1], *n[2], **n[3])
1961 # remember that this notification is currently active
1963 Notifications.current_notifications.append(d)
1964 dlg.onClose.append(boundFunction(self.__notificationClosed, d))
1966 def __notificationClosed(self, d):
1967 Notifications.current_notifications.remove(d)
1969 class InfoBarServiceNotifications:
1971 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1973 iPlayableService.evEnd: self.serviceHasEnded
1976 def serviceHasEnded(self):
1977 print "service end!"
1980 self.setSeekState(self.SEEK_STATE_PLAY)
1984 class InfoBarCueSheetSupport:
1990 ENABLE_RESUME_SUPPORT = False
1992 def __init__(self, actionmap = "InfobarCueSheetActions"):
1993 self["CueSheetActions"] = HelpableActionMap(self, actionmap,
1995 "jumpPreviousMark": (self.jumpPreviousMark, _("jump to previous marked position")),
1996 "jumpNextMark": (self.jumpNextMark, _("jump to next marked position")),
1997 "toggleMark": (self.toggleMark, _("toggle a cut mark at the current position"))
2001 self.is_closing = False
2002 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2004 iPlayableService.evStart: self.__serviceStarted,
2007 def __serviceStarted(self):
2010 print "new service started! trying to download cuts!"
2011 self.downloadCuesheet()
2013 if self.ENABLE_RESUME_SUPPORT:
2016 for (pts, what) in self.cut_list:
2017 if what == self.CUT_TYPE_LAST:
2020 if last is not None:
2021 self.resume_point = last
2022 if config.usage.on_movie_start.value == "ask":
2023 Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Do you want to resume this playback?"), timeout=10)
2024 elif config.usage.on_movie_start.value == "resume":
2025 # TRANSLATORS: The string "Resuming playback" flashes for a moment
2026 # TRANSLATORS: at the start of a movie, when the user has selected
2027 # TRANSLATORS: "Resume from last position" as start behavior.
2028 # TRANSLATORS: The purpose is to notify the user that the movie starts
2029 # TRANSLATORS: in the middle somewhere and not from the beginning.
2030 # TRANSLATORS: (Some translators seem to have interpreted it as a
2031 # TRANSLATORS: question or a choice, but it is a statement.)
2032 Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Resuming playback"), timeout=2, type=MessageBox.TYPE_INFO)
2034 def playLastCB(self, answer):
2036 self.doSeek(self.resume_point)
2037 self.hideAfterResume()
2039 def hideAfterResume(self):
2040 if isinstance(self, InfoBarShowHide):
2043 def __getSeekable(self):
2044 service = self.session.nav.getCurrentService()
2047 return service.seek()
2049 def cueGetCurrentPosition(self):
2050 seek = self.__getSeekable()
2053 r = seek.getPlayPosition()
2058 def cueGetEndCutPosition(self):
2061 for cp in self.cut_list:
2062 if cp[1] == self.CUT_TYPE_OUT:
2066 elif cp[1] == self.CUT_TYPE_IN:
2070 def jumpPreviousNextMark(self, cmp, start=False):
2071 current_pos = self.cueGetCurrentPosition()
2072 if current_pos is None:
2074 mark = self.getNearestCutPoint(current_pos, cmp=cmp, start=start)
2075 if mark is not None:
2083 def jumpPreviousMark(self):
2084 # we add 2 seconds, so if the play position is <2s after
2085 # the mark, the mark before will be used
2086 self.jumpPreviousNextMark(lambda x: -x-5*90000, start=True)
2088 def jumpNextMark(self):
2089 if not self.jumpPreviousNextMark(lambda x: x):
2092 def getNearestCutPoint(self, pts, cmp=abs, start=False):
2098 bestdiff = cmp(0 - pts)
2100 nearest = [0, False]
2101 for cp in self.cut_list:
2102 if beforecut and cp[1] in (self.CUT_TYPE_IN, self.CUT_TYPE_OUT):
2104 if cp[1] == self.CUT_TYPE_IN: # Start is here, disregard previous marks
2105 diff = cmp(cp[0] - pts)
2111 if cp[1] in (self.CUT_TYPE_MARK, self.CUT_TYPE_LAST):
2112 diff = cmp(cp[0] - pts)
2113 if diff >= 0 and (nearest is None or bestdiff > diff):
2118 def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
2119 current_pos = self.cueGetCurrentPosition()
2120 if current_pos is None:
2121 print "not seekable"
2124 nearest_cutpoint = self.getNearestCutPoint(current_pos)
2126 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
2128 return nearest_cutpoint
2130 self.removeMark(nearest_cutpoint)
2131 elif not onlyremove and not onlyreturn:
2132 self.addMark((current_pos, self.CUT_TYPE_MARK))
2137 def addMark(self, point):
2138 insort(self.cut_list, point)
2139 self.uploadCuesheet()
2140 self.showAfterCuesheetOperation()
2142 def removeMark(self, point):
2143 self.cut_list.remove(point)
2144 self.uploadCuesheet()
2145 self.showAfterCuesheetOperation()
2147 def showAfterCuesheetOperation(self):
2148 if isinstance(self, InfoBarShowHide):
2151 def __getCuesheet(self):
2152 service = self.session.nav.getCurrentService()
2155 return service.cueSheet()
2157 def uploadCuesheet(self):
2158 cue = self.__getCuesheet()
2161 print "upload failed, no cuesheet interface"
2163 cue.setCutList(self.cut_list)
2165 def downloadCuesheet(self):
2166 cue = self.__getCuesheet()
2169 print "download failed, no cuesheet interface"
2172 self.cut_list = cue.getCutList()
2174 class InfoBarSummary(Screen):
2176 <screen position="0,0" size="132,64">
2177 <widget source="global.CurrentTime" render="Label" position="62,46" size="82,18" font="Regular;16" >
2178 <convert type="ClockToText">WithSeconds</convert>
2180 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="82,18" zPosition="1" >
2181 <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2182 <convert type="ConditionalShowHide">Blink</convert>
2184 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2185 <convert type="ServiceName">Name</convert>
2187 <widget source="session.Event_Now" render="Progress" position="6,46" size="46,18" borderWidth="1" >
2188 <convert type="EventTime">Progress</convert>
2192 # for picon: (path="piconlcd" will use LCD picons)
2193 # <widget source="session.CurrentService" render="Picon" position="6,0" size="120,64" path="piconlcd" >
2194 # <convert type="ServiceName">Reference</convert>
2197 class InfoBarSummarySupport:
2201 def createSummary(self):
2202 return InfoBarSummary
2204 class InfoBarMoviePlayerSummary(Screen):
2206 <screen position="0,0" size="132,64">
2207 <widget source="global.CurrentTime" render="Label" position="62,46" size="64,18" font="Regular;16" halign="right" >
2208 <convert type="ClockToText">WithSeconds</convert>
2210 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="64,18" zPosition="1" >
2211 <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2212 <convert type="ConditionalShowHide">Blink</convert>
2214 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2215 <convert type="ServiceName">Name</convert>
2217 <widget source="session.CurrentService" render="Progress" position="6,46" size="56,18" borderWidth="1" >
2218 <convert type="ServicePosition">Position</convert>
2222 class InfoBarMoviePlayerSummarySupport:
2226 def createSummary(self):
2227 return InfoBarMoviePlayerSummary
2229 class InfoBarTeletextPlugin:
2231 self.teletext_plugin = None
2233 for p in plugins.getPlugins(PluginDescriptor.WHERE_TELETEXT):
2234 self.teletext_plugin = p
2236 if self.teletext_plugin is not None:
2237 self["TeletextActions"] = HelpableActionMap(self, "InfobarTeletextActions",
2239 "startTeletext": (self.startTeletext, _("View teletext..."))
2242 print "no teletext plugin found!"
2244 def startTeletext(self):
2245 self.teletext_plugin(session=self.session, service=self.session.nav.getCurrentService())
2247 class InfoBarSubtitleSupport(object):
2249 object.__init__(self)
2250 self.subtitle_window = self.session.instantiateDialog(SubtitleDisplay)
2251 self.__subtitles_enabled = False
2253 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2255 iPlayableService.evEnd: self.__serviceStopped,
2256 iPlayableService.evUpdatedInfo: self.__updatedInfo
2258 self.cached_subtitle_checked = False
2259 self.__selected_subtitle = None
2261 def __serviceStopped(self):
2262 self.cached_subtitle_checked = False
2263 if self.__subtitles_enabled:
2264 self.subtitle_window.hide()
2265 self.__subtitles_enabled = False
2266 self.__selected_subtitle = None
2268 def __updatedInfo(self):
2269 if not self.cached_subtitle_checked:
2270 self.cached_subtitle_checked = True
2271 subtitle = self.getCurrentServiceSubtitle()
2272 self.setSelectedSubtitle(subtitle and subtitle.getCachedSubtitle())
2273 if self.__selected_subtitle:
2274 self.setSubtitlesEnable(True)
2276 def getCurrentServiceSubtitle(self):
2277 service = self.session.nav.getCurrentService()
2278 return service and service.subtitle()
2280 def setSubtitlesEnable(self, enable=True):
2281 subtitle = self.getCurrentServiceSubtitle()
2283 if self.__selected_subtitle:
2284 if subtitle and not self.__subtitles_enabled:
2285 subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
2286 self.subtitle_window.show()
2287 self.__subtitles_enabled = True
2290 subtitle.disableSubtitles(self.subtitle_window.instance)
2291 self.__selected_subtitle = False
2292 self.__subtitles_enabled = False
2293 self.subtitle_window.hide()
2295 def setSelectedSubtitle(self, subtitle):
2296 self.__selected_subtitle = subtitle
2298 subtitles_enabled = property(lambda self: self.__subtitles_enabled, setSubtitlesEnable)
2299 selected_subtitle = property(lambda self: self.__selected_subtitle, setSelectedSubtitle)
2301 class InfoBarServiceErrorPopupSupport:
2303 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2305 iPlayableService.evTuneFailed: self.__tuneFailed,
2306 iPlayableService.evStart: self.__serviceStarted
2308 self.__serviceStarted()
2310 def __serviceStarted(self):
2311 self.last_error = None
2312 Notifications.RemovePopup(id = "ZapError")
2314 def __tuneFailed(self):
2315 service = self.session.nav.getCurrentService()
2316 info = service and service.info()
2317 error = info and info.getInfo(iServiceInformation.sDVBState)
2319 if error == self.last_error:
2322 self.last_error = error
2325 eDVBServicePMTHandler.eventNoResources: _("No free tuner!"),
2326 eDVBServicePMTHandler.eventTuneFailed: _("Tune failed!"),
2327 eDVBServicePMTHandler.eventNoPAT: _("No data on transponder!\n(Timeout reading PAT)"),
2328 eDVBServicePMTHandler.eventNoPATEntry: _("Service not found!\n(SID not found in PAT)"),
2329 eDVBServicePMTHandler.eventNoPMT: _("Service invalid!\n(Timeout reading PMT)"),
2330 eDVBServicePMTHandler.eventNewProgramInfo: None,
2331 eDVBServicePMTHandler.eventTuned: None,
2332 eDVBServicePMTHandler.eventSOF: None,
2333 eDVBServicePMTHandler.eventEOF: None,
2334 eDVBServicePMTHandler.eventMisconfiguration: _("Service unavailable!\nCheck tuner configuration!"),
2335 }.get(error) #this returns None when the key not exist in the dict
2337 if error is not None:
2338 Notifications.AddPopup(text = error, type = MessageBox.TYPE_ERROR, timeout = 5, id = "ZapError")
2340 Notifications.RemovePopup(id = "ZapError")