1 from ChannelSelection import ChannelSelection, BouquetSelector, SilentBouquetSelector
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.hideUnhandledKeySymbolTimer = eTimer()
55 self.hideUnhandledKeySymbolTimer.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
64 #this function is called on every keypress!
65 def actionA(self, key, flag):
67 if self.flags & (1<<1):
68 self.flags = self.uflags = 0
69 self.flags |= (1<<flag)
71 self.checkUnusedTimer.start(0, True)
74 #this function is only called when no other action has handled this key
75 def actionB(self, key, flag):
77 self.uflags |= (1<<flag)
79 def checkUnused(self):
80 if self.flags == self.uflags:
81 self.unhandledKeyDialog.show()
82 self.hideUnhandledKeySymbolTimer.start(2000, True)
84 class InfoBarShowHide:
85 """ InfoBar show/hide control, accepts toggleShow and hide actions, might start
93 self["ShowHideActions"] = ActionMap( ["InfobarShowHideActions"] ,
95 "toggleShow": self.toggleShow,
97 }, 1) # lower prio to make it possible to override ok and cancel..
99 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
101 iPlayableService.evStart: self.serviceStarted,
104 self.__state = self.STATE_SHOWN
107 self.hideTimer = eTimer()
108 self.hideTimer.callback.append(self.doTimerHide)
109 self.hideTimer.start(5000, True)
111 self.onShow.append(self.__onShow)
112 self.onHide.append(self.__onHide)
114 def serviceStarted(self):
116 if config.usage.show_infobar_on_zap.value:
120 self.__state = self.STATE_SHOWN
121 self.startHideTimer()
123 def startHideTimer(self):
124 if self.__state == self.STATE_SHOWN and not self.__locked:
125 idx = config.usage.infobar_timeout.index
127 self.hideTimer.start(idx*1000, True)
130 self.__state = self.STATE_HIDDEN
134 self.startHideTimer()
136 def doTimerHide(self):
137 self.hideTimer.stop()
138 if self.__state == self.STATE_SHOWN:
141 def toggleShow(self):
142 if self.__state == self.STATE_SHOWN:
144 self.hideTimer.stop()
145 elif self.__state == self.STATE_HIDDEN:
149 self.__locked = self.__locked + 1
152 self.hideTimer.stop()
154 def unlockShow(self):
155 self.__locked = self.__locked - 1
157 self.startHideTimer()
159 # def startShow(self):
160 # self.instance.m_animation.startMoveAnimation(ePoint(0, 600), ePoint(0, 380), 100)
161 # self.__state = self.STATE_SHOWN
163 # def startHide(self):
164 # self.instance.m_animation.startMoveAnimation(ePoint(0, 380), ePoint(0, 600), 100)
165 # self.__state = self.STATE_HIDDEN
167 class NumberZap(Screen):
174 self.close(int(self["number"].getText()))
176 def keyNumberGlobal(self, number):
177 self.Timer.start(3000, True) #reset timer
178 self.field = self.field + str(number)
179 self["number"].setText(self.field)
180 if len(self.field) >= 4:
183 def __init__(self, session, number):
184 Screen.__init__(self, session)
185 self.field = str(number)
187 self["channel"] = Label(_("Channel:"))
189 self["number"] = Label(self.field)
191 self["actions"] = NumberActionMap( [ "SetupActions" ],
195 "1": self.keyNumberGlobal,
196 "2": self.keyNumberGlobal,
197 "3": self.keyNumberGlobal,
198 "4": self.keyNumberGlobal,
199 "5": self.keyNumberGlobal,
200 "6": self.keyNumberGlobal,
201 "7": self.keyNumberGlobal,
202 "8": self.keyNumberGlobal,
203 "9": self.keyNumberGlobal,
204 "0": self.keyNumberGlobal
207 self.Timer = eTimer()
208 self.Timer.callback.append(self.keyOK)
209 self.Timer.start(3000, True)
211 class InfoBarNumberZap:
212 """ Handles an initial number for NumberZapping """
214 self["NumberActions"] = NumberActionMap( [ "NumberActions"],
216 "1": self.keyNumberGlobal,
217 "2": self.keyNumberGlobal,
218 "3": self.keyNumberGlobal,
219 "4": self.keyNumberGlobal,
220 "5": self.keyNumberGlobal,
221 "6": self.keyNumberGlobal,
222 "7": self.keyNumberGlobal,
223 "8": self.keyNumberGlobal,
224 "9": self.keyNumberGlobal,
225 "0": self.keyNumberGlobal,
228 def keyNumberGlobal(self, number):
229 # print "You pressed number " + str(number)
231 if isinstance(self, InfoBarPiP) and self.pipHandles0Action():
232 self.pipDoHandle0Action()
234 self.servicelist.recallPrevService()
236 if self.has_key("TimeshiftActions") and not self.timeshift_enabled:
237 self.session.openWithCallback(self.numberEntered, NumberZap, number)
239 def numberEntered(self, retval):
240 # print self.servicelist
242 self.zapToNumber(retval)
244 def searchNumberHelper(self, serviceHandler, num, bouquet):
245 servicelist = serviceHandler.list(bouquet)
246 if not servicelist is None:
248 serviceIterator = servicelist.getNext()
249 if not serviceIterator.valid(): #check end of list
251 playable = not (serviceIterator.flags & (eServiceReference.isMarker|eServiceReference.isDirectory))
254 if not num: #found service with searched number ?
255 return serviceIterator, 0
258 def zapToNumber(self, number):
259 bouquet = self.servicelist.bouquet_root
261 serviceHandler = eServiceCenter.getInstance()
262 if not config.usage.multibouquet.value:
263 service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
265 bouquetlist = serviceHandler.list(bouquet)
266 if not bouquetlist is None:
268 bouquet = bouquetlist.getNext()
269 if not bouquet.valid(): #check end of list
271 if bouquet.flags & eServiceReference.isDirectory:
272 service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
273 if not service is None:
274 if self.servicelist.getRoot() != bouquet: #already in correct bouquet?
275 self.servicelist.clearPath()
276 if self.servicelist.bouquet_root != bouquet:
277 self.servicelist.enterPath(self.servicelist.bouquet_root)
278 self.servicelist.enterPath(bouquet)
279 self.servicelist.setCurrentSelection(service) #select the service in servicelist
280 self.servicelist.zap()
282 config.misc.initialchannelselection = ConfigBoolean(default = True)
284 class InfoBarChannelSelection:
285 """ ChannelSelection - handles the channelSelection dialog and the initial
286 channelChange actions which open the channelSelection dialog """
289 self.servicelist = self.session.instantiateDialog(ChannelSelection)
291 if config.misc.initialchannelselection.value:
292 self.onShown.append(self.firstRun)
294 self["ChannelSelectActions"] = HelpableActionMap(self, "InfobarChannelSelection",
296 "switchChannelUp": (self.switchChannelUp, _("open servicelist(up)")),
297 "switchChannelDown": (self.switchChannelDown, _("open servicelist(down)")),
298 "zapUp": (self.zapUp, _("previous channel")),
299 "zapDown": (self.zapDown, _("next channel")),
300 "historyBack": (self.historyBack, _("previous channel in history")),
301 "historyNext": (self.historyNext, _("next channel in history")),
302 "openServiceList": (self.openServiceList, _("open servicelist")),
305 def showTvChannelList(self, zap=False):
306 self.servicelist.setModeTv()
308 self.servicelist.zap()
309 self.session.execDialog(self.servicelist)
311 def showRadioChannelList(self, zap=False):
312 self.servicelist.setModeRadio()
314 self.servicelist.zap()
315 self.session.execDialog(self.servicelist)
318 self.onShown.remove(self.firstRun)
319 config.misc.initialchannelselection.value = False
320 config.misc.initialchannelselection.save()
321 self.switchChannelDown()
323 def historyBack(self):
324 self.servicelist.historyBack()
326 def historyNext(self):
327 self.servicelist.historyNext()
329 def switchChannelUp(self):
330 self.servicelist.moveUp()
331 self.session.execDialog(self.servicelist)
333 def switchChannelDown(self):
334 self.servicelist.moveDown()
335 self.session.execDialog(self.servicelist)
337 def openServiceList(self):
338 self.session.execDialog(self.servicelist)
341 if self.servicelist.inBouquet():
342 prev = self.servicelist.getCurrentSelection()
344 prev = prev.toString()
346 if config.usage.quickzap_bouquet_change.value:
347 if self.servicelist.atBegin():
348 self.servicelist.prevBouquet()
349 self.servicelist.moveUp()
350 cur = self.servicelist.getCurrentSelection()
351 if not cur or (not (cur.flags & 64)) or cur.toString() == prev:
354 self.servicelist.moveUp()
355 self.servicelist.zap()
358 if self.servicelist.inBouquet():
359 prev = self.servicelist.getCurrentSelection()
361 prev = prev.toString()
363 if config.usage.quickzap_bouquet_change.value and self.servicelist.atEnd():
364 self.servicelist.nextBouquet()
366 self.servicelist.moveDown()
367 cur = self.servicelist.getCurrentSelection()
368 if not cur or (not (cur.flags & 64)) or cur.toString() == prev:
371 self.servicelist.moveDown()
372 self.servicelist.zap()
375 """ Handles a menu action, to open the (main) menu """
377 self["MenuActions"] = HelpableActionMap(self, "InfobarMenuActions",
379 "mainMenu": (self.mainMenu, _("Enter main menu...")),
381 self.session.infobar = None
384 print "loading mainmenu XML..."
385 menu = mdom.getroot()
386 assert menu.tag == "menu", "root element in menu must be 'menu'!"
388 self.session.infobar = self
389 # so we can access the currently active infobar from screens opened from within the mainmenu
390 # at the moment used from the SubserviceSelection
392 self.session.openWithCallback(self.mainMenuClosed, MainMenu, menu)
394 def mainMenuClosed(self, *val):
395 self.session.infobar = None
397 class InfoBarSimpleEventView:
398 """ Opens the Eventview for now/next """
400 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
402 "showEventInfo": (self.openEventView, _("show event details")),
403 "showInfobarOrEpgWhenInfobarAlreadyVisible": self.showEventInfoWhenNotVisible,
406 def showEventInfoWhenNotVisible(self):
413 def openEventView(self):
415 self.epglist = epglist
416 service = self.session.nav.getCurrentService()
417 ref = self.session.nav.getCurrentlyPlayingServiceReference()
418 info = service.info()
426 self.session.open(EventViewSimple, epglist[0], ServiceReference(ref), self.eventViewCallback)
428 def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
429 epglist = self.epglist
432 epglist[0] = epglist[1]
436 class SimpleServicelist:
437 def __init__(self, services):
438 self.services = services
439 self.length = len(services)
442 def selectService(self, service):
448 while self.services[self.current].ref != service:
450 if self.current >= self.length:
454 def nextService(self):
457 if self.current+1 < self.length:
462 def prevService(self):
465 if self.current-1 > -1:
468 self.current = self.length - 1
470 def currentService(self):
471 if not self.length or self.current >= self.length:
473 return self.services[self.current]
476 """ EPG - Opens an EPG list when the showEPGList action fires """
478 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
480 iPlayableService.evUpdatedEventInfo: self.__evEventInfoChanged,
483 self.is_now_next = False
485 self.bouquetSel = None
486 self.eventView = None
487 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
489 "showEventInfo": (self.openEventView, _("show EPG...")),
490 "showEventInfoPlugin": (self.showEventInfoPlugins, _("show single service EPG...")),
491 "showInfobarOrEpgWhenInfobarAlreadyVisible": self.showEventInfoWhenNotVisible,
494 def showEventInfoWhenNotVisible(self):
501 def zapToService(self, service):
502 if not service is None:
503 if self.servicelist.getRoot() != self.epg_bouquet: #already in correct bouquet?
504 self.servicelist.clearPath()
505 if self.servicelist.bouquet_root != self.epg_bouquet:
506 self.servicelist.enterPath(self.servicelist.bouquet_root)
507 self.servicelist.enterPath(self.epg_bouquet)
508 self.servicelist.setCurrentSelection(service) #select the service in servicelist
509 self.servicelist.zap()
511 def getBouquetServices(self, bouquet):
513 servicelist = eServiceCenter.getInstance().list(bouquet)
514 if not servicelist is None:
516 service = servicelist.getNext()
517 if not service.valid(): #check if end of list
519 if service.flags & (eServiceReference.isDirectory | eServiceReference.isMarker): #ignore non playable services
521 services.append(ServiceReference(service))
524 def openBouquetEPG(self, bouquet, withCallback=True):
525 services = self.getBouquetServices(bouquet)
527 self.epg_bouquet = bouquet
529 self.dlg_stack.append(self.session.openWithCallback(self.closed, EPGSelection, services, self.zapToService, None, self.changeBouquetCB))
531 self.session.open(EPGSelection, services, self.zapToService, None, self.changeBouquetCB)
533 def changeBouquetCB(self, direction, epg):
536 self.bouquetSel.down()
539 bouquet = self.bouquetSel.getCurrent()
540 services = self.getBouquetServices(bouquet)
542 self.epg_bouquet = bouquet
543 epg.setServices(services)
545 def closed(self, ret=False):
546 closedScreen = self.dlg_stack.pop()
547 if self.bouquetSel and closedScreen == self.bouquetSel:
548 self.bouquetSel = None
549 elif self.eventView and closedScreen == self.eventView:
550 self.eventView = None
552 dlgs=len(self.dlg_stack)
554 self.dlg_stack[dlgs-1].close(dlgs > 1)
556 def openMultiServiceEPG(self, withCallback=True):
557 bouquets = self.servicelist.getBouquetList()
562 if config.usage.multiepg_ask_bouquet.value:
563 self.openMultiServiceEPGAskBouquet(bouquets, cnt, withCallback)
565 self.openMultiServiceEPGSilent(bouquets, cnt, withCallback)
567 def openMultiServiceEPGAskBouquet(self, bouquets, cnt, withCallback):
568 if cnt > 1: # show bouquet list
570 self.bouquetSel = self.session.openWithCallback(self.closed, BouquetSelector, bouquets, self.openBouquetEPG, enableWrapAround=True)
571 self.dlg_stack.append(self.bouquetSel)
573 self.bouquetSel = self.session.open(BouquetSelector, bouquets, self.openBouquetEPG, enableWrapAround=True)
575 self.openBouquetEPG(bouquets[0][1], withCallback)
577 def openMultiServiceEPGSilent(self, bouquets, cnt, withCallback):
578 root = self.servicelist.getRoot()
579 rootstr = root.toCompareString()
581 for bouquet in bouquets:
582 if bouquet[1].toCompareString() == rootstr:
587 if cnt > 1: # create bouquet list for bouq+/-
588 self.bouquetSel = SilentBouquetSelector(bouquets, True, self.servicelist.getBouquetNumOffset(root))
590 self.openBouquetEPG(root, withCallback)
592 def changeServiceCB(self, direction, epg):
595 self.serviceSel.nextService()
597 self.serviceSel.prevService()
598 epg.setService(self.serviceSel.currentService())
600 def SingleServiceEPGClosed(self, ret=False):
601 self.serviceSel = None
603 def openSingleServiceEPG(self):
604 ref=self.session.nav.getCurrentlyPlayingServiceReference()
606 if self.servicelist.getMutableList() is not None: # bouquet in channellist
607 current_path = self.servicelist.getRoot()
608 services = self.getBouquetServices(current_path)
609 self.serviceSel = SimpleServicelist(services)
610 if self.serviceSel.selectService(ref):
611 self.session.openWithCallback(self.SingleServiceEPGClosed, EPGSelection, ref, serviceChangeCB = self.changeServiceCB)
613 self.session.openWithCallback(self.SingleServiceEPGClosed, EPGSelection, ref)
615 self.session.open(EPGSelection, ref)
617 def showEventInfoPlugins(self):
618 list = [(p.name, boundFunction(self.runPlugin, p)) for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EVENTINFO)]
621 list.append((_("show single service EPG..."), self.openSingleServiceEPG))
622 self.session.openWithCallback(self.EventInfoPluginChosen, ChoiceBox, title=_("Please choose an extension..."), list = list, skin_name = "EPGExtensionsList")
624 self.openSingleServiceEPG()
626 def runPlugin(self, plugin):
627 plugin(session = self.session, servicelist = self.servicelist)
629 def EventInfoPluginChosen(self, answer):
630 if answer is not None:
633 def openSimilarList(self, eventid, refstr):
634 self.session.open(EPGSelection, refstr, None, eventid)
636 def getNowNext(self):
638 service = self.session.nav.getCurrentService()
639 info = service and service.info()
640 ptr = info and info.getEvent(0)
643 ptr = info and info.getEvent(1)
646 self.epglist = epglist
648 def __evEventInfoChanged(self):
649 if self.is_now_next and len(self.dlg_stack) == 1:
651 assert self.eventView
653 self.eventView.setEvent(self.epglist[0])
655 def openEventView(self):
656 ref = self.session.nav.getCurrentlyPlayingServiceReference()
658 epglist = self.epglist
660 self.is_now_next = False
661 epg = eEPGCache.getInstance()
662 ptr = ref and ref.valid() and epg.lookupEventTime(ref, -1)
665 ptr = epg.lookupEventTime(ref, ptr.getBeginTime(), +1)
669 self.is_now_next = True
671 self.eventView = self.session.openWithCallback(self.closed, EventViewEPGSelect, self.epglist[0], ServiceReference(ref), self.eventViewCallback, self.openSingleServiceEPG, self.openMultiServiceEPG, self.openSimilarList)
672 self.dlg_stack.append(self.eventView)
674 print "no epg for the service avail.. so we show multiepg instead of eventinfo"
675 self.openMultiServiceEPG(False)
677 def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
678 epglist = self.epglist
681 epglist[0]=epglist[1]
685 class InfoBarRdsDecoder:
686 """provides RDS and Rass support/display"""
688 self.rds_display = self.session.instantiateDialog(RdsInfoDisplay)
689 self.rass_interactive = None
691 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
693 iPlayableService.evEnd: self.__serviceStopped,
694 iPlayableService.evUpdatedRassSlidePic: self.RassSlidePicChanged
697 self["RdsActions"] = ActionMap(["InfobarRdsActions"],
699 "startRassInteractive": self.startRassInteractive
702 self["RdsActions"].setEnabled(False)
704 self.onLayoutFinish.append(self.rds_display.show)
705 self.rds_display.onRassInteractivePossibilityChanged.append(self.RassInteractivePossibilityChanged)
707 def RassInteractivePossibilityChanged(self, state):
708 self["RdsActions"].setEnabled(state)
710 def RassSlidePicChanged(self):
711 if not self.rass_interactive:
712 service = self.session.nav.getCurrentService()
713 decoder = service and service.rdsDecoder()
715 decoder.showRassSlidePicture()
717 def __serviceStopped(self):
718 if self.rass_interactive is not None:
719 rass_interactive = self.rass_interactive
720 self.rass_interactive = None
721 rass_interactive.close()
723 def startRassInteractive(self):
724 self.rds_display.hide()
725 self.rass_interactive = self.session.openWithCallback(self.RassInteractiveClosed, RassInteractive)
727 def RassInteractiveClosed(self, *val):
728 if self.rass_interactive is not None:
729 self.rass_interactive = None
730 self.RassSlidePicChanged()
731 self.rds_display.show()
734 """handles actions like seeking, pause"""
736 SEEK_STATE_PLAY = (0, 0, 0, ">")
737 SEEK_STATE_PAUSE = (1, 0, 0, "||")
738 SEEK_STATE_EOF = (1, 0, 0, "END")
740 def __init__(self, actionmap = "InfobarSeekActions", useSeekBackHack=True):
741 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
743 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
744 iPlayableService.evStart: self.__serviceStarted,
746 iPlayableService.evEOF: self.__evEOF,
747 iPlayableService.evSOF: self.__evSOF,
749 self.fast_winding_hint_message_showed = False
751 class InfoBarSeekActionMap(HelpableActionMap):
752 def __init__(self, screen, *args, **kwargs):
753 HelpableActionMap.__init__(self, screen, *args, **kwargs)
756 def action(self, contexts, action):
757 print "action:", action
758 if action[:5] == "seek:":
759 time = int(action[5:])
760 self.screen.doSeekRelative(time * 90000)
762 elif action[:8] == "seekdef:":
763 key = int(action[8:])
764 time = (-config.seek.selfdefined_13.value, False, config.seek.selfdefined_13.value,
765 -config.seek.selfdefined_46.value, False, config.seek.selfdefined_46.value,
766 -config.seek.selfdefined_79.value, False, config.seek.selfdefined_79.value)[key-1]
767 self.screen.doSeekRelative(time * 90000)
770 return HelpableActionMap.action(self, contexts, action)
772 self["SeekActions"] = InfoBarSeekActionMap(self, actionmap,
774 "playpauseService": self.playpauseService,
775 "pauseService": (self.pauseService, _("pause")),
776 "unPauseService": (self.unPauseService, _("continue")),
778 "seekFwd": (self.seekFwd, _("skip forward")),
779 "seekFwdManual": (self.seekFwdManual, _("skip forward (enter time)")),
780 "seekBack": (self.seekBack, _("skip backward")),
781 "seekBackManual": (self.seekBackManual, _("skip backward (enter time)"))
783 # give them a little more priority to win over color buttons
785 self["SeekActions"].setEnabled(False)
787 self.seekstate = self.SEEK_STATE_PLAY
788 self.lastseekstate = self.SEEK_STATE_PLAY
790 self.onPlayStateChanged = [ ]
792 self.lockedBecauseOfSkipping = False
794 self.__seekableStatusChanged()
796 def makeStateForward(self, n):
797 # minspeed = config.seek.stepwise_minspeed.value
798 # repeat = int(config.seek.stepwise_repeat.value)
799 # if minspeed != "Never" and n >= int(minspeed) and repeat > 1:
800 # return (0, n * repeat, repeat, ">> %dx" % n)
802 return (0, n, 0, ">> %dx" % n)
804 def makeStateBackward(self, n):
805 # minspeed = config.seek.stepwise_minspeed.value
806 # repeat = int(config.seek.stepwise_repeat.value)
807 # if minspeed != "Never" and n >= int(minspeed) and repeat > 1:
808 # return (0, -n * repeat, repeat, "<< %dx" % n)
810 return (0, -n, 0, "<< %dx" % n)
812 def makeStateSlowMotion(self, n):
813 return (0, 0, n, "/%d" % n)
815 def isStateForward(self, state):
818 def isStateBackward(self, state):
821 def isStateSlowMotion(self, state):
822 return state[1] == 0 and state[2] > 1
824 def getHigher(self, n, lst):
830 def getLower(self, n, lst):
838 def showAfterSeek(self):
839 if isinstance(self, InfoBarShowHide):
849 service = self.session.nav.getCurrentService()
853 seek = service.seek()
855 if seek is None or not seek.isCurrentlySeekable():
860 def isSeekable(self):
861 if self.getSeek() is None:
865 def __seekableStatusChanged(self):
866 # print "seekable status changed!"
867 if not self.isSeekable():
868 self["SeekActions"].setEnabled(False)
869 # print "not seekable, return to play"
870 self.setSeekState(self.SEEK_STATE_PLAY)
872 self["SeekActions"].setEnabled(True)
875 def __serviceStarted(self):
876 self.fast_winding_hint_message_showed = False
877 self.seekstate = self.SEEK_STATE_PLAY
878 self.__seekableStatusChanged()
880 def setSeekState(self, state):
881 service = self.session.nav.getCurrentService()
886 if not self.isSeekable():
887 if state not in (self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE):
888 state = self.SEEK_STATE_PLAY
890 pauseable = service.pause()
892 if pauseable is None:
893 print "not pauseable."
894 state = self.SEEK_STATE_PLAY
896 self.seekstate = state
898 if pauseable is not None:
899 if self.seekstate[0]:
900 print "resolved to PAUSE"
902 elif self.seekstate[1]:
903 print "resolved to FAST FORWARD"
904 pauseable.setFastForward(self.seekstate[1])
905 elif self.seekstate[2]:
906 print "resolved to SLOW MOTION"
907 pauseable.setSlowMotion(self.seekstate[2])
909 print "resolved to PLAY"
912 for c in self.onPlayStateChanged:
915 self.checkSkipShowHideLock()
919 def playpauseService(self):
920 if self.seekstate != self.SEEK_STATE_PLAY:
921 self.unPauseService()
925 def pauseService(self):
926 if self.seekstate == self.SEEK_STATE_PAUSE:
927 if config.seek.on_pause.value == "play":
928 self.unPauseService()
929 elif config.seek.on_pause.value == "step":
930 self.doSeekRelative(1)
931 elif config.seek.on_pause.value == "last":
932 self.setSeekState(self.lastseekstate)
933 self.lastseekstate = self.SEEK_STATE_PLAY
935 if self.seekstate != self.SEEK_STATE_EOF:
936 self.lastseekstate = self.seekstate
937 self.setSeekState(self.SEEK_STATE_PAUSE);
939 def unPauseService(self):
941 if self.seekstate == self.SEEK_STATE_PLAY:
943 self.setSeekState(self.SEEK_STATE_PLAY)
945 def doSeek(self, pts):
946 seekable = self.getSeek()
951 def doSeekRelative(self, pts):
952 seekable = self.getSeek()
955 prevstate = self.seekstate
957 if self.seekstate == self.SEEK_STATE_EOF:
958 if prevstate == self.SEEK_STATE_PAUSE:
959 self.setSeekState(self.SEEK_STATE_PAUSE)
961 self.setSeekState(self.SEEK_STATE_PLAY)
962 seekable.seekRelative(pts<0 and -1 or 1, abs(pts))
963 if abs(pts) > 100 and config.usage.show_infobar_on_skip.value:
967 seek = self.getSeek()
968 if seek and not (seek.isCurrentlySeekable() & 2):
969 if not self.fast_winding_hint_message_showed and (seek.isCurrentlySeekable() & 1):
970 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)
971 self.fast_winding_hint_message_showed = True
973 return 0 # trade as unhandled action
974 if self.seekstate == self.SEEK_STATE_PLAY:
975 self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
976 elif self.seekstate == self.SEEK_STATE_PAUSE:
977 if len(config.seek.speeds_slowmotion.value):
978 self.setSeekState(self.makeStateSlowMotion(config.seek.speeds_slowmotion.value[-1]))
980 self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
981 elif self.seekstate == self.SEEK_STATE_EOF:
983 elif self.isStateForward(self.seekstate):
984 speed = self.seekstate[1]
985 if self.seekstate[2]:
986 speed /= self.seekstate[2]
987 speed = self.getHigher(speed, config.seek.speeds_forward.value) or config.seek.speeds_forward.value[-1]
988 self.setSeekState(self.makeStateForward(speed))
989 elif self.isStateBackward(self.seekstate):
990 speed = -self.seekstate[1]
991 if self.seekstate[2]:
992 speed /= self.seekstate[2]
993 speed = self.getLower(speed, config.seek.speeds_backward.value)
995 self.setSeekState(self.makeStateBackward(speed))
997 self.setSeekState(self.SEEK_STATE_PLAY)
998 elif self.isStateSlowMotion(self.seekstate):
999 speed = self.getLower(self.seekstate[2], config.seek.speeds_slowmotion.value) or config.seek.speeds_slowmotion.value[0]
1000 self.setSeekState(self.makeStateSlowMotion(speed))
1003 seek = self.getSeek()
1004 if seek and not (seek.isCurrentlySeekable() & 2):
1005 if not self.fast_winding_hint_message_showed and (seek.isCurrentlySeekable() & 1):
1006 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)
1007 self.fast_winding_hint_message_showed = True
1009 return 0 # trade as unhandled action
1010 seekstate = self.seekstate
1011 if seekstate == self.SEEK_STATE_PLAY:
1012 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1013 elif seekstate == self.SEEK_STATE_EOF:
1014 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1015 self.doSeekRelative(-6)
1016 elif seekstate == self.SEEK_STATE_PAUSE:
1017 self.doSeekRelative(-1)
1018 elif self.isStateForward(seekstate):
1019 speed = seekstate[1]
1021 speed /= seekstate[2]
1022 speed = self.getLower(speed, config.seek.speeds_forward.value)
1024 self.setSeekState(self.makeStateForward(speed))
1026 self.setSeekState(self.SEEK_STATE_PLAY)
1027 elif self.isStateBackward(seekstate):
1028 speed = -seekstate[1]
1030 speed /= seekstate[2]
1031 speed = self.getHigher(speed, config.seek.speeds_backward.value) or config.seek.speeds_backward.value[-1]
1032 self.setSeekState(self.makeStateBackward(speed))
1033 elif self.isStateSlowMotion(seekstate):
1034 speed = self.getHigher(seekstate[2], config.seek.speeds_slowmotion.value)
1036 self.setSeekState(self.makeStateSlowMotion(speed))
1038 self.setSeekState(self.SEEK_STATE_PAUSE)
1040 def seekFwdManual(self):
1041 self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
1043 def fwdSeekTo(self, minutes):
1044 print "Seek", minutes, "minutes forward"
1045 self.doSeekRelative(minutes * 60 * 90000)
1047 def seekBackManual(self):
1048 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
1050 def rwdSeekTo(self, minutes):
1052 self.doSeekRelative(-minutes * 60 * 90000)
1054 def checkSkipShowHideLock(self):
1055 wantlock = self.seekstate != self.SEEK_STATE_PLAY
1057 if config.usage.show_infobar_on_skip.value:
1058 if self.lockedBecauseOfSkipping and not wantlock:
1060 self.lockedBecauseOfSkipping = False
1062 if wantlock and not self.lockedBecauseOfSkipping:
1064 self.lockedBecauseOfSkipping = True
1066 def calcRemainingTime(self):
1067 seekable = self.getSeek()
1068 if seekable is not None:
1069 len = seekable.getLength()
1071 tmp = self.cueGetEndCutPosition()
1076 pos = seekable.getPlayPosition()
1077 speednom = self.seekstate[1] or 1
1078 speedden = self.seekstate[2] or 1
1079 if not len[0] and not pos[0]:
1080 if len[1] <= pos[1]:
1082 time = (len[1] - pos[1])*speedden/(90*speednom)
1087 if self.seekstate == self.SEEK_STATE_EOF:
1090 # if we are seeking forward, we try to end up ~1s before the end, and pause there.
1091 seekstate = self.seekstate
1092 if self.seekstate != self.SEEK_STATE_PAUSE:
1093 self.setSeekState(self.SEEK_STATE_EOF)
1095 if seekstate not in (self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE): # if we are seeking
1096 seekable = self.getSeek()
1097 if seekable is not None:
1099 if seekstate == self.SEEK_STATE_PLAY: # regular EOF
1100 self.doEofInternal(True)
1102 self.doEofInternal(False)
1104 def doEofInternal(self, playing):
1105 pass # Defined in subclasses
1108 self.setSeekState(self.SEEK_STATE_PLAY)
1111 from Screens.PVRState import PVRState, TimeshiftState
1113 class InfoBarPVRState:
1114 def __init__(self, screen=PVRState, force_show = False):
1115 self.onPlayStateChanged.append(self.__playStateChanged)
1116 self.pvrStateDialog = self.session.instantiateDialog(screen)
1117 self.onShow.append(self._mayShow)
1118 self.onHide.append(self.pvrStateDialog.hide)
1119 self.force_show = force_show
1122 if self.execing and self.seekstate != self.SEEK_STATE_PLAY:
1123 self.pvrStateDialog.show()
1125 def __playStateChanged(self, state):
1126 playstateString = state[3]
1127 self.pvrStateDialog["state"].setText(playstateString)
1129 # if we return into "PLAY" state, ensure that the dialog gets hidden if there will be no infobar displayed
1130 if not config.usage.show_infobar_on_skip.value and self.seekstate == self.SEEK_STATE_PLAY and not self.force_show:
1131 self.pvrStateDialog.hide()
1136 class InfoBarTimeshiftState(InfoBarPVRState):
1138 InfoBarPVRState.__init__(self, screen=TimeshiftState, force_show = True)
1141 if self.execing and self.timeshift_enabled and self.seekstate != self.SEEK_STATE_PLAY:
1142 self.pvrStateDialog.show()
1144 class InfoBarShowMovies:
1146 # i don't really like this class.
1147 # it calls a not further specified "movie list" on up/down/movieList,
1148 # so this is not more than an action map
1150 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions",
1152 "movieList": (self.showMovies, _("movie list")),
1153 "up": (self.showMovies, _("movie list")),
1154 "down": (self.showMovies, _("movie list"))
1157 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
1161 # Timeshift works the following way:
1162 # demux0 demux1 "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
1163 # - normal playback TUNER unused PLAY enable disable disable
1164 # - user presses "yellow" button. FILE record PAUSE enable disable enable
1165 # - user presess pause again FILE record PLAY enable disable enable
1166 # - user fast forwards FILE record FF enable disable enable
1167 # - end of timeshift buffer reached TUNER record PLAY enable enable disable
1168 # - user backwards FILE record BACK # !! enable disable enable
1172 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
1173 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
1174 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
1175 # - the user can now PVR around
1176 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
1177 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
1179 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
1180 # - if the user rewinds, or press pause, timeshift will be activated again
1182 # note that a timeshift can be enabled ("recording") and
1183 # activated (currently time-shifting).
1185 class InfoBarTimeshift:
1187 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions",
1189 "timeshiftStart": (self.startTimeshift, _("start timeshift")), # the "yellow key"
1190 "timeshiftStop": (self.stopTimeshift, _("stop timeshift")) # currently undefined :), probably 'TV'
1192 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
1194 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "rewind key"
1195 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause # something like "pause key"
1196 }, prio=-1) # priority over record
1198 self.timeshift_enabled = 0
1199 self.timeshift_state = 0
1200 self.ts_rewind_timer = eTimer()
1201 self.ts_rewind_timer.callback.append(self.rewindService)
1203 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1205 iPlayableService.evStart: self.__serviceStarted,
1206 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged
1209 def getTimeshift(self):
1210 service = self.session.nav.getCurrentService()
1211 return service and service.timeshift()
1213 def startTimeshift(self):
1214 print "enable timeshift"
1215 ts = self.getTimeshift()
1217 self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
1218 print "no ts interface"
1221 if self.timeshift_enabled:
1222 print "hu, timeshift already enabled?"
1224 if not ts.startTimeshift():
1225 self.timeshift_enabled = 1
1227 # we remove the "relative time" for now.
1228 #self.pvrStateDialog["timeshift"].setRelative(time.time())
1231 #self.setSeekState(self.SEEK_STATE_PAUSE)
1232 self.activateTimeshiftEnd(False)
1234 # enable the "TimeshiftEnableActions", which will override
1235 # the startTimeshift actions
1236 self.__seekableStatusChanged()
1238 print "timeshift failed"
1240 def stopTimeshift(self):
1241 if not self.timeshift_enabled:
1243 print "disable timeshift"
1244 ts = self.getTimeshift()
1247 self.session.openWithCallback(self.stopTimeshiftConfirmed, MessageBox, _("Stop Timeshift?"), MessageBox.TYPE_YESNO)
1249 def stopTimeshiftConfirmed(self, confirmed):
1253 ts = self.getTimeshift()
1258 self.timeshift_enabled = 0
1261 self.__seekableStatusChanged()
1263 # activates timeshift, and seeks to (almost) the end
1264 def activateTimeshiftEnd(self, back = True):
1265 ts = self.getTimeshift()
1266 print "activateTimeshiftEnd"
1271 if ts.isTimeshiftActive():
1272 print "!! activate timeshift called - but shouldn't this be a normal pause?"
1276 ts.activateTimeshift() # activate timeshift will automatically pause
1277 self.setSeekState(self.SEEK_STATE_PAUSE)
1280 self.ts_rewind_timer.start(200, 1)
1282 def rewindService(self):
1283 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1285 # same as activateTimeshiftEnd, but pauses afterwards.
1286 def activateTimeshiftEndAndPause(self):
1287 print "activateTimeshiftEndAndPause"
1288 #state = self.seekstate
1289 self.activateTimeshiftEnd(False)
1291 def __seekableStatusChanged(self):
1294 # print "self.isSeekable", self.isSeekable()
1295 # print "self.timeshift_enabled", self.timeshift_enabled
1297 # when this service is not seekable, but timeshift
1298 # is enabled, this means we can activate
1300 if not self.isSeekable() and self.timeshift_enabled:
1303 # print "timeshift activate:", enabled
1304 self["TimeshiftActivateActions"].setEnabled(enabled)
1306 def __serviceStarted(self):
1307 self.timeshift_enabled = False
1308 self.__seekableStatusChanged()
1310 from Screens.PiPSetup import PiPSetup
1312 class InfoBarExtensions:
1313 EXTENSION_SINGLE = 0
1319 self["InstantExtensionsActions"] = HelpableActionMap(self, "InfobarExtensions",
1321 "extensions": (self.showExtensionSelection, _("view extensions...")),
1322 }, 1) # lower priority
1324 def addExtension(self, extension, key = None, type = EXTENSION_SINGLE):
1325 self.list.append((type, extension, key))
1327 def updateExtension(self, extension, key = None):
1328 self.extensionsList.append(extension)
1330 if self.extensionKeys.has_key(key):
1334 for x in self.availableKeys:
1335 if not self.extensionKeys.has_key(x):
1340 self.extensionKeys[key] = len(self.extensionsList) - 1
1342 def updateExtensions(self):
1343 self.extensionsList = []
1344 self.availableKeys = [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "red", "green", "yellow", "blue" ]
1345 self.extensionKeys = {}
1347 if x[0] == self.EXTENSION_SINGLE:
1348 self.updateExtension(x[1], x[2])
1351 self.updateExtension(y[0], y[1])
1354 def showExtensionSelection(self):
1355 self.updateExtensions()
1356 extensionsList = self.extensionsList[:]
1359 for x in self.availableKeys:
1360 if self.extensionKeys.has_key(x):
1361 entry = self.extensionKeys[x]
1362 extension = self.extensionsList[entry]
1364 name = str(extension[0]())
1365 list.append((extension[0](), extension))
1367 extensionsList.remove(extension)
1369 extensionsList.remove(extension)
1370 list.extend([(x[0](), x) for x in extensionsList])
1372 keys += [""] * len(extensionsList)
1373 self.session.openWithCallback(self.extensionCallback, ChoiceBox, title=_("Please choose an extension..."), list = list, keys = keys, skin_name = "ExtensionsList")
1375 def extensionCallback(self, answer):
1376 if answer is not None:
1379 from Tools.BoundFunction import boundFunction
1381 # depends on InfoBarExtensions
1383 class InfoBarPlugins:
1385 self.addExtension(extension = self.getPluginList, type = InfoBarExtensions.EXTENSION_LIST)
1387 def getPluginName(self, name):
1390 def getPluginList(self):
1391 list = [((boundFunction(self.getPluginName, p.name), boundFunction(self.runPlugin, p), lambda: True), None, p.name) for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EXTENSIONSMENU)]
1392 list.sort(key = lambda e: e[2]) # sort by name
1395 def runPlugin(self, plugin):
1396 if isinstance(self, InfoBarChannelSelection):
1397 plugin(session = self.session, servicelist = self.servicelist)
1399 plugin(session = self.session)
1401 from Components.Task import job_manager
1402 class InfoBarJobman:
1404 self.addExtension(extension = self.getJobList, type = InfoBarExtensions.EXTENSION_LIST)
1406 def getJobList(self):
1407 return [((boundFunction(self.getJobName, job), boundFunction(self.showJobView, job), lambda: True), None) for job in job_manager.getPendingJobs()]
1409 def getJobName(self, job):
1410 return "%s: %s (%d%%)" % (job.getStatustext(), job.name, int(100*job.progress/float(job.end)))
1412 def showJobView(self, job):
1413 from Screens.TaskView import JobView
1414 job_manager.in_background = False
1415 self.session.openWithCallback(self.JobViewCB, JobView, job)
1417 def JobViewCB(self, in_background):
1418 job_manager.in_background = in_background
1420 # depends on InfoBarExtensions
1424 self.session.pipshown
1426 self.session.pipshown = False
1427 if SystemInfo.get("NumVideoDecoders", 1) > 1:
1429 self.addExtension((self.getShowHideName, self.showPiP, lambda: True), "blue")
1430 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1431 self.addExtension((self.getSwapName, self.swapPiP, self.pipShown), "yellow")
1433 self.addExtension((self.getShowHideName, self.showPiP, self.pipShown), "blue")
1434 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1437 return self.session.pipshown
1439 def pipHandles0Action(self):
1440 return self.pipShown() and config.usage.pip_zero_button.value != "standard"
1442 def getShowHideName(self):
1443 if self.session.pipshown:
1444 return _("Disable Picture in Picture")
1446 return _("Activate Picture in Picture")
1448 def getSwapName(self):
1449 return _("Swap Services")
1451 def getMoveName(self):
1452 return _("Move Picture in Picture")
1455 if self.session.pipshown:
1456 del self.session.pip
1457 self.session.pipshown = False
1459 self.session.pip = self.session.instantiateDialog(PictureInPicture)
1460 self.session.pip.show()
1461 newservice = self.session.nav.getCurrentlyPlayingServiceReference()
1462 if self.session.pip.playService(newservice):
1463 self.session.pipshown = True
1464 self.session.pip.servicePath = self.servicelist.getCurrentServicePath()
1466 self.session.pipshown = False
1467 del self.session.pip
1468 self.session.nav.playService(newservice)
1471 swapservice = self.session.nav.getCurrentlyPlayingServiceReference()
1472 if self.session.pip.servicePath:
1473 servicepath = self.servicelist.getCurrentServicePath()
1474 ref=servicepath[len(servicepath)-1]
1475 pipref=self.session.pip.getCurrentService()
1476 self.session.pip.playService(swapservice)
1477 self.servicelist.setCurrentServicePath(self.session.pip.servicePath)
1478 if pipref.toString() != ref.toString(): # is a subservice ?
1479 self.session.nav.stopService() # stop portal
1480 self.session.nav.playService(pipref) # start subservice
1481 self.session.pip.servicePath=servicepath
1484 self.session.open(PiPSetup, pip = self.session.pip)
1486 def pipDoHandle0Action(self):
1487 use = config.usage.pip_zero_button.value
1490 elif "swapstop" == use:
1496 from RecordTimer import parseEvent, RecordTimerEntry
1498 class InfoBarInstantRecord:
1499 """Instant Record - handles the instantRecord action in order to
1500 start/stop instant records"""
1502 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
1504 "instantRecord": (self.instantRecord, _("Instant Record...")),
1508 def stopCurrentRecording(self, entry = -1):
1509 if entry is not None and entry != -1:
1510 self.session.nav.RecordTimer.removeEntry(self.recording[entry])
1511 self.recording.remove(self.recording[entry])
1513 def startInstantRecording(self, limitEvent = False):
1514 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
1516 # try to get event info
1519 service = self.session.nav.getCurrentService()
1520 epg = eEPGCache.getInstance()
1521 event = epg.lookupEventTime(serviceref, -1, 0)
1523 info = service.info()
1524 ev = info.getEvent(0)
1530 end = begin + 3600 # dummy
1531 name = "instant record"
1535 if event is not None:
1536 curEvent = parseEvent(event)
1538 description = curEvent[3]
1539 eventid = curEvent[4]
1544 self.session.open(MessageBox, _("No event info found, recording indefinitely."), MessageBox.TYPE_INFO)
1546 if isinstance(serviceref, eServiceReference):
1547 serviceref = ServiceReference(serviceref)
1549 recording = RecordTimerEntry(serviceref, begin, end, name, description, eventid, dirname = preferredInstantRecordPath())
1550 recording.dontSave = True
1552 if event is None or limitEvent == False:
1553 recording.autoincrease = True
1554 if recording.setAutoincreaseEnd():
1555 self.session.nav.RecordTimer.record(recording)
1556 self.recording.append(recording)
1558 simulTimerList = self.session.nav.RecordTimer.record(recording)
1559 if simulTimerList is not None: # conflict with other recording
1560 name = simulTimerList[1].name
1561 name_date = ' '.join((name, strftime('%c', localtime(simulTimerList[1].begin))))
1562 print "[TIMER] conflicts with", name_date
1563 recording.autoincrease = True # start with max available length, then increment
1564 if recording.setAutoincreaseEnd():
1565 self.session.nav.RecordTimer.record(recording)
1566 self.recording.append(recording)
1567 self.session.open(MessageBox, _("Record time limited due to conflicting timer %s") % name_date, MessageBox.TYPE_INFO)
1569 self.session.open(MessageBox, _("Couldn't record due to conflicting timer %s") % name, MessageBox.TYPE_INFO)
1570 recording.autoincrease = False
1572 self.recording.append(recording)
1574 def isInstantRecordRunning(self):
1575 print "self.recording:", self.recording
1577 for x in self.recording:
1582 def recordQuestionCallback(self, answer):
1583 print "pre:\n", self.recording
1585 if answer is None or answer[1] == "no":
1588 recording = self.recording[:]
1590 if not x in self.session.nav.RecordTimer.timer_list:
1591 self.recording.remove(x)
1592 elif x.dontSave and x.isRunning():
1593 list.append((x, False))
1595 if answer[1] == "changeduration":
1596 if len(self.recording) == 1:
1597 self.changeDuration(0)
1599 self.session.openWithCallback(self.changeDuration, TimerSelection, list)
1600 elif answer[1] == "changeendtime":
1601 if len(self.recording) == 1:
1604 self.session.openWithCallback(self.setEndtime, TimerSelection, list)
1605 elif answer[1] == "stop":
1606 if len(self.recording) == 1:
1607 self.stopCurrentRecording(0)
1609 self.session.openWithCallback(self.stopCurrentRecording, TimerSelection, list)
1610 elif answer[1] in ( "indefinitely" , "manualduration", "manualendtime", "event"):
1611 self.startInstantRecording(limitEvent = answer[1] in ("event", "manualendtime") or False)
1612 if answer[1] == "manualduration":
1613 self.changeDuration(len(self.recording)-1)
1614 elif answer[1] == "manualendtime":
1615 self.setEndtime(len(self.recording)-1)
1616 print "after:\n", self.recording
1618 def setEndtime(self, entry):
1619 if entry is not None and entry >= 0:
1620 self.selectedEntry = entry
1621 self.endtime=ConfigClock(default = self.recording[self.selectedEntry].end)
1622 dlg = self.session.openWithCallback(self.TimeDateInputClosed, TimeDateInput, self.endtime)
1623 dlg.setTitle(_("Please change recording endtime"))
1625 def TimeDateInputClosed(self, ret):
1628 localendtime = localtime(ret[1])
1629 print "stopping recording at", strftime("%c", localendtime)
1630 if self.recording[self.selectedEntry].end != ret[1]:
1631 self.recording[self.selectedEntry].autoincrease = False
1632 self.recording[self.selectedEntry].end = ret[1]
1633 self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
1635 def changeDuration(self, entry):
1636 if entry is not None and entry >= 0:
1637 self.selectedEntry = entry
1638 self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1640 def inputCallback(self, value):
1641 if value is not None:
1642 print "stopping recording after", int(value), "minutes."
1643 entry = self.recording[self.selectedEntry]
1645 entry.autoincrease = False
1646 entry.end = int(time()) + 60 * int(value)
1647 self.session.nav.RecordTimer.timeChanged(entry)
1649 def instantRecord(self):
1650 dir = preferredInstantRecordPath()
1651 if not dir or not fileExists(dir, 'w'):
1652 dir = defaultMoviePath()
1656 # XXX: this message is a little odd as we might be recording to a remote device
1657 self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1660 if self.isInstantRecordRunning():
1661 self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1662 title=_("A recording is currently running.\nWhat do you want to do?"), \
1663 list=((_("stop recording"), "stop"), \
1664 (_("add recording (stop after current event)"), "event"), \
1665 (_("add recording (indefinitely)"), "indefinitely"), \
1666 (_("add recording (enter recording duration)"), "manualduration"), \
1667 (_("add recording (enter recording endtime)"), "manualendtime"), \
1668 (_("change recording (duration)"), "changeduration"), \
1669 (_("change recording (endtime)"), "changeendtime"), \
1670 (_("do nothing"), "no")))
1672 self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1673 title=_("Start recording?"), \
1674 list=((_("add recording (stop after current event)"), "event"), \
1675 (_("add recording (indefinitely)"), "indefinitely"), \
1676 (_("add recording (enter recording duration)"), "manualduration"), \
1677 (_("add recording (enter recording endtime)"), "manualendtime"), \
1678 (_("don't record"), "no")))
1680 from Tools.ISO639 import LanguageCodes
1682 class InfoBarAudioSelection:
1684 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions",
1686 "audioSelection": (self.audioSelection, _("Audio Options...")),
1689 def audioSelection(self):
1690 service = self.session.nav.getCurrentService()
1691 self.audioTracks = audio = service and service.audioTracks()
1692 n = audio and audio.getNumberOfTracks() or 0
1695 self.audioChannel = service.audioChannel()
1700 i = audio.getTrackInfo(idx)
1701 languages = i.getLanguage().split('/')
1702 description = i.getDescription()
1705 for lang in languages:
1708 if LanguageCodes.has_key(lang):
1709 language += LanguageCodes[lang][0]
1714 if len(description):
1715 description += " (" + language + ")"
1717 description = language
1719 tlist.append((description, idx))
1722 tlist.sort(key=lambda x: x[0])
1724 selectedAudio = self.audioTracks.getCurrentTrack()
1729 if x[1] != selectedAudio:
1737 if SystemInfo["CanDownmixAC3"]:
1738 flist = [(_("AC3 downmix") + " - " +(_("Off"), _("On"))[config.av.downmix_ac3.value and 1 or 0], "CALLFUNC", self.changeAC3Downmix),
1739 ((_("Left"), _("Stereo"), _("Right"))[self.audioChannel.getCurrentChannel()], "mode")]
1740 usedKeys.extend(["red", "green"])
1741 availableKeys.extend(["yellow", "blue"])
1744 flist = [((_("Left"), _("Stereo"), _("Right"))[self.audioChannel.getCurrentChannel()], "mode")]
1745 usedKeys.extend(["red"])
1746 availableKeys.extend(["green", "yellow", "blue"])
1749 if hasattr(self, "runPlugin"):
1751 def __init__(self, fnc, *args):
1754 def __call__(self, *args, **kwargs):
1755 self.fnc(*self.args)
1757 Plugins = [ (p.name, PluginCaller(self.runPlugin, p)) for p in plugins.getPlugins(where = PluginDescriptor.WHERE_AUDIOMENU) ]
1761 flist.append((p[0], "CALLFUNC", p[1]))
1763 usedKeys.append(availableKeys[0])
1764 del availableKeys[0]
1768 flist.append(("--", ""))
1772 keys = usedKeys + [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "0" ] + [""] * n
1773 self.session.openWithCallback(self.audioSelected, ChoiceBox, title=_("Select audio track"), list = flist + tlist, selection = selection, keys = keys, skin_name = "AudioTrackSelection")
1775 del self.audioTracks
1777 def changeAC3Downmix(self, arg):
1778 choicelist = self.session.current_dialog["list"]
1779 list = choicelist.list
1781 list[0][1]=(t[0], t[1], t[2], t[3], t[4], t[5], t[6],
1782 _("AC3 downmix") + " - " + (_("On"), _("Off"))[config.av.downmix_ac3.value and 1 or 0])
1783 choicelist.setList(list)
1784 if config.av.downmix_ac3.value:
1785 config.av.downmix_ac3.value = False
1787 config.av.downmix_ac3.value = True
1788 config.av.downmix_ac3.save()
1790 def audioSelected(self, audio):
1791 if audio is not None:
1792 if isinstance(audio[1], str):
1793 if audio[1] == "mode":
1794 keys = ["red", "green", "yellow"]
1795 selection = self.audioChannel.getCurrentChannel()
1796 tlist = ((_("left"), 0), (_("stereo"), 1), (_("right"), 2))
1797 self.session.openWithCallback(self.modeSelected, ChoiceBox, title=_("Select audio mode"), list = tlist, selection = selection, keys = keys, skin_name ="AudioModeSelection")
1799 del self.audioChannel
1800 if self.session.nav.getCurrentService().audioTracks().getNumberOfTracks() > audio[1]:
1801 self.audioTracks.selectTrack(audio[1])
1803 del self.audioChannel
1804 del self.audioTracks
1806 def modeSelected(self, mode):
1807 if mode is not None:
1808 self.audioChannel.selectChannel(mode[1])
1809 del self.audioChannel
1811 class InfoBarSubserviceSelection:
1813 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1815 "subserviceSelection": (self.subserviceSelection, _("Subservice list...")),
1818 self["SubserviceQuickzapAction"] = HelpableActionMap(self, "InfobarSubserviceQuickzapActions",
1820 "nextSubservice": (self.nextSubservice, _("Switch to next subservice")),
1821 "prevSubservice": (self.prevSubservice, _("Switch to previous subservice"))
1823 self["SubserviceQuickzapAction"].setEnabled(False)
1825 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1827 iPlayableService.evUpdatedEventInfo: self.checkSubservicesAvail
1832 def checkSubservicesAvail(self):
1833 service = self.session.nav.getCurrentService()
1834 subservices = service and service.subServices()
1835 if not subservices or subservices.getNumberOfSubservices() == 0:
1836 self["SubserviceQuickzapAction"].setEnabled(False)
1838 def nextSubservice(self):
1839 self.changeSubservice(+1)
1841 def prevSubservice(self):
1842 self.changeSubservice(-1)
1844 def changeSubservice(self, direction):
1845 service = self.session.nav.getCurrentService()
1846 subservices = service and service.subServices()
1847 n = subservices and subservices.getNumberOfSubservices()
1850 ref = self.session.nav.getCurrentlyPlayingServiceReference()
1853 if subservices.getSubservice(idx).toString() == ref.toString():
1858 selection += direction
1863 newservice = subservices.getSubservice(selection)
1864 if newservice.valid():
1867 self.session.nav.playService(newservice, False)
1869 def subserviceSelection(self):
1870 service = self.session.nav.getCurrentService()
1871 subservices = service and service.subServices()
1872 self.bouquets = self.servicelist.getBouquetList()
1873 n = subservices and subservices.getNumberOfSubservices()
1876 ref = self.session.nav.getCurrentlyPlayingServiceReference()
1880 i = subservices.getSubservice(idx)
1881 if i.toString() == ref.toString():
1883 tlist.append((i.getName(), i))
1886 if self.bouquets and len(self.bouquets):
1887 keys = ["red", "blue", "", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1888 if config.usage.multibouquet.value:
1889 tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to bouquet"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1891 tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to favourites"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1894 tlist = [(_("Quickzap"), "quickzap", service.subServices()), ("--", "")] + tlist
1895 keys = ["red", "", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1898 self.session.openWithCallback(self.subserviceSelected, ChoiceBox, title=_("Please select a subservice..."), list = tlist, selection = selection, keys = keys, skin_name = "SubserviceSelection")
1900 def subserviceSelected(self, service):
1902 if not service is None:
1903 if isinstance(service[1], str):
1904 if service[1] == "quickzap":
1905 from Screens.SubservicesQuickzap import SubservicesQuickzap
1906 self.session.open(SubservicesQuickzap, service[2])
1908 self["SubserviceQuickzapAction"].setEnabled(True)
1909 self.session.nav.playService(service[1], False)
1911 def addSubserviceToBouquetCallback(self, service):
1912 if len(service) > 1 and isinstance(service[1], eServiceReference):
1913 self.selectedSubservice = service
1914 if self.bouquets is None:
1917 cnt = len(self.bouquets)
1918 if cnt > 1: # show bouquet list
1919 self.bsel = self.session.openWithCallback(self.bouquetSelClosed, BouquetSelector, self.bouquets, self.addSubserviceToBouquet)
1920 elif cnt == 1: # add to only one existing bouquet
1921 self.addSubserviceToBouquet(self.bouquets[0][1])
1922 self.session.open(MessageBox, _("Service has been added to the favourites."), MessageBox.TYPE_INFO)
1924 def bouquetSelClosed(self, confirmed):
1926 del self.selectedSubservice
1928 self.session.open(MessageBox, _("Service has been added to the selected bouquet."), MessageBox.TYPE_INFO)
1930 def addSubserviceToBouquet(self, dest):
1931 self.servicelist.addServiceToBouquet(dest, self.selectedSubservice[1])
1933 self.bsel.close(True)
1935 del self.selectedSubservice
1937 class InfoBarAdditionalInfo:
1940 self["RecordingPossible"] = Boolean(fixed=harddiskmanager.HDDCount() > 0 and config.misc.rcused.value == 1)
1941 self["TimeshiftPossible"] = self["RecordingPossible"]
1942 self["ShowTimeshiftOnYellow"] = Boolean(fixed=(not config.misc.rcused.value == 0))
1943 self["ShowAudioOnYellow"] = Boolean(fixed=config.misc.rcused.value == 0)
1944 self["ShowRecordOnRed"] = Boolean(fixed=config.misc.rcused.value == 1)
1945 self["ExtensionsAvailable"] = Boolean(fixed=1)
1947 class InfoBarNotifications:
1949 self.onExecBegin.append(self.checkNotifications)
1950 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1951 self.onClose.append(self.__removeNotification)
1953 def __removeNotification(self):
1954 Notifications.notificationAdded.remove(self.checkNotificationsIfExecing)
1956 def checkNotificationsIfExecing(self):
1958 self.checkNotifications()
1960 def checkNotifications(self):
1961 notifications = Notifications.notifications
1963 n = notifications[0]
1965 del notifications[0]
1968 if n[3].has_key("onSessionOpenCallback"):
1969 n[3]["onSessionOpenCallback"]()
1970 del n[3]["onSessionOpenCallback"]
1973 dlg = self.session.openWithCallback(cb, n[1], *n[2], **n[3])
1975 dlg = self.session.open(n[1], *n[2], **n[3])
1977 # remember that this notification is currently active
1979 Notifications.current_notifications.append(d)
1980 dlg.onClose.append(boundFunction(self.__notificationClosed, d))
1982 def __notificationClosed(self, d):
1983 Notifications.current_notifications.remove(d)
1985 class InfoBarServiceNotifications:
1987 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1989 iPlayableService.evEnd: self.serviceHasEnded
1992 def serviceHasEnded(self):
1993 print "service end!"
1996 self.setSeekState(self.SEEK_STATE_PLAY)
2000 class InfoBarCueSheetSupport:
2006 ENABLE_RESUME_SUPPORT = False
2008 def __init__(self, actionmap = "InfobarCueSheetActions"):
2009 self["CueSheetActions"] = HelpableActionMap(self, actionmap,
2011 "jumpPreviousMark": (self.jumpPreviousMark, _("jump to previous marked position")),
2012 "jumpNextMark": (self.jumpNextMark, _("jump to next marked position")),
2013 "toggleMark": (self.toggleMark, _("toggle a cut mark at the current position"))
2017 self.is_closing = False
2018 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2020 iPlayableService.evStart: self.__serviceStarted,
2023 def __serviceStarted(self):
2026 print "new service started! trying to download cuts!"
2027 self.downloadCuesheet()
2029 if self.ENABLE_RESUME_SUPPORT:
2032 for (pts, what) in self.cut_list:
2033 if what == self.CUT_TYPE_LAST:
2036 if last is not None:
2037 self.resume_point = last
2040 if config.usage.on_movie_start.value == "ask":
2041 Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Do you want to resume this playback?") + "\n" + (_("Resume position at %s") % ("%d:%02d:%02d" % (l/3600, l%3600/60, l%60))), timeout=10)
2042 elif config.usage.on_movie_start.value == "resume":
2043 # TRANSLATORS: The string "Resuming playback" flashes for a moment
2044 # TRANSLATORS: at the start of a movie, when the user has selected
2045 # TRANSLATORS: "Resume from last position" as start behavior.
2046 # TRANSLATORS: The purpose is to notify the user that the movie starts
2047 # TRANSLATORS: in the middle somewhere and not from the beginning.
2048 # TRANSLATORS: (Some translators seem to have interpreted it as a
2049 # TRANSLATORS: question or a choice, but it is a statement.)
2050 Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Resuming playback"), timeout=2, type=MessageBox.TYPE_INFO)
2052 def playLastCB(self, answer):
2054 self.doSeek(self.resume_point)
2055 self.hideAfterResume()
2057 def hideAfterResume(self):
2058 if isinstance(self, InfoBarShowHide):
2061 def __getSeekable(self):
2062 service = self.session.nav.getCurrentService()
2065 return service.seek()
2067 def cueGetCurrentPosition(self):
2068 seek = self.__getSeekable()
2071 r = seek.getPlayPosition()
2076 def cueGetEndCutPosition(self):
2079 for cp in self.cut_list:
2080 if cp[1] == self.CUT_TYPE_OUT:
2084 elif cp[1] == self.CUT_TYPE_IN:
2088 def jumpPreviousNextMark(self, cmp, start=False):
2089 current_pos = self.cueGetCurrentPosition()
2090 if current_pos is None:
2092 mark = self.getNearestCutPoint(current_pos, cmp=cmp, start=start)
2093 if mark is not None:
2101 def jumpPreviousMark(self):
2102 # we add 2 seconds, so if the play position is <2s after
2103 # the mark, the mark before will be used
2104 self.jumpPreviousNextMark(lambda x: -x-5*90000, start=True)
2106 def jumpNextMark(self):
2107 if not self.jumpPreviousNextMark(lambda x: x):
2110 def getNearestCutPoint(self, pts, cmp=abs, start=False):
2116 bestdiff = cmp(0 - pts)
2118 nearest = [0, False]
2119 for cp in self.cut_list:
2120 if beforecut and cp[1] in (self.CUT_TYPE_IN, self.CUT_TYPE_OUT):
2122 if cp[1] == self.CUT_TYPE_IN: # Start is here, disregard previous marks
2123 diff = cmp(cp[0] - pts)
2129 if cp[1] in (self.CUT_TYPE_MARK, self.CUT_TYPE_LAST):
2130 diff = cmp(cp[0] - pts)
2131 if diff >= 0 and (nearest is None or bestdiff > diff):
2136 def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
2137 current_pos = self.cueGetCurrentPosition()
2138 if current_pos is None:
2139 print "not seekable"
2142 nearest_cutpoint = self.getNearestCutPoint(current_pos)
2144 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
2146 return nearest_cutpoint
2148 self.removeMark(nearest_cutpoint)
2149 elif not onlyremove and not onlyreturn:
2150 self.addMark((current_pos, self.CUT_TYPE_MARK))
2155 def addMark(self, point):
2156 insort(self.cut_list, point)
2157 self.uploadCuesheet()
2158 self.showAfterCuesheetOperation()
2160 def removeMark(self, point):
2161 self.cut_list.remove(point)
2162 self.uploadCuesheet()
2163 self.showAfterCuesheetOperation()
2165 def showAfterCuesheetOperation(self):
2166 if isinstance(self, InfoBarShowHide):
2169 def __getCuesheet(self):
2170 service = self.session.nav.getCurrentService()
2173 return service.cueSheet()
2175 def uploadCuesheet(self):
2176 cue = self.__getCuesheet()
2179 print "upload failed, no cuesheet interface"
2181 cue.setCutList(self.cut_list)
2183 def downloadCuesheet(self):
2184 cue = self.__getCuesheet()
2187 print "download failed, no cuesheet interface"
2190 self.cut_list = cue.getCutList()
2192 class InfoBarSummary(Screen):
2194 <screen position="0,0" size="132,64">
2195 <widget source="global.CurrentTime" render="Label" position="62,46" size="82,18" font="Regular;16" >
2196 <convert type="ClockToText">WithSeconds</convert>
2198 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="82,18" zPosition="1" >
2199 <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2200 <convert type="ConditionalShowHide">Blink</convert>
2202 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2203 <convert type="ServiceName">Name</convert>
2205 <widget source="session.Event_Now" render="Progress" position="6,46" size="46,18" borderWidth="1" >
2206 <convert type="EventTime">Progress</convert>
2210 # for picon: (path="piconlcd" will use LCD picons)
2211 # <widget source="session.CurrentService" render="Picon" position="6,0" size="120,64" path="piconlcd" >
2212 # <convert type="ServiceName">Reference</convert>
2215 class InfoBarSummarySupport:
2219 def createSummary(self):
2220 return InfoBarSummary
2222 class InfoBarMoviePlayerSummary(Screen):
2224 <screen position="0,0" size="132,64">
2225 <widget source="global.CurrentTime" render="Label" position="62,46" size="64,18" font="Regular;16" halign="right" >
2226 <convert type="ClockToText">WithSeconds</convert>
2228 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="64,18" zPosition="1" >
2229 <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2230 <convert type="ConditionalShowHide">Blink</convert>
2232 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2233 <convert type="ServiceName">Name</convert>
2235 <widget source="session.CurrentService" render="Progress" position="6,46" size="56,18" borderWidth="1" >
2236 <convert type="ServicePosition">Position</convert>
2240 class InfoBarMoviePlayerSummarySupport:
2244 def createSummary(self):
2245 return InfoBarMoviePlayerSummary
2247 class InfoBarTeletextPlugin:
2249 self.teletext_plugin = None
2251 for p in plugins.getPlugins(PluginDescriptor.WHERE_TELETEXT):
2252 self.teletext_plugin = p
2254 if self.teletext_plugin is not None:
2255 self["TeletextActions"] = HelpableActionMap(self, "InfobarTeletextActions",
2257 "startTeletext": (self.startTeletext, _("View teletext..."))
2260 print "no teletext plugin found!"
2262 def startTeletext(self):
2263 self.teletext_plugin(session=self.session, service=self.session.nav.getCurrentService())
2265 class InfoBarSubtitleSupport(object):
2267 object.__init__(self)
2268 self.subtitle_window = self.session.instantiateDialog(SubtitleDisplay)
2269 self.__subtitles_enabled = False
2271 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2273 iPlayableService.evEnd: self.__serviceStopped,
2274 iPlayableService.evUpdatedInfo: self.__updatedInfo
2276 self.cached_subtitle_checked = False
2277 self.__selected_subtitle = None
2279 def __serviceStopped(self):
2280 self.cached_subtitle_checked = False
2281 if self.__subtitles_enabled:
2282 self.subtitle_window.hide()
2283 self.__subtitles_enabled = False
2284 self.__selected_subtitle = None
2286 def __updatedInfo(self):
2287 if not self.cached_subtitle_checked:
2288 self.cached_subtitle_checked = True
2289 subtitle = self.getCurrentServiceSubtitle()
2290 self.setSelectedSubtitle(subtitle and subtitle.getCachedSubtitle())
2291 if self.__selected_subtitle:
2292 self.setSubtitlesEnable(True)
2294 def getCurrentServiceSubtitle(self):
2295 service = self.session.nav.getCurrentService()
2296 return service and service.subtitle()
2298 def setSubtitlesEnable(self, enable=True):
2299 subtitle = self.getCurrentServiceSubtitle()
2301 if self.__selected_subtitle:
2302 if subtitle and not self.__subtitles_enabled:
2303 subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
2304 self.subtitle_window.show()
2305 self.__subtitles_enabled = True
2308 subtitle.disableSubtitles(self.subtitle_window.instance)
2309 self.__selected_subtitle = False
2310 self.__subtitles_enabled = False
2311 self.subtitle_window.hide()
2313 def setSelectedSubtitle(self, subtitle):
2314 self.__selected_subtitle = subtitle
2316 subtitles_enabled = property(lambda self: self.__subtitles_enabled, setSubtitlesEnable)
2317 selected_subtitle = property(lambda self: self.__selected_subtitle, setSelectedSubtitle)
2319 class InfoBarServiceErrorPopupSupport:
2321 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2323 iPlayableService.evTuneFailed: self.__tuneFailed,
2324 iPlayableService.evStart: self.__serviceStarted
2326 self.__serviceStarted()
2328 def __serviceStarted(self):
2329 self.last_error = None
2330 Notifications.RemovePopup(id = "ZapError")
2332 def __tuneFailed(self):
2333 service = self.session.nav.getCurrentService()
2334 info = service and service.info()
2335 error = info and info.getInfo(iServiceInformation.sDVBState)
2337 if error == self.last_error:
2340 self.last_error = error
2343 eDVBServicePMTHandler.eventNoResources: _("No free tuner!"),
2344 eDVBServicePMTHandler.eventTuneFailed: _("Tune failed!"),
2345 eDVBServicePMTHandler.eventNoPAT: _("No data on transponder!\n(Timeout reading PAT)"),
2346 eDVBServicePMTHandler.eventNoPATEntry: _("Service not found!\n(SID not found in PAT)"),
2347 eDVBServicePMTHandler.eventNoPMT: _("Service invalid!\n(Timeout reading PMT)"),
2348 eDVBServicePMTHandler.eventNewProgramInfo: None,
2349 eDVBServicePMTHandler.eventTuned: None,
2350 eDVBServicePMTHandler.eventSOF: None,
2351 eDVBServicePMTHandler.eventEOF: None,
2352 eDVBServicePMTHandler.eventMisconfiguration: _("Service unavailable!\nCheck tuner configuration!"),
2353 }.get(error) #this returns None when the key not exist in the dict
2355 if error is not None:
2356 Notifications.AddPopup(text = error, type = MessageBox.TYPE_ERROR, timeout = 5, id = "ZapError")
2358 Notifications.RemovePopup(id = "ZapError")