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, system as os_system
40 from bisect import insort
42 from RecordTimer import RecordTimerEntry, RecordTimer, findSafeRecordPath
45 from Menu import MainMenu, mdom
49 self.dishDialog = self.session.instantiateDialog(Dish)
50 self.dishDialog.setAnimationMode(0)
52 class InfoBarUnhandledKey:
54 self.unhandledKeyDialog = self.session.instantiateDialog(UnhandledKey)
55 self.unhandledKeyDialog.setAnimationMode(0)
56 self.hideUnhandledKeySymbolTimer = eTimer()
57 self.hideUnhandledKeySymbolTimer.callback.append(self.unhandledKeyDialog.hide)
58 self.checkUnusedTimer = eTimer()
59 self.checkUnusedTimer.callback.append(self.checkUnused)
60 self.onLayoutFinish.append(self.unhandledKeyDialog.hide)
61 eActionMap.getInstance().bindAction('', -0x7FFFFFFF, self.actionA) #highest prio
62 eActionMap.getInstance().bindAction('', 0x7FFFFFFF, self.actionB) #lowest prio
66 #this function is called on every keypress!
67 def actionA(self, key, flag):
69 if self.flags & (1<<1):
70 self.flags = self.uflags = 0
71 self.flags |= (1<<flag)
73 self.checkUnusedTimer.start(0, True)
76 #this function is only called when no other action has handled this key
77 def actionB(self, key, flag):
79 self.uflags |= (1<<flag)
81 def checkUnused(self):
82 if self.flags == self.uflags:
83 self.unhandledKeyDialog.show()
84 self.hideUnhandledKeySymbolTimer.start(2000, True)
86 class InfoBarShowHide:
87 """ InfoBar show/hide control, accepts toggleShow and hide actions, might start
95 self["ShowHideActions"] = ActionMap( ["InfobarShowHideActions"] ,
97 "toggleShow": self.toggleShow,
99 }, 1) # lower prio to make it possible to override ok and cancel..
101 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
103 iPlayableService.evStart: self.serviceStarted,
106 self.__state = self.STATE_SHOWN
109 self.hideTimer = eTimer()
110 self.hideTimer.callback.append(self.doTimerHide)
111 self.hideTimer.start(5000, True)
113 self.onShow.append(self.__onShow)
114 self.onHide.append(self.__onHide)
116 def serviceStarted(self):
118 if config.usage.show_infobar_on_zap.value:
122 self.__state = self.STATE_SHOWN
123 self.startHideTimer()
125 def startHideTimer(self):
126 if self.__state == self.STATE_SHOWN and not self.__locked:
127 idx = config.usage.infobar_timeout.index
129 self.hideTimer.start(idx*1000, True)
132 self.__state = self.STATE_HIDDEN
136 self.startHideTimer()
138 def doTimerHide(self):
139 self.hideTimer.stop()
140 if self.__state == self.STATE_SHOWN:
143 def toggleShow(self):
144 if self.__state == self.STATE_SHOWN:
146 self.hideTimer.stop()
147 elif self.__state == self.STATE_HIDDEN:
151 self.__locked = self.__locked + 1
154 self.hideTimer.stop()
156 def unlockShow(self):
157 self.__locked = self.__locked - 1
159 self.startHideTimer()
161 # def startShow(self):
162 # self.instance.m_animation.startMoveAnimation(ePoint(0, 600), ePoint(0, 380), 100)
163 # self.__state = self.STATE_SHOWN
165 # def startHide(self):
166 # self.instance.m_animation.startMoveAnimation(ePoint(0, 380), ePoint(0, 600), 100)
167 # self.__state = self.STATE_HIDDEN
169 class NumberZap(Screen):
176 self.close(int(self["number"].getText()))
178 def keyNumberGlobal(self, number):
179 self.Timer.start(3000, True) #reset timer
180 self.field = self.field + str(number)
181 self["number"].setText(self.field)
182 if len(self.field) >= 4:
185 def __init__(self, session, number):
186 Screen.__init__(self, session)
187 self.field = str(number)
189 self["channel"] = Label(_("Channel:"))
191 self["number"] = Label(self.field)
193 self["actions"] = NumberActionMap( [ "SetupActions" ],
197 "1": self.keyNumberGlobal,
198 "2": self.keyNumberGlobal,
199 "3": self.keyNumberGlobal,
200 "4": self.keyNumberGlobal,
201 "5": self.keyNumberGlobal,
202 "6": self.keyNumberGlobal,
203 "7": self.keyNumberGlobal,
204 "8": self.keyNumberGlobal,
205 "9": self.keyNumberGlobal,
206 "0": self.keyNumberGlobal
209 self.Timer = eTimer()
210 self.Timer.callback.append(self.keyOK)
211 self.Timer.start(3000, True)
213 class InfoBarNumberZap:
214 """ Handles an initial number for NumberZapping """
216 self["NumberActions"] = NumberActionMap( [ "NumberActions"],
218 "1": self.keyNumberGlobal,
219 "2": self.keyNumberGlobal,
220 "3": self.keyNumberGlobal,
221 "4": self.keyNumberGlobal,
222 "5": self.keyNumberGlobal,
223 "6": self.keyNumberGlobal,
224 "7": self.keyNumberGlobal,
225 "8": self.keyNumberGlobal,
226 "9": self.keyNumberGlobal,
227 "0": self.keyNumberGlobal,
230 def keyNumberGlobal(self, number):
231 # print "You pressed number " + str(number)
233 if isinstance(self, InfoBarPiP) and self.pipHandles0Action():
234 self.pipDoHandle0Action()
236 self.servicelist.recallPrevService()
238 if self.has_key("TimeshiftActions") and not self.timeshift_enabled:
239 self.session.openWithCallback(self.numberEntered, NumberZap, number)
241 def numberEntered(self, retval):
242 # print self.servicelist
244 self.zapToNumber(retval)
246 def searchNumberHelper(self, serviceHandler, num, bouquet):
247 servicelist = serviceHandler.list(bouquet)
248 if not servicelist is None:
250 serviceIterator = servicelist.getNext()
251 if not serviceIterator.valid(): #check end of list
253 playable = not (serviceIterator.flags & (eServiceReference.isMarker|eServiceReference.isDirectory))
256 if not num: #found service with searched number ?
257 return serviceIterator, 0
260 def zapToNumber(self, number):
261 bouquet = self.servicelist.bouquet_root
263 serviceHandler = eServiceCenter.getInstance()
264 if not config.usage.multibouquet.value:
265 service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
267 bouquetlist = serviceHandler.list(bouquet)
268 if not bouquetlist is None:
270 bouquet = bouquetlist.getNext()
271 if not bouquet.valid(): #check end of list
273 if bouquet.flags & eServiceReference.isDirectory:
274 service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
275 if not service is None:
276 if self.servicelist.getRoot() != bouquet: #already in correct bouquet?
277 self.servicelist.clearPath()
278 if self.servicelist.bouquet_root != bouquet:
279 self.servicelist.enterPath(self.servicelist.bouquet_root)
280 self.servicelist.enterPath(bouquet)
281 self.servicelist.setCurrentSelection(service) #select the service in servicelist
282 self.servicelist.zap()
284 config.misc.initialchannelselection = ConfigBoolean(default = True)
286 class InfoBarChannelSelection:
287 """ ChannelSelection - handles the channelSelection dialog and the initial
288 channelChange actions which open the channelSelection dialog """
291 self.servicelist = self.session.instantiateDialog(ChannelSelection)
293 if config.misc.initialchannelselection.value:
294 self.onShown.append(self.firstRun)
296 self["ChannelSelectActions"] = HelpableActionMap(self, "InfobarChannelSelection",
298 "switchChannelUp": (self.switchChannelUp, _("open servicelist(up)")),
299 "switchChannelDown": (self.switchChannelDown, _("open servicelist(down)")),
300 "zapUp": (self.zapUp, _("previous channel")),
301 "zapDown": (self.zapDown, _("next channel")),
302 "historyBack": (self.historyBack, _("previous channel in history")),
303 "historyNext": (self.historyNext, _("next channel in history")),
304 "openServiceList": (self.openServiceList, _("open servicelist")),
307 def showTvChannelList(self, zap=False):
308 self.servicelist.setModeTv()
310 self.servicelist.zap()
311 self.session.execDialog(self.servicelist)
313 def showRadioChannelList(self, zap=False):
314 self.servicelist.setModeRadio()
316 self.servicelist.zap()
317 self.session.execDialog(self.servicelist)
320 self.onShown.remove(self.firstRun)
321 config.misc.initialchannelselection.value = False
322 config.misc.initialchannelselection.save()
323 self.switchChannelDown()
325 def historyBack(self):
326 self.servicelist.historyBack()
328 def historyNext(self):
329 self.servicelist.historyNext()
331 def switchChannelUp(self):
332 self.servicelist.moveUp()
333 self.session.execDialog(self.servicelist)
335 def switchChannelDown(self):
336 self.servicelist.moveDown()
337 self.session.execDialog(self.servicelist)
339 def openServiceList(self):
340 self.session.execDialog(self.servicelist)
343 if self.servicelist.inBouquet():
344 prev = self.servicelist.getCurrentSelection()
346 prev = prev.toString()
348 if config.usage.quickzap_bouquet_change.value:
349 if self.servicelist.atBegin():
350 self.servicelist.prevBouquet()
351 self.servicelist.moveUp()
352 cur = self.servicelist.getCurrentSelection()
353 if not cur or (not (cur.flags & 64)) or cur.toString() == prev:
356 self.servicelist.moveUp()
357 self.servicelist.zap()
360 if self.servicelist.inBouquet():
361 prev = self.servicelist.getCurrentSelection()
363 prev = prev.toString()
365 if config.usage.quickzap_bouquet_change.value and self.servicelist.atEnd():
366 self.servicelist.nextBouquet()
368 self.servicelist.moveDown()
369 cur = self.servicelist.getCurrentSelection()
370 if not cur or (not (cur.flags & 64)) or cur.toString() == prev:
373 self.servicelist.moveDown()
374 self.servicelist.zap()
377 """ Handles a menu action, to open the (main) menu """
379 self["MenuActions"] = HelpableActionMap(self, "InfobarMenuActions",
381 "mainMenu": (self.mainMenu, _("Enter main menu...")),
383 self.session.infobar = None
386 print "loading mainmenu XML..."
387 menu = mdom.getroot()
388 assert menu.tag == "menu", "root element in menu must be 'menu'!"
390 self.session.infobar = self
391 # so we can access the currently active infobar from screens opened from within the mainmenu
392 # at the moment used from the SubserviceSelection
394 self.session.openWithCallback(self.mainMenuClosed, MainMenu, menu)
396 def mainMenuClosed(self, *val):
397 self.session.infobar = None
399 class InfoBarSimpleEventView:
400 """ Opens the Eventview for now/next """
402 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
404 "showEventInfo": (self.openEventView, _("show event details")),
405 "showInfobarOrEpgWhenInfobarAlreadyVisible": self.showEventInfoWhenNotVisible,
408 def showEventInfoWhenNotVisible(self):
415 def openEventView(self):
417 self.epglist = epglist
418 service = self.session.nav.getCurrentService()
419 ref = self.session.nav.getCurrentlyPlayingServiceReference()
420 info = service.info()
428 self.session.open(EventViewSimple, epglist[0], ServiceReference(ref), self.eventViewCallback)
430 def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
431 epglist = self.epglist
434 epglist[0] = epglist[1]
438 class SimpleServicelist:
439 def __init__(self, services):
440 self.services = services
441 self.length = len(services)
444 def selectService(self, service):
450 while self.services[self.current].ref != service:
452 if self.current >= self.length:
456 def nextService(self):
459 if self.current+1 < self.length:
464 def prevService(self):
467 if self.current-1 > -1:
470 self.current = self.length - 1
472 def currentService(self):
473 if not self.length or self.current >= self.length:
475 return self.services[self.current]
478 """ EPG - Opens an EPG list when the showEPGList action fires """
480 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
482 iPlayableService.evUpdatedEventInfo: self.__evEventInfoChanged,
485 self.is_now_next = False
487 self.bouquetSel = None
488 self.eventView = None
489 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
491 "showEventInfo": (self.openEventView, _("show EPG...")),
492 "showEventInfoPlugin": (self.showEventInfoPlugins, _("list of EPG views...")),
493 "showInfobarOrEpgWhenInfobarAlreadyVisible": self.showEventInfoWhenNotVisible,
496 def showEventInfoWhenNotVisible(self):
503 def zapToService(self, service):
504 if not service is None:
505 if self.servicelist.getRoot() != self.epg_bouquet: #already in correct bouquet?
506 self.servicelist.clearPath()
507 if self.servicelist.bouquet_root != self.epg_bouquet:
508 self.servicelist.enterPath(self.servicelist.bouquet_root)
509 self.servicelist.enterPath(self.epg_bouquet)
510 self.servicelist.setCurrentSelection(service) #select the service in servicelist
511 self.servicelist.zap()
513 def getBouquetServices(self, bouquet):
515 servicelist = eServiceCenter.getInstance().list(bouquet)
516 if not servicelist is None:
518 service = servicelist.getNext()
519 if not service.valid(): #check if end of list
521 if service.flags & (eServiceReference.isDirectory | eServiceReference.isMarker): #ignore non playable services
523 services.append(ServiceReference(service))
526 def openBouquetEPG(self, bouquet, withCallback=True):
527 services = self.getBouquetServices(bouquet)
529 self.epg_bouquet = bouquet
531 self.dlg_stack.append(self.session.openWithCallback(self.closed, EPGSelection, services, self.zapToService, None, self.changeBouquetCB))
533 self.session.open(EPGSelection, services, self.zapToService, None, self.changeBouquetCB)
535 def changeBouquetCB(self, direction, epg):
538 self.bouquetSel.down()
541 bouquet = self.bouquetSel.getCurrent()
542 services = self.getBouquetServices(bouquet)
544 self.epg_bouquet = bouquet
545 epg.setServices(services)
547 def closed(self, ret=False):
548 closedScreen = self.dlg_stack.pop()
549 if self.bouquetSel and closedScreen == self.bouquetSel:
550 self.bouquetSel = None
551 elif self.eventView and closedScreen == self.eventView:
552 self.eventView = None
554 dlgs=len(self.dlg_stack)
556 self.dlg_stack[dlgs-1].close(dlgs > 1)
558 def openMultiServiceEPG(self, withCallback=True):
559 bouquets = self.servicelist.getBouquetList()
564 if config.usage.multiepg_ask_bouquet.value:
565 self.openMultiServiceEPGAskBouquet(bouquets, cnt, withCallback)
567 self.openMultiServiceEPGSilent(bouquets, cnt, withCallback)
569 def openMultiServiceEPGAskBouquet(self, bouquets, cnt, withCallback):
570 if cnt > 1: # show bouquet list
572 self.bouquetSel = self.session.openWithCallback(self.closed, BouquetSelector, bouquets, self.openBouquetEPG, enableWrapAround=True)
573 self.dlg_stack.append(self.bouquetSel)
575 self.bouquetSel = self.session.open(BouquetSelector, bouquets, self.openBouquetEPG, enableWrapAround=True)
577 self.openBouquetEPG(bouquets[0][1], withCallback)
579 def openMultiServiceEPGSilent(self, bouquets, cnt, withCallback):
580 root = self.servicelist.getRoot()
581 rootstr = root.toCompareString()
583 for bouquet in bouquets:
584 if bouquet[1].toCompareString() == rootstr:
589 if cnt > 1: # create bouquet list for bouq+/-
590 self.bouquetSel = SilentBouquetSelector(bouquets, True, self.servicelist.getBouquetNumOffset(root))
592 self.openBouquetEPG(root, withCallback)
594 def changeServiceCB(self, direction, epg):
597 self.serviceSel.nextService()
599 self.serviceSel.prevService()
600 epg.setService(self.serviceSel.currentService())
602 def SingleServiceEPGClosed(self, ret=False):
603 self.serviceSel = None
605 def openSingleServiceEPG(self):
606 ref=self.session.nav.getCurrentlyPlayingServiceReference()
608 if self.servicelist.getMutableList() is not None: # bouquet in channellist
609 current_path = self.servicelist.getRoot()
610 services = self.getBouquetServices(current_path)
611 self.serviceSel = SimpleServicelist(services)
612 if self.serviceSel.selectService(ref):
613 self.session.openWithCallback(self.SingleServiceEPGClosed, EPGSelection, ref, serviceChangeCB = self.changeServiceCB)
615 self.session.openWithCallback(self.SingleServiceEPGClosed, EPGSelection, ref)
617 self.session.open(EPGSelection, ref)
619 def showEventInfoPlugins(self):
620 list = [(p.name, boundFunction(self.runPlugin, p)) for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EVENTINFO)]
623 list.append((_("show single service EPG..."), self.openSingleServiceEPG))
624 list.append((_("Multi EPG"), self.openMultiServiceEPG))
625 self.session.openWithCallback(self.EventInfoPluginChosen, ChoiceBox, title=_("Please choose an extension..."), list = list, skin_name = "EPGExtensionsList")
627 self.openSingleServiceEPG()
629 def runPlugin(self, plugin):
630 plugin(session = self.session, servicelist = self.servicelist)
632 def EventInfoPluginChosen(self, answer):
633 if answer is not None:
636 def openSimilarList(self, eventid, refstr):
637 self.session.open(EPGSelection, refstr, None, eventid)
639 def getNowNext(self):
641 service = self.session.nav.getCurrentService()
642 info = service and service.info()
643 ptr = info and info.getEvent(0)
646 ptr = info and info.getEvent(1)
649 self.epglist = epglist
651 def __evEventInfoChanged(self):
652 if self.is_now_next and len(self.dlg_stack) == 1:
654 assert self.eventView
656 self.eventView.setEvent(self.epglist[0])
658 def openEventView(self):
659 ref = self.session.nav.getCurrentlyPlayingServiceReference()
661 epglist = self.epglist
663 self.is_now_next = False
664 epg = eEPGCache.getInstance()
665 ptr = ref and ref.valid() and epg.lookupEventTime(ref, -1)
668 ptr = epg.lookupEventTime(ref, ptr.getBeginTime(), +1)
672 self.is_now_next = True
674 self.eventView = self.session.openWithCallback(self.closed, EventViewEPGSelect, self.epglist[0], ServiceReference(ref), self.eventViewCallback, self.openSingleServiceEPG, self.openMultiServiceEPG, self.openSimilarList)
675 self.dlg_stack.append(self.eventView)
677 print "no epg for the service avail.. so we show multiepg instead of eventinfo"
678 self.openMultiServiceEPG(False)
680 def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
681 epglist = self.epglist
684 epglist[0]=epglist[1]
688 class InfoBarRdsDecoder:
689 """provides RDS and Rass support/display"""
691 self.rds_display = self.session.instantiateDialog(RdsInfoDisplay)
692 self.rds_display.setAnimationMode(0)
693 self.rass_interactive = None
695 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
697 iPlayableService.evEnd: self.__serviceStopped,
698 iPlayableService.evUpdatedRassSlidePic: self.RassSlidePicChanged
701 self["RdsActions"] = ActionMap(["InfobarRdsActions"],
703 "startRassInteractive": self.startRassInteractive
706 self["RdsActions"].setEnabled(False)
708 self.onLayoutFinish.append(self.rds_display.show)
709 self.rds_display.onRassInteractivePossibilityChanged.append(self.RassInteractivePossibilityChanged)
711 def RassInteractivePossibilityChanged(self, state):
712 self["RdsActions"].setEnabled(state)
714 def RassSlidePicChanged(self):
715 if not self.rass_interactive:
716 service = self.session.nav.getCurrentService()
717 decoder = service and service.rdsDecoder()
719 decoder.showRassSlidePicture()
721 def __serviceStopped(self):
722 if self.rass_interactive is not None:
723 rass_interactive = self.rass_interactive
724 self.rass_interactive = None
725 rass_interactive.close()
727 def startRassInteractive(self):
728 self.rds_display.hide()
729 self.rass_interactive = self.session.openWithCallback(self.RassInteractiveClosed, RassInteractive)
731 def RassInteractiveClosed(self, *val):
732 if self.rass_interactive is not None:
733 self.rass_interactive = None
734 self.RassSlidePicChanged()
735 self.rds_display.show()
738 """handles actions like seeking, pause"""
740 SEEK_STATE_PLAY = (0, 0, 0, ">")
741 SEEK_STATE_PAUSE = (1, 0, 0, "||")
742 SEEK_STATE_EOF = (1, 0, 0, "END")
744 def __init__(self, actionmap = "InfobarSeekActions"):
745 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
747 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
748 iPlayableService.evStart: self.__serviceStarted,
750 iPlayableService.evEOF: self.__evEOF,
751 iPlayableService.evSOF: self.__evSOF,
753 self.fast_winding_hint_message_showed = False
755 class InfoBarSeekActionMap(HelpableActionMap):
756 def __init__(self, screen, *args, **kwargs):
757 HelpableActionMap.__init__(self, screen, *args, **kwargs)
760 def action(self, contexts, action):
761 print "action:", action
762 if action[:5] == "seek:":
763 time = int(action[5:])
764 self.screen.doSeekRelative(time * 90000)
766 elif action[:8] == "seekdef:":
767 key = int(action[8:])
768 time = (-config.seek.selfdefined_13.value, False, config.seek.selfdefined_13.value,
769 -config.seek.selfdefined_46.value, False, config.seek.selfdefined_46.value,
770 -config.seek.selfdefined_79.value, False, config.seek.selfdefined_79.value)[key-1]
771 self.screen.doSeekRelative(time * 90000)
774 return HelpableActionMap.action(self, contexts, action)
776 self["SeekActions"] = InfoBarSeekActionMap(self, actionmap,
778 "playpauseService": (self.playpauseService, _("Pause/Continue")),
779 "pauseService": (self.pauseService, _("pause")),
780 "unPauseService": (self.unPauseService, _("continue")),
782 "seekFwd": (self.seekFwd, _("skip forward")),
783 "seekFwdManual": (self.seekFwdManual, _("skip forward (enter time)")),
784 "seekBack": (self.seekBack, _("skip backward")),
785 "seekBackManual": (self.seekBackManual, _("skip backward (enter time)"))
787 # give them a little more priority to win over color buttons
789 self["SeekActions"].setEnabled(False)
791 self.seekstate = self.SEEK_STATE_PLAY
792 self.lastseekstate = self.SEEK_STATE_PLAY
794 self.onPlayStateChanged = [ ]
796 self.lockedBecauseOfSkipping = False
798 self.__seekableStatusChanged()
800 def makeStateForward(self, n):
801 return (0, n, 0, ">> %dx" % n)
803 def makeStateBackward(self, n):
804 return (0, -n, 0, "<< %dx" % n)
806 def makeStateSlowMotion(self, n):
807 return (0, 0, n, "/%d" % n)
809 def isStateForward(self, state):
812 def isStateBackward(self, state):
815 def isStateSlowMotion(self, state):
816 return state[1] == 0 and state[2] > 1
818 def getHigher(self, n, lst):
824 def getLower(self, n, lst):
832 def showAfterSeek(self):
833 if isinstance(self, InfoBarShowHide):
843 service = self.session.nav.getCurrentService()
847 seek = service.seek()
849 if seek is None or not seek.isCurrentlySeekable():
854 def isSeekable(self):
855 if self.getSeek() is None:
859 def __seekableStatusChanged(self):
860 # print "seekable status changed!"
861 if not self.isSeekable():
862 self["SeekActions"].setEnabled(False)
863 # print "not seekable, return to play"
864 self.setSeekState(self.SEEK_STATE_PLAY)
866 self["SeekActions"].setEnabled(True)
869 def __serviceStarted(self):
870 self.fast_winding_hint_message_showed = False
871 self.seekstate = self.SEEK_STATE_PLAY
872 self.__seekableStatusChanged()
874 def setSeekState(self, state):
875 service = self.session.nav.getCurrentService()
880 if not self.isSeekable():
881 if state not in (self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE):
882 state = self.SEEK_STATE_PLAY
884 pauseable = service.pause()
886 if pauseable is None:
887 print "not pauseable."
888 state = self.SEEK_STATE_PLAY
890 self.seekstate = state
892 if pauseable is not None:
893 if self.seekstate[0]:
894 print "resolved to PAUSE"
896 elif self.seekstate[1]:
897 print "resolved to FAST FORWARD"
898 pauseable.setFastForward(self.seekstate[1])
899 elif self.seekstate[2]:
900 print "resolved to SLOW MOTION"
901 pauseable.setSlowMotion(self.seekstate[2])
903 print "resolved to PLAY"
906 for c in self.onPlayStateChanged:
909 self.checkSkipShowHideLock()
913 def playpauseService(self):
914 if self.seekstate != self.SEEK_STATE_PLAY:
915 self.unPauseService()
919 def pauseService(self):
920 if self.seekstate == self.SEEK_STATE_PAUSE:
921 if config.seek.on_pause.value == "play":
922 self.unPauseService()
923 elif config.seek.on_pause.value == "step":
924 self.doSeekRelative(1)
925 elif config.seek.on_pause.value == "last":
926 self.setSeekState(self.lastseekstate)
927 self.lastseekstate = self.SEEK_STATE_PLAY
929 if self.seekstate != self.SEEK_STATE_EOF:
930 self.lastseekstate = self.seekstate
931 self.setSeekState(self.SEEK_STATE_PAUSE);
933 def unPauseService(self):
935 if self.seekstate == self.SEEK_STATE_PLAY:
937 self.setSeekState(self.SEEK_STATE_PLAY)
939 def doSeek(self, pts):
940 seekable = self.getSeek()
945 def doSeekRelative(self, pts):
946 seekable = self.getSeek()
949 prevstate = self.seekstate
951 if self.seekstate == self.SEEK_STATE_EOF:
952 if prevstate == self.SEEK_STATE_PAUSE:
953 self.setSeekState(self.SEEK_STATE_PAUSE)
955 self.setSeekState(self.SEEK_STATE_PLAY)
956 seekable.seekRelative(pts<0 and -1 or 1, abs(pts))
957 if abs(pts) > 100 and config.usage.show_infobar_on_skip.value:
961 seek = self.getSeek()
962 if seek and not (seek.isCurrentlySeekable() & 2):
963 if not self.fast_winding_hint_message_showed and (seek.isCurrentlySeekable() & 1):
964 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)
965 self.fast_winding_hint_message_showed = True
967 return 0 # trade as unhandled action
968 if self.seekstate == self.SEEK_STATE_PLAY:
969 self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
970 elif self.seekstate == self.SEEK_STATE_PAUSE:
971 if len(config.seek.speeds_slowmotion.value):
972 self.setSeekState(self.makeStateSlowMotion(config.seek.speeds_slowmotion.value[-1]))
974 self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
975 elif self.seekstate == self.SEEK_STATE_EOF:
977 elif self.isStateForward(self.seekstate):
978 speed = self.seekstate[1]
979 if self.seekstate[2]:
980 speed /= self.seekstate[2]
981 speed = self.getHigher(speed, config.seek.speeds_forward.value) or config.seek.speeds_forward.value[-1]
982 self.setSeekState(self.makeStateForward(speed))
983 elif self.isStateBackward(self.seekstate):
984 speed = -self.seekstate[1]
985 if self.seekstate[2]:
986 speed /= self.seekstate[2]
987 speed = self.getLower(speed, config.seek.speeds_backward.value)
989 self.setSeekState(self.makeStateBackward(speed))
991 self.setSeekState(self.SEEK_STATE_PLAY)
992 elif self.isStateSlowMotion(self.seekstate):
993 speed = self.getLower(self.seekstate[2], config.seek.speeds_slowmotion.value) or config.seek.speeds_slowmotion.value[0]
994 self.setSeekState(self.makeStateSlowMotion(speed))
997 seek = self.getSeek()
998 if seek and not (seek.isCurrentlySeekable() & 2):
999 if not self.fast_winding_hint_message_showed and (seek.isCurrentlySeekable() & 1):
1000 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)
1001 self.fast_winding_hint_message_showed = True
1003 return 0 # trade as unhandled action
1004 seekstate = self.seekstate
1005 if seekstate == self.SEEK_STATE_PLAY:
1006 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1007 elif seekstate == self.SEEK_STATE_EOF:
1008 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1009 self.doSeekRelative(-6)
1010 elif seekstate == self.SEEK_STATE_PAUSE:
1011 self.doSeekRelative(-1)
1012 elif self.isStateForward(seekstate):
1013 speed = seekstate[1]
1015 speed /= seekstate[2]
1016 speed = self.getLower(speed, config.seek.speeds_forward.value)
1018 self.setSeekState(self.makeStateForward(speed))
1020 self.setSeekState(self.SEEK_STATE_PLAY)
1021 elif self.isStateBackward(seekstate):
1022 speed = -seekstate[1]
1024 speed /= seekstate[2]
1025 speed = self.getHigher(speed, config.seek.speeds_backward.value) or config.seek.speeds_backward.value[-1]
1026 self.setSeekState(self.makeStateBackward(speed))
1027 elif self.isStateSlowMotion(seekstate):
1028 speed = self.getHigher(seekstate[2], config.seek.speeds_slowmotion.value)
1030 self.setSeekState(self.makeStateSlowMotion(speed))
1032 self.setSeekState(self.SEEK_STATE_PAUSE)
1034 def seekFwdManual(self):
1035 self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
1037 def fwdSeekTo(self, minutes):
1038 print "Seek", minutes, "minutes forward"
1039 self.doSeekRelative(minutes * 60 * 90000)
1041 def seekBackManual(self):
1042 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
1044 def rwdSeekTo(self, minutes):
1046 self.doSeekRelative(-minutes * 60 * 90000)
1048 def checkSkipShowHideLock(self):
1049 wantlock = self.seekstate != self.SEEK_STATE_PLAY
1051 if config.usage.show_infobar_on_skip.value:
1052 if self.lockedBecauseOfSkipping and not wantlock:
1054 self.lockedBecauseOfSkipping = False
1056 if wantlock and not self.lockedBecauseOfSkipping:
1058 self.lockedBecauseOfSkipping = True
1060 def calcRemainingTime(self):
1061 seekable = self.getSeek()
1062 if seekable is not None:
1063 len = seekable.getLength()
1065 tmp = self.cueGetEndCutPosition()
1070 pos = seekable.getPlayPosition()
1071 speednom = self.seekstate[1] or 1
1072 speedden = self.seekstate[2] or 1
1073 if not len[0] and not pos[0]:
1074 if len[1] <= pos[1]:
1076 time = (len[1] - pos[1])*speedden/(90*speednom)
1081 if self.seekstate == self.SEEK_STATE_EOF:
1084 # if we are seeking forward, we try to end up ~1s before the end, and pause there.
1085 seekstate = self.seekstate
1086 if self.seekstate != self.SEEK_STATE_PAUSE:
1087 self.setSeekState(self.SEEK_STATE_EOF)
1089 if seekstate not in (self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE): # if we are seeking
1090 seekable = self.getSeek()
1091 if seekable is not None:
1093 if seekstate == self.SEEK_STATE_PLAY: # regular EOF
1094 self.doEofInternal(True)
1096 self.doEofInternal(False)
1098 def doEofInternal(self, playing):
1099 pass # Defined in subclasses
1102 self.setSeekState(self.SEEK_STATE_PLAY)
1105 from Screens.PVRState import PVRState, TimeshiftState
1107 class InfoBarPVRState:
1108 def __init__(self, screen=PVRState, force_show = False):
1109 self.onPlayStateChanged.append(self.__playStateChanged)
1110 self.pvrStateDialog = self.session.instantiateDialog(screen)
1111 self.pvrStateDialog.setAnimationMode(0)
1112 self.onShow.append(self._mayShow)
1113 self.onHide.append(self.pvrStateDialog.hide)
1114 self.force_show = force_show
1117 if self.execing and self.seekstate != self.SEEK_STATE_PLAY:
1118 self.pvrStateDialog.show()
1120 def __playStateChanged(self, state):
1121 playstateString = state[3]
1122 self.pvrStateDialog["state"].setText(playstateString)
1124 # if we return into "PLAY" state, ensure that the dialog gets hidden if there will be no infobar displayed
1125 if not config.usage.show_infobar_on_skip.value and self.seekstate == self.SEEK_STATE_PLAY and not self.force_show:
1126 self.pvrStateDialog.hide()
1130 class InfoBarTimeshiftState(InfoBarPVRState):
1132 InfoBarPVRState.__init__(self, screen=TimeshiftState, force_show = True)
1133 self.__hideTimer = eTimer()
1134 self.__hideTimer.callback.append(self.__hideTimeshiftState)
1137 if self.execing and self.timeshift_enabled:
1138 self.pvrStateDialog.show()
1139 if self.seekstate == self.SEEK_STATE_PLAY and not self.shown:
1140 self.__hideTimer.start(5*1000, True)
1142 def __hideTimeshiftState(self):
1143 self.pvrStateDialog.hide()
1145 class InfoBarShowMovies:
1147 # i don't really like this class.
1148 # it calls a not further specified "movie list" on up/down/movieList,
1149 # so this is not more than an action map
1151 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions",
1153 "movieList": (self.showMovies, _("movie list")),
1154 "up": (self.showMovies, _("movie list")),
1155 "down": (self.showMovies, _("movie list"))
1158 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
1162 # Timeshift works the following way:
1163 # demux0 demux1 "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
1164 # - normal playback TUNER unused PLAY enable disable disable
1165 # - user presses "yellow" button. FILE record PAUSE enable disable enable
1166 # - user presess pause again FILE record PLAY enable disable enable
1167 # - user fast forwards FILE record FF enable disable enable
1168 # - end of timeshift buffer reached TUNER record PLAY enable enable disable
1169 # - user backwards FILE record BACK # !! enable disable enable
1173 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
1174 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
1175 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
1176 # - the user can now PVR around
1177 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
1178 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
1180 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
1181 # - if the user rewinds, or press pause, timeshift will be activated again
1183 # note that a timeshift can be enabled ("recording") and
1184 # activated (currently time-shifting).
1186 class InfoBarTimeshift:
1188 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions",
1190 "timeshiftStart": (self.startTimeshift, _("start timeshift")), # the "yellow key"
1191 "timeshiftStop": (self.stopTimeshift, _("stop timeshift")) # currently undefined :), probably 'TV'
1193 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
1195 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "rewind key"
1196 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause # something like "pause key"
1197 }, prio=-1) # priority over record
1199 self.timeshift_enabled = 0
1200 self.timeshift_state = 0
1201 self.ts_rewind_timer = eTimer()
1202 self.ts_rewind_timer.callback.append(self.rewindService)
1204 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1206 iPlayableService.evStart: self.__serviceStarted,
1207 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
1208 iPlayableService.evUser+3: self.__lowDiskspaceMessage
1211 def getTimeshift(self):
1212 service = self.session.nav.getCurrentService()
1213 return service and service.timeshift()
1215 def startTimeshift(self):
1216 print "enable timeshift"
1217 ts = self.getTimeshift()
1219 self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
1220 print "no ts interface"
1223 if self.timeshift_enabled:
1224 print "hu, timeshift already enabled?"
1226 from Components import Harddisk
1227 if Harddisk.getMountPath(config.usage.timeshift_path.value) != '/' and \
1228 SystemInfo.get("DisableUsbRecord", True) and \
1229 Harddisk.isUsbStorage(config.usage.timeshift_path.value):
1230 self.session.open(MessageBox, _("Timeshift not possible on a USB storage."), MessageBox.TYPE_ERROR)
1233 if not ts.startTimeshift():
1234 self.timeshift_enabled = 1
1236 # we remove the "relative time" for now.
1237 #self.pvrStateDialog["timeshift"].setRelative(time.time())
1240 #self.setSeekState(self.SEEK_STATE_PAUSE)
1241 self.activateTimeshiftEnd(False)
1243 # enable the "TimeshiftEnableActions", which will override
1244 # the startTimeshift actions
1245 self.__seekableStatusChanged()
1247 print "timeshift failed"
1249 def stopTimeshift(self):
1250 if not self.timeshift_enabled:
1252 print "disable timeshift"
1253 ts = self.getTimeshift()
1256 self.session.openWithCallback(self.stopTimeshiftConfirmed, MessageBox, _("Stop Timeshift?"), MessageBox.TYPE_YESNO)
1258 def stopTimeshiftConfirmed(self, confirmed):
1262 ts = self.getTimeshift()
1267 self.timeshift_enabled = 0
1270 self.__seekableStatusChanged()
1272 # activates timeshift, and seeks to (almost) the end
1273 def activateTimeshiftEnd(self, back = True):
1274 ts = self.getTimeshift()
1275 print "activateTimeshiftEnd"
1280 if ts.isTimeshiftActive():
1281 print "!! activate timeshift called - but shouldn't this be a normal pause?"
1285 ts.activateTimeshift() # activate timeshift will automatically pause
1286 self.setSeekState(self.SEEK_STATE_PAUSE)
1289 self.ts_rewind_timer.start(200, 1)
1291 def rewindService(self):
1292 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1294 # same as activateTimeshiftEnd, but pauses afterwards.
1295 def activateTimeshiftEndAndPause(self):
1296 print "activateTimeshiftEndAndPause"
1297 #state = self.seekstate
1298 self.activateTimeshiftEnd(False)
1300 def __seekableStatusChanged(self):
1303 # print "self.isSeekable", self.isSeekable()
1304 # print "self.timeshift_enabled", self.timeshift_enabled
1306 # when this service is not seekable, but timeshift
1307 # is enabled, this means we can activate
1309 if not self.isSeekable() and self.timeshift_enabled:
1312 # print "timeshift activate:", enabled
1313 self["TimeshiftActivateActions"].setEnabled(enabled)
1315 def __serviceStarted(self):
1316 self.timeshift_enabled = False
1317 self.__seekableStatusChanged()
1319 def __lowDiskspaceMessage(self):
1320 Notifications.AddPopup(text = _("Write error. Not enough space for writing.\n"), type = MessageBox.TYPE_ERROR, timeout = 0, id = "DiskFullMessage")
1322 from Screens.PiPSetup import PiPSetup
1324 class InfoBarExtensions:
1325 EXTENSION_SINGLE = 0
1331 self["InstantExtensionsActions"] = HelpableActionMap(self, "InfobarExtensions",
1333 "extensions": (self.showExtensionSelection, _("view extensions...")),
1334 }, 1) # lower priority
1336 for p in plugins.getPlugins(PluginDescriptor.WHERE_EXTENSIONSINGLE):
1339 def addExtension(self, extension, key = None, type = EXTENSION_SINGLE):
1340 self.list.append((type, extension, key))
1342 def updateExtension(self, extension, key = None):
1343 self.extensionsList.append(extension)
1345 if self.extensionKeys.has_key(key):
1349 for x in self.availableKeys:
1350 if not self.extensionKeys.has_key(x):
1355 self.extensionKeys[key] = len(self.extensionsList) - 1
1357 def updateExtensions(self):
1358 self.extensionsList = []
1359 self.availableKeys = [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "red", "green", "yellow", "blue" ]
1360 self.extensionKeys = {}
1362 if x[0] == self.EXTENSION_SINGLE:
1363 self.updateExtension(x[1], x[2])
1366 self.updateExtension(y[0], y[1])
1369 def showExtensionSelection(self):
1370 self.updateExtensions()
1371 extensionsList = self.extensionsList[:]
1374 for x in self.availableKeys:
1375 if self.extensionKeys.has_key(x):
1376 entry = self.extensionKeys[x]
1377 extension = self.extensionsList[entry]
1379 name = str(extension[0]())
1380 list.append((extension[0](), extension))
1382 extensionsList.remove(extension)
1384 extensionsList.remove(extension)
1385 list.extend([(x[0](), x) for x in extensionsList])
1387 keys += [""] * len(extensionsList)
1388 self.session.openWithCallback(self.extensionCallback, ChoiceBox, title=_("Please choose an extension..."), list = list, keys = keys, skin_name = "ExtensionsList")
1390 def extensionCallback(self, answer):
1391 if answer is not None:
1394 from Tools.BoundFunction import boundFunction
1397 # depends on InfoBarExtensions
1399 class InfoBarPlugins:
1401 self.addExtension(extension = self.getPluginList, type = InfoBarExtensions.EXTENSION_LIST)
1403 def getPluginName(self, name):
1406 def getPluginList(self):
1408 for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EXTENSIONSMENU):
1409 args = inspect.getargspec(p.__call__)[0]
1410 if len(args) == 1 or len(args) == 2 and isinstance(self, InfoBarChannelSelection):
1411 l.append(((boundFunction(self.getPluginName, p.name), boundFunction(self.runPlugin, p), lambda: True), None, p.name))
1412 l.sort(key = lambda e: e[2]) # sort by name
1415 def runPlugin(self, plugin):
1416 if isinstance(self, InfoBarChannelSelection):
1417 plugin(session = self.session, servicelist = self.servicelist)
1419 plugin(session = self.session)
1421 from Components.Task import job_manager
1422 class InfoBarJobman:
1424 self.addExtension(extension = self.getJobList, type = InfoBarExtensions.EXTENSION_LIST)
1426 def getJobList(self):
1427 return [((boundFunction(self.getJobName, job), boundFunction(self.showJobView, job), lambda: True), None) for job in job_manager.getPendingJobs()]
1429 def getJobName(self, job):
1430 return "%s: %s (%d%%)" % (job.getStatustext(), job.name, int(100*job.progress/float(job.end)))
1432 def showJobView(self, job):
1433 from Screens.TaskView import JobView
1434 job_manager.in_background = False
1435 self.session.openWithCallback(self.JobViewCB, JobView, job)
1437 def JobViewCB(self, in_background):
1438 job_manager.in_background = in_background
1440 # depends on InfoBarExtensions
1444 self.session.pipshown
1446 self.session.pipshown = False
1447 if SystemInfo.get("NumVideoDecoders", 1) > 1:
1449 self.addExtension((self.getShowHideName, self.showPiP, lambda: True), "blue")
1450 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1451 self.addExtension((self.getSwapName, self.swapPiP, self.pipShown), "yellow")
1453 self.addExtension((self.getShowHideName, self.showPiP, self.pipShown), "blue")
1454 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1457 return self.session.pipshown
1459 def pipHandles0Action(self):
1460 return self.pipShown() and config.usage.pip_zero_button.value != "standard"
1462 def getShowHideName(self):
1463 if self.session.pipshown:
1464 return _("Disable Picture in Picture")
1466 return _("Activate Picture in Picture")
1468 def getSwapName(self):
1469 return _("Swap Services")
1471 def getMoveName(self):
1472 return _("Move Picture in Picture")
1475 if self.session.pipshown:
1476 del self.session.pip
1477 self.session.pipshown = False
1479 self.session.pip = self.session.instantiateDialog(PictureInPicture)
1480 self.session.pip.setAnimationMode(0)
1481 self.session.pip.show()
1482 newservice = self.session.nav.getCurrentlyPlayingServiceReference()
1483 if self.session.pip.playService(newservice):
1484 self.session.pipshown = True
1485 self.session.pip.servicePath = self.servicelist.getCurrentServicePath()
1487 self.session.pipshown = False
1488 del self.session.pip
1489 self.session.nav.playService(newservice)
1492 swapservice = self.session.nav.getCurrentlyPlayingServiceReference()
1493 if self.session.pip.servicePath:
1494 servicepath = self.servicelist.getCurrentServicePath()
1495 ref=servicepath[len(servicepath)-1]
1496 pipref=self.session.pip.getCurrentService()
1497 self.session.pip.playService(swapservice)
1498 self.servicelist.setCurrentServicePath(self.session.pip.servicePath)
1499 if pipref.toString() != ref.toString(): # is a subservice ?
1500 self.session.nav.stopService() # stop portal
1501 self.session.nav.playService(pipref) # start subservice
1502 self.session.pip.servicePath=servicepath
1505 self.session.open(PiPSetup, pip = self.session.pip)
1507 def pipDoHandle0Action(self):
1508 use = config.usage.pip_zero_button.value
1511 elif "swapstop" == use:
1517 from RecordTimer import parseEvent, RecordTimerEntry
1519 class InfoBarInstantRecord:
1520 """Instant Record - handles the instantRecord action in order to
1521 start/stop instant records"""
1523 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
1525 "instantRecord": (self.instantRecord, _("Instant Record...")),
1529 def stopCurrentRecording(self, entry = -1):
1530 if entry is not None and entry != -1:
1531 self.session.nav.RecordTimer.removeEntry(self.recording[entry])
1532 self.recording.remove(self.recording[entry])
1534 def startInstantRecording(self, limitEvent = False):
1535 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
1537 # try to get event info
1540 service = self.session.nav.getCurrentService()
1541 epg = eEPGCache.getInstance()
1542 event = epg.lookupEventTime(serviceref, -1, 0)
1544 info = service.info()
1545 ev = info.getEvent(0)
1551 end = begin + 3600 # dummy
1552 name = "instant record"
1556 if event is not None:
1557 curEvent = parseEvent(event)
1559 description = curEvent[3]
1560 eventid = curEvent[4]
1565 self.session.open(MessageBox, _("No event info found, recording indefinitely."), MessageBox.TYPE_INFO)
1567 if isinstance(serviceref, eServiceReference):
1568 serviceref = ServiceReference(serviceref)
1570 recording = RecordTimerEntry(serviceref, begin, end, name, description, eventid, dirname = preferredInstantRecordPath())
1571 recording.dontSave = True
1573 if event is None or limitEvent == False:
1574 recording.autoincrease = True
1575 recording.setAutoincreaseEnd()
1577 simulTimerList = self.session.nav.RecordTimer.record(recording)
1579 if simulTimerList is None: # no conflict
1580 self.recording.append(recording)
1582 if len(simulTimerList) > 1: # with other recording
1583 name = simulTimerList[1].name
1584 name_date = ' '.join((name, strftime('%c', localtime(simulTimerList[1].begin))))
1585 print "[TIMER] conflicts with", name_date
1586 recording.autoincrease = True # start with max available length, then increment
1587 if recording.setAutoincreaseEnd():
1588 self.session.nav.RecordTimer.record(recording)
1589 self.recording.append(recording)
1590 self.session.open(MessageBox, _("Record time limited due to conflicting timer %s") % name_date, MessageBox.TYPE_INFO)
1592 self.session.open(MessageBox, _("Couldn't record due to conflicting timer %s") % name, MessageBox.TYPE_INFO)
1594 self.session.open(MessageBox, _("Couldn't record due to invalid service %s") % serviceref, MessageBox.TYPE_INFO)
1595 recording.autoincrease = False
1597 def isInstantRecordRunning(self):
1598 print "self.recording:", self.recording
1600 for x in self.recording:
1605 def recordQuestionCallback(self, answer):
1606 print "pre:\n", self.recording
1608 if answer is None or answer[1] == "no":
1611 recording = self.recording[:]
1613 if not x in self.session.nav.RecordTimer.timer_list:
1614 self.recording.remove(x)
1615 elif x.dontSave and x.isRunning():
1616 list.append((x, False))
1618 if answer[1] == "changeduration":
1619 if len(self.recording) == 1:
1620 self.changeDuration(0)
1622 self.session.openWithCallback(self.changeDuration, TimerSelection, list)
1623 elif answer[1] == "changeendtime":
1624 if len(self.recording) == 1:
1627 self.session.openWithCallback(self.setEndtime, TimerSelection, list)
1628 elif answer[1] == "stop":
1629 if len(self.recording) == 1:
1630 self.stopCurrentRecording(0)
1632 self.session.openWithCallback(self.stopCurrentRecording, TimerSelection, list)
1633 elif answer[1] in ( "indefinitely" , "manualduration", "manualendtime", "event"):
1634 self.startInstantRecording(limitEvent = answer[1] in ("event", "manualendtime") or False)
1635 if answer[1] == "manualduration":
1636 self.changeDuration(len(self.recording)-1)
1637 elif answer[1] == "manualendtime":
1638 self.setEndtime(len(self.recording)-1)
1639 print "after:\n", self.recording
1641 def setEndtime(self, entry):
1642 if entry is not None and entry >= 0:
1643 self.selectedEntry = entry
1644 self.endtime=ConfigClock(default = self.recording[self.selectedEntry].end)
1645 dlg = self.session.openWithCallback(self.TimeDateInputClosed, TimeDateInput, self.endtime)
1646 dlg.setTitle(_("Please change recording endtime"))
1648 def TimeDateInputClosed(self, ret):
1651 localendtime = localtime(ret[1])
1652 print "stopping recording at", strftime("%c", localendtime)
1653 if self.recording[self.selectedEntry].end != ret[1]:
1654 self.recording[self.selectedEntry].autoincrease = False
1655 self.recording[self.selectedEntry].end = ret[1]
1656 self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
1658 def changeDuration(self, entry):
1659 if entry is not None and entry >= 0:
1660 self.selectedEntry = entry
1661 self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1663 def inputCallback(self, value):
1664 if value is not None:
1665 print "stopping recording after", int(value), "minutes."
1666 entry = self.recording[self.selectedEntry]
1668 entry.autoincrease = False
1669 entry.end = int(time()) + 60 * int(value)
1670 self.session.nav.RecordTimer.timeChanged(entry)
1672 def instantRecord(self):
1673 if not fileExists("/hdd", 0):
1674 print "not found /hdd"
1675 os_system("ln -s /media/hdd /hdd")
1677 recPath = preferredInstantRecordPath()
1678 if not findSafeRecordPath(recPath) and not findSafeRecordPath(defaultMoviePath()):
1681 self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1684 if self.isInstantRecordRunning():
1685 self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1686 title=_("A recording is currently running.\nWhat do you want to do?"), \
1687 list=((_("stop recording"), "stop"), \
1688 (_("add recording (stop after current event)"), "event"), \
1689 (_("add recording (indefinitely)"), "indefinitely"), \
1690 (_("add recording (enter recording duration)"), "manualduration"), \
1691 (_("add recording (enter recording endtime)"), "manualendtime"), \
1692 (_("change recording (duration)"), "changeduration"), \
1693 (_("change recording (endtime)"), "changeendtime"), \
1694 (_("do nothing"), "no")))
1696 self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1697 title=_("Start recording?"), \
1698 list=((_("add recording (stop after current event)"), "event"), \
1699 (_("add recording (indefinitely)"), "indefinitely"), \
1700 (_("add recording (enter recording duration)"), "manualduration"), \
1701 (_("add recording (enter recording endtime)"), "manualendtime"), \
1702 (_("don't record"), "no")))
1704 from Tools.ISO639 import LanguageCodes
1706 class InfoBarAudioSelection:
1708 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions",
1710 "audioSelection": (self.audioSelection, _("Audio Options...")),
1713 def audioSelection(self):
1714 from Screens.AudioSelection import AudioSelection
1715 self.session.openWithCallback(self.audioSelected, AudioSelection, infobar=self)
1717 def audioSelected(self, ret=None):
1718 print "[infobar::audioSelected]", ret
1720 class InfoBarSubserviceSelection:
1722 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1724 "subserviceSelection": (self.subserviceSelection, _("Subservice list...")),
1727 self["SubserviceQuickzapAction"] = HelpableActionMap(self, "InfobarSubserviceQuickzapActions",
1729 "nextSubservice": (self.nextSubservice, _("Switch to next subservice")),
1730 "prevSubservice": (self.prevSubservice, _("Switch to previous subservice"))
1732 self["SubserviceQuickzapAction"].setEnabled(False)
1734 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1736 iPlayableService.evUpdatedEventInfo: self.checkSubservicesAvail
1741 def checkSubservicesAvail(self):
1742 service = self.session.nav.getCurrentService()
1743 subservices = service and service.subServices()
1744 if not subservices or subservices.getNumberOfSubservices() == 0:
1745 self["SubserviceQuickzapAction"].setEnabled(False)
1747 def nextSubservice(self):
1748 self.changeSubservice(+1)
1750 def prevSubservice(self):
1751 self.changeSubservice(-1)
1753 def changeSubservice(self, direction):
1754 service = self.session.nav.getCurrentService()
1755 subservices = service and service.subServices()
1756 n = subservices and subservices.getNumberOfSubservices()
1759 ref = self.session.nav.getCurrentlyPlayingServiceReference()
1762 if subservices.getSubservice(idx).toString() == ref.toString():
1767 selection += direction
1772 newservice = subservices.getSubservice(selection)
1773 if newservice.valid():
1776 self.session.nav.playService(newservice, False)
1778 def subserviceSelection(self):
1779 service = self.session.nav.getCurrentService()
1780 subservices = service and service.subServices()
1781 self.bouquets = self.servicelist.getBouquetList()
1782 n = subservices and subservices.getNumberOfSubservices()
1785 ref = self.session.nav.getCurrentlyPlayingServiceReference()
1789 i = subservices.getSubservice(idx)
1790 if i.toString() == ref.toString():
1792 tlist.append((i.getName(), i))
1795 if self.bouquets and len(self.bouquets):
1796 keys = ["red", "blue", "", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1797 if config.usage.multibouquet.value:
1798 tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to bouquet"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1800 tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to favourites"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1803 tlist = [(_("Quickzap"), "quickzap", service.subServices()), ("--", "")] + tlist
1804 keys = ["red", "", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1807 self.session.openWithCallback(self.subserviceSelected, ChoiceBox, title=_("Please select a subservice..."), list = tlist, selection = selection, keys = keys, skin_name = "SubserviceSelection")
1809 def subserviceSelected(self, service):
1811 if not service is None:
1812 if isinstance(service[1], str):
1813 if service[1] == "quickzap":
1814 from Screens.SubservicesQuickzap import SubservicesQuickzap
1815 self.session.open(SubservicesQuickzap, service[2])
1817 self["SubserviceQuickzapAction"].setEnabled(True)
1818 self.session.nav.playService(service[1], False)
1820 def addSubserviceToBouquetCallback(self, service):
1821 if len(service) > 1 and isinstance(service[1], eServiceReference):
1822 self.selectedSubservice = service
1823 if self.bouquets is None:
1826 cnt = len(self.bouquets)
1827 if cnt > 1: # show bouquet list
1828 self.bsel = self.session.openWithCallback(self.bouquetSelClosed, BouquetSelector, self.bouquets, self.addSubserviceToBouquet)
1829 elif cnt == 1: # add to only one existing bouquet
1830 self.addSubserviceToBouquet(self.bouquets[0][1])
1831 self.session.open(MessageBox, _("Service has been added to the favourites."), MessageBox.TYPE_INFO)
1833 def bouquetSelClosed(self, confirmed):
1835 del self.selectedSubservice
1837 self.session.open(MessageBox, _("Service has been added to the selected bouquet."), MessageBox.TYPE_INFO)
1839 def addSubserviceToBouquet(self, dest):
1840 self.servicelist.addServiceToBouquet(dest, self.selectedSubservice[1])
1842 self.bsel.close(True)
1844 del self.selectedSubservice
1846 from Components.Sources.HbbtvApplication import HbbtvApplication
1847 gHbbtvApplication = HbbtvApplication()
1848 class InfoBarRedButton:
1850 if not (config.misc.rcused.value == 1):
1851 self["RedButtonActions"] = HelpableActionMap(self, "InfobarRedButtonActions",
1853 "activateRedButton": (self.activateRedButton, _("Red button...")),
1855 self["HbbtvApplication"] = gHbbtvApplication
1857 self["HbbtvApplication"] = Boolean(fixed=0)
1858 self["HbbtvApplication"].name = "" #is this a hack?
1860 self.onHBBTVActivation = [ ]
1861 self.onRedButtonActivation = [ ]
1862 self.onReadyForAIT = [ ]
1863 self.__et = ServiceEventTracker(screen=self, eventmap=
1865 iPlayableService.evHBBTVInfo: self.detectedHbbtvApplication,
1866 iPlayableService.evUpdatedInfo: self.updateInfomation
1869 def updateAIT(self, orgId=0):
1870 for x in self.onReadyForAIT:
1873 except Exception, ErrMsg:
1875 #self.onReadyForAIT.remove(x)
1877 def updateInfomation(self):
1879 self["HbbtvApplication"].setApplicationName("")
1881 except Exception, ErrMsg:
1884 def detectedHbbtvApplication(self):
1885 service = self.session.nav.getCurrentService()
1886 info = service and service.info()
1888 for x in info.getInfoObject(iServiceInformation.sHBBTVUrl):
1891 self.updateAIT(x[3])
1892 self["HbbtvApplication"].setApplicationName(x[1])
1894 except Exception, ErrMsg:
1897 def activateRedButton(self):
1898 service = self.session.nav.getCurrentService()
1899 info = service and service.info()
1900 if info and info.getInfoString(iServiceInformation.sHBBTVUrl) != "":
1901 for x in self.onHBBTVActivation:
1903 elif False: # TODO: other red button services
1904 for x in self.onRedButtonActivation:
1907 class InfoBarAdditionalInfo:
1910 self["RecordingPossible"] = Boolean(fixed=harddiskmanager.HDDCount() > 0 and config.misc.rcused.value == 1)
1911 self["TimeshiftPossible"] = self["RecordingPossible"]
1912 self["ShowTimeshiftOnYellow"] = Boolean(fixed=(config.misc.rcused.value == 1))
1913 self["ShowAudioOnYellow"] = Boolean(fixed=config.misc.rcused.value != 1)
1914 self["ShowRecordOnRed"] = Boolean(fixed=config.misc.rcused.value == 1)
1915 self["ExtensionsAvailable"] = Boolean(fixed=1)
1917 class InfoBarNotifications:
1919 self.onExecBegin.append(self.checkNotifications)
1920 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1921 self.onClose.append(self.__removeNotification)
1923 def __removeNotification(self):
1924 Notifications.notificationAdded.remove(self.checkNotificationsIfExecing)
1926 def checkNotificationsIfExecing(self):
1928 self.checkNotifications()
1930 def checkNotifications(self):
1931 notifications = Notifications.notifications
1933 n = notifications[0]
1935 del notifications[0]
1938 if n[3].has_key("onSessionOpenCallback"):
1939 n[3]["onSessionOpenCallback"]()
1940 del n[3]["onSessionOpenCallback"]
1943 dlg = self.session.openWithCallback(cb, n[1], *n[2], **n[3])
1945 dlg = self.session.open(n[1], *n[2], **n[3])
1947 # remember that this notification is currently active
1949 Notifications.current_notifications.append(d)
1950 dlg.onClose.append(boundFunction(self.__notificationClosed, d))
1952 def __notificationClosed(self, d):
1953 Notifications.current_notifications.remove(d)
1955 class InfoBarServiceNotifications:
1957 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1959 iPlayableService.evEnd: self.serviceHasEnded
1962 def serviceHasEnded(self):
1963 print "service end!"
1966 self.setSeekState(self.SEEK_STATE_PLAY)
1970 class InfoBarCueSheetSupport:
1976 ENABLE_RESUME_SUPPORT = False
1978 def __init__(self, actionmap = "InfobarCueSheetActions"):
1979 self["CueSheetActions"] = HelpableActionMap(self, actionmap,
1981 "jumpPreviousMark": (self.jumpPreviousMark, _("jump to previous marked position")),
1982 "jumpNextMark": (self.jumpNextMark, _("jump to next marked position")),
1983 "toggleMark": (self.toggleMark, _("toggle a cut mark at the current position"))
1987 self.is_closing = False
1988 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1990 iPlayableService.evStart: self.__serviceStarted,
1993 def __serviceStarted(self):
1996 print "new service started! trying to download cuts!"
1997 self.downloadCuesheet()
1999 if self.ENABLE_RESUME_SUPPORT:
2002 for (pts, what) in self.cut_list:
2003 if what == self.CUT_TYPE_LAST:
2006 if last is not None:
2007 self.resume_point = last
2010 if config.usage.on_movie_start.value == "ask":
2011 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)
2012 elif config.usage.on_movie_start.value == "resume":
2013 # TRANSLATORS: The string "Resuming playback" flashes for a moment
2014 # TRANSLATORS: at the start of a movie, when the user has selected
2015 # TRANSLATORS: "Resume from last position" as start behavior.
2016 # TRANSLATORS: The purpose is to notify the user that the movie starts
2017 # TRANSLATORS: in the middle somewhere and not from the beginning.
2018 # TRANSLATORS: (Some translators seem to have interpreted it as a
2019 # TRANSLATORS: question or a choice, but it is a statement.)
2020 Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Resuming playback"), timeout=2, type=MessageBox.TYPE_INFO)
2022 def playLastCB(self, answer):
2024 self.doSeek(self.resume_point)
2025 self.hideAfterResume()
2027 def hideAfterResume(self):
2028 if isinstance(self, InfoBarShowHide):
2031 def __getSeekable(self):
2032 service = self.session.nav.getCurrentService()
2035 return service.seek()
2037 def cueGetCurrentPosition(self):
2038 seek = self.__getSeekable()
2041 r = seek.getPlayPosition()
2046 def cueGetEndCutPosition(self):
2049 for cp in self.cut_list:
2050 if cp[1] == self.CUT_TYPE_OUT:
2054 elif cp[1] == self.CUT_TYPE_IN:
2058 def jumpPreviousNextMark(self, cmp, start=False):
2059 current_pos = self.cueGetCurrentPosition()
2060 if current_pos is None:
2062 mark = self.getNearestCutPoint(current_pos, cmp=cmp, start=start)
2063 if mark is not None:
2071 def jumpPreviousMark(self):
2072 # we add 5 seconds, so if the play position is <5s after
2073 # the mark, the mark before will be used
2074 self.jumpPreviousNextMark(lambda x: -x-5*90000, start=True)
2076 def jumpNextMark(self):
2077 if not self.jumpPreviousNextMark(lambda x: x-90000):
2080 def getNearestCutPoint(self, pts, cmp=abs, start=False):
2087 bestdiff = cmp(0 - pts)
2089 nearest = [0, False]
2090 for cp in self.cut_list:
2091 if beforecut and cp[1] in (self.CUT_TYPE_IN, self.CUT_TYPE_OUT):
2093 if cp[1] == self.CUT_TYPE_IN: # Start is here, disregard previous marks
2094 diff = cmp(cp[0] - pts)
2095 if start and diff >= 0:
2101 if cp[1] == self.CUT_TYPE_IN:
2103 elif cp[1] == self.CUT_TYPE_OUT:
2105 elif cp[1] in (self.CUT_TYPE_MARK, self.CUT_TYPE_LAST):
2106 diff = cmp(cp[0] - pts)
2107 if instate and diff >= 0 and (nearest is None or bestdiff > diff):
2112 def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
2113 current_pos = self.cueGetCurrentPosition()
2114 if current_pos is None:
2115 print "not seekable"
2118 nearest_cutpoint = self.getNearestCutPoint(current_pos)
2120 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
2122 return nearest_cutpoint
2124 self.removeMark(nearest_cutpoint)
2125 elif not onlyremove and not onlyreturn:
2126 self.addMark((current_pos, self.CUT_TYPE_MARK))
2131 def addMark(self, point):
2132 insort(self.cut_list, point)
2133 self.uploadCuesheet()
2134 self.showAfterCuesheetOperation()
2136 def removeMark(self, point):
2137 self.cut_list.remove(point)
2138 self.uploadCuesheet()
2139 self.showAfterCuesheetOperation()
2141 def showAfterCuesheetOperation(self):
2142 if isinstance(self, InfoBarShowHide):
2145 def __getCuesheet(self):
2146 service = self.session.nav.getCurrentService()
2149 return service.cueSheet()
2151 def uploadCuesheet(self):
2152 cue = self.__getCuesheet()
2155 print "upload failed, no cuesheet interface"
2157 cue.setCutList(self.cut_list)
2159 def downloadCuesheet(self):
2160 cue = self.__getCuesheet()
2163 print "download failed, no cuesheet interface"
2166 self.cut_list = cue.getCutList()
2168 class InfoBarSummary(Screen):
2170 <screen position="0,0" size="132,64">
2171 <widget source="global.CurrentTime" render="Label" position="62,46" size="82,18" font="Regular;16" >
2172 <convert type="ClockToText">WithSeconds</convert>
2174 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="82,18" zPosition="1" >
2175 <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2176 <convert type="ConditionalShowHide">Blink</convert>
2178 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2179 <convert type="ServiceName">Name</convert>
2181 <widget source="session.Event_Now" render="Progress" position="6,46" size="46,18" borderWidth="1" >
2182 <convert type="EventTime">Progress</convert>
2186 # for picon: (path="piconlcd" will use LCD picons)
2187 # <widget source="session.CurrentService" render="Picon" position="6,0" size="120,64" path="piconlcd" >
2188 # <convert type="ServiceName">Reference</convert>
2191 class InfoBarSummarySupport:
2195 def createSummary(self):
2196 return InfoBarSummary
2198 class InfoBarMoviePlayerSummary(Screen):
2200 <screen position="0,0" size="132,64">
2201 <widget source="global.CurrentTime" render="Label" position="62,46" size="64,18" font="Regular;16" halign="right" >
2202 <convert type="ClockToText">WithSeconds</convert>
2204 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="64,18" zPosition="1" >
2205 <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2206 <convert type="ConditionalShowHide">Blink</convert>
2208 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2209 <convert type="ServiceName">Name</convert>
2211 <widget source="session.CurrentService" render="Progress" position="6,46" size="56,18" borderWidth="1" >
2212 <convert type="ServicePosition">Position</convert>
2216 class InfoBarMoviePlayerSummarySupport:
2220 def createSummary(self):
2221 return InfoBarMoviePlayerSummary
2223 class InfoBarTeletextPlugin:
2225 self.teletext_plugin = None
2227 for p in plugins.getPlugins(PluginDescriptor.WHERE_TELETEXT):
2228 self.teletext_plugin = p
2230 if self.teletext_plugin is not None:
2231 self["TeletextActions"] = HelpableActionMap(self, "InfobarTeletextActions",
2233 "startTeletext": (self.startTeletext, _("View teletext..."))
2236 print "no teletext plugin found!"
2238 def startTeletext(self):
2239 self.teletext_plugin(session=self.session, service=self.session.nav.getCurrentService())
2241 class InfoBarSubtitleSupport(object):
2243 object.__init__(self)
2244 self.subtitle_window = self.session.instantiateDialog(SubtitleDisplay)
2245 self.subtitle_window.setAnimationMode(0)
2246 self.__subtitles_enabled = False
2248 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2250 iPlayableService.evEnd: self.__serviceStopped,
2251 iPlayableService.evUpdatedInfo: self.__updatedInfo
2253 self.cached_subtitle_checked = False
2254 self.__selected_subtitle = None
2256 def __serviceStopped(self):
2257 self.cached_subtitle_checked = False
2258 if self.__subtitles_enabled:
2259 self.subtitle_window.hide()
2260 self.__subtitles_enabled = False
2261 self.__selected_subtitle = None
2263 def __updatedInfo(self):
2264 if not self.__selected_subtitle:
2265 subtitle = self.getCurrentServiceSubtitle()
2266 self.setSelectedSubtitle(subtitle and subtitle.getCachedSubtitle())
2267 if self.__selected_subtitle:
2268 self.setSubtitlesEnable(True)
2270 def getCurrentServiceSubtitle(self):
2271 service = self.session.nav.getCurrentService()
2272 return service and service.subtitle()
2274 def setSubtitlesEnable(self, enable=True):
2275 subtitle = self.getCurrentServiceSubtitle()
2277 if self.__selected_subtitle:
2278 if subtitle and not self.__subtitles_enabled:
2279 subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
2280 self.subtitle_window.show()
2281 self.__subtitles_enabled = True
2284 subtitle.disableSubtitles(self.subtitle_window.instance)
2285 self.__selected_subtitle = None
2286 self.__subtitles_enabled = False
2287 self.subtitle_window.hide()
2289 def setSelectedSubtitle(self, subtitle):
2290 self.__selected_subtitle = subtitle
2292 subtitles_enabled = property(lambda self: self.__subtitles_enabled, setSubtitlesEnable)
2293 selected_subtitle = property(lambda self: self.__selected_subtitle, setSelectedSubtitle)
2295 class InfoBarServiceErrorPopupSupport:
2297 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2299 iPlayableService.evTuneFailed: self.__tuneFailed,
2300 iPlayableService.evStart: self.__serviceStarted
2302 self.__serviceStarted()
2304 def __serviceStarted(self):
2305 self.last_error = None
2306 Notifications.RemovePopup(id = "ZapError")
2308 def __tuneFailed(self):
2309 service = self.session.nav.getCurrentService()
2310 info = service and service.info()
2311 error = info and info.getInfo(iServiceInformation.sDVBState)
2313 if error == self.last_error:
2316 self.last_error = error
2319 eDVBServicePMTHandler.eventNoResources: _("No free tuner!"),
2320 eDVBServicePMTHandler.eventTuneFailed: _("Tune failed!"),
2321 eDVBServicePMTHandler.eventNoPAT: _("No data on transponder!\n(Timeout reading PAT)"),
2322 eDVBServicePMTHandler.eventNoPATEntry: _("Service not found!\n(SID not found in PAT)"),
2323 eDVBServicePMTHandler.eventNoPMT: _("Service invalid!\n(Timeout reading PMT)"),
2324 eDVBServicePMTHandler.eventNewProgramInfo: None,
2325 eDVBServicePMTHandler.eventTuned: None,
2326 eDVBServicePMTHandler.eventSOF: None,
2327 eDVBServicePMTHandler.eventEOF: None,
2328 eDVBServicePMTHandler.eventMisconfiguration: _("Service unavailable!\nCheck tuner configuration!"),
2329 }.get(error) #this returns None when the key not exist in the dict
2331 if error is not None:
2332 Notifications.AddPopup(text = error, type = MessageBox.TYPE_ERROR, timeout = 5, id = "ZapError")
2334 Notifications.RemovePopup(id = "ZapError")
2338 self.hdmiInServiceRef = eServiceReference('8192:0:1:0:0:0:0:0:0:0:')
2340 if SystemInfo.get("HdmiInSupport", False):
2341 self.addExtension((self.getShowHdmiInName, self.HDMIIn, lambda: True), None)
2342 self.addExtension((self.getShowHdmiInPIPName, self.HDMIInPIP, self.showHDMIPIPMenu), None)
2344 def getShowHdmiInName(self):
2345 curref = self.session.nav.getCurrentlyPlayingServiceReference()
2346 if curref and curref.type == 8192:
2347 name = _("Disable HDMI-IN on Main Screen")
2349 name = _("Enable HDMI-IN on Main Screen")
2353 def getShowHdmiInPIPName(self):
2354 return _("Enable HDMI-IN on PIP")
2356 def showHDMIPIPMenu(self):
2357 _pipAvailable = SystemInfo.get("NumVideoDecoders", 1) > 1
2359 hdmiin_enabled = False
2360 curref = self.session.nav.getCurrentlyPlayingServiceReference()
2361 if curref and curref.type == 8192:
2362 hdmiin_enabled = True
2364 hdmiin_pip_shown = False
2365 if self.session.pipshown:
2366 pipref=self.session.pip.getCurrentService()
2367 if pipref and pipref.type == 8192:
2368 hdmiin_pip_shown = True
2370 return _pipAvailable and not hdmiin_enabled and not hdmiin_pip_shown
2372 def getCurrentServiceRef(self):
2373 slist = self.servicelist
2374 currentServiceSref = slist.servicelist.getCurrent()
2375 return currentServiceSref
2378 curref = self.session.nav.getCurrentlyPlayingServiceReference()
2379 if curref and curref.type == 8192:
2380 self.session.nav.playService(self.getCurrentServiceRef())
2382 self.session.nav.playService(self.hdmiInServiceRef)
2384 def HDMIInPIP(self):
2385 if self.session.pipshown:
2386 del self.session.pip
2388 self.session.pip = self.session.instantiateDialog(PictureInPicture)
2389 self.session.pip.setAnimationMode(0)
2390 self.session.pip.show()
2391 newservice = self.hdmiInServiceRef
2392 if self.session.pip.playService(newservice):
2393 self.session.pipshown = True
2394 self.session.pip.servicePath = self.servicelist.getCurrentServicePath()
2396 self.session.pipshown = False
2397 del self.session.pip
2398 self.session.openWithCallback(self.close, MessageBox, _("Could not open Picture in Picture"), MessageBox.TYPE_ERROR)