+ if self.seekstate == self.SEEK_STATE_PLAY:
+ self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
+ elif self.seekstate == self.SEEK_STATE_EOF:
+ self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
+ self.doSeekRelative(-6)
+ elif self.seekstate == self.SEEK_STATE_PAUSE:
+ self.doSeekRelative(-3)
+ elif self.isStateForward(self.seekstate):
+ speed = self.seekstate[1]
+ if self.seekstate[2]:
+ speed /= self.seekstate[2]
+ speed = self.getLower(speed, config.seek.speeds_forward.value)
+ if speed:
+ self.setSeekState(self.makeStateForward(speed))
+ else:
+ self.setSeekState(self.SEEK_STATE_PLAY)
+ elif self.isStateBackward(self.seekstate):
+ speed = -self.seekstate[1]
+ if self.seekstate[2]:
+ speed /= self.seekstate[2]
+ speed = self.getHigher(speed, config.seek.speeds_backward.value) or config.seek.speeds_backward.value[-1]
+ self.setSeekState(self.makeStateBackward(speed))
+ elif self.isStateSlowMotion(self.seekstate):
+ speed = self.getHigher(self.seekstate[2], config.seek.speeds_slowmotion.value)
+ if speed:
+ self.setSeekState(self.makeStateSlowMotion(speed))
+ else:
+ self.setSeekState(self.SEEK_STATE_PAUSE)
+
+ def seekFwdManual(self):
+ self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
+
+ def fwdSeekTo(self, minutes):
+ print "Seek", minutes, "minutes forward"
+ self.doSeekRelative(minutes * 60 * 90000)
+
+ def seekBackManual(self):
+ self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
+
+ def rwdSeekTo(self, minutes):
+ print "rwdSeekTo"
+ self.doSeekRelative(-minutes * 60 * 90000)
+
+ def checkSkipShowHideLock(self):
+ wantlock = self.seekstate != self.SEEK_STATE_PLAY
+
+ if config.usage.show_infobar_on_skip.value:
+ if self.lockedBecauseOfSkipping and not wantlock:
+ self.unlockShow()
+ self.lockedBecauseOfSkipping = False
+
+ if wantlock and not self.lockedBecauseOfSkipping:
+ self.lockShow()
+ self.lockedBecauseOfSkipping = True
+
+ def calcRemainingTime(self):
+ seekable = self.getSeek()
+ if seekable is not None:
+ len = seekable.getLength()
+ try:
+ tmp = self.cueGetEndCutPosition()
+ if tmp:
+ len = [False, tmp]
+ except:
+ pass
+ pos = seekable.getPlayPosition()
+ speednom = self.seekstate[1] or 1
+ speedden = self.seekstate[2] or 1
+ if not len[0] and not pos[0]:
+ if len[1] <= pos[1]:
+ return 0
+ time = (len[1] - pos[1])*speedden/(90*speednom)
+ return time
+ return False
+
+ def __evEOF(self):
+ if self.eofState == 0 and self.seekstate != self.SEEK_STATE_EOF:
+ self.eofState = 1
+ time = self.calcRemainingTime()
+ if not time:
+ time = 3000 # Failed to calc, use default
+ elif time == 0:
+ time = 300 # Passed end, shortest wait
+ elif time > 15000:
+ self.eofState = -2 # Too long, block eof
+ time = 15000
+ else:
+ time += 1000 # Add margin
+ self.eofTimer.start(time, True)
+
+ def inhibitEof(self):
+ if self.eofState >= 1:
+ self.eofState = -self.eofState
+ self.eofTimer.stop()
+ self.doEof()
+
+ def doEof(self):
+ if self.seekstate == self.SEEK_STATE_EOF:
+ return
+ if self.eofState == -2 or self.isStateBackward(self.seekstate):
+ self.eofState = 0
+ return
+
+ # if we are seeking, we try to end up ~1s before the end, and pause there.
+ eofstate = self.eofState
+ seekstate = self.seekstate
+ self.eofState = 0
+ if not self.seekstate == self.SEEK_STATE_PAUSE:
+ self.setSeekState(self.SEEK_STATE_EOF)
+ if eofstate == -1 or not seekstate in [self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE]:
+ seekable = self.getSeek()
+ if seekable is not None:
+ seekable.seekTo(-1)
+ if eofstate == 1 and seekstate == self.SEEK_STATE_PLAY:
+ self.doEofInternal(True)
+ else:
+ self.doEofInternal(False)
+
+ def doEofInternal(self, playing):
+ pass # Defined in subclasses
+
+ def __evSOF(self):
+ self.setSeekState(self.SEEK_STATE_PLAY)
+ self.doSeek(0)
+
+from Screens.PVRState import PVRState, TimeshiftState
+
+class InfoBarPVRState:
+ def __init__(self, screen=PVRState):
+ self.onPlayStateChanged.append(self.__playStateChanged)
+ self.pvrStateDialog = self.session.instantiateDialog(screen)
+ self.onShow.append(self._mayShow)
+ self.onHide.append(self.pvrStateDialog.hide)
+
+ def _mayShow(self):
+ if self.execing and self.seekstate != self.SEEK_STATE_PLAY:
+ self.pvrStateDialog.show()
+
+ def __playStateChanged(self, state):
+ playstateString = state[3]
+ self.pvrStateDialog["state"].setText(playstateString)
+ self._mayShow()
+
+class InfoBarTimeshiftState(InfoBarPVRState):
+ def __init__(self):
+ InfoBarPVRState.__init__(self, screen=TimeshiftState)
+
+ def _mayShow(self):
+ if self.execing and self.timeshift_enabled:
+ self.pvrStateDialog.show()
+
+class InfoBarShowMovies:
+
+ # i don't really like this class.
+ # it calls a not further specified "movie list" on up/down/movieList,
+ # so this is not more than an action map
+ def __init__(self):
+ self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions",
+ {
+ "movieList": (self.showMovies, _("movie list")),
+ "up": (self.showMovies, _("movie list")),
+ "down": (self.showMovies, _("movie list"))
+ })
+
+# InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
+
+# Hrmf.
+#
+# Timeshift works the following way:
+# demux0 demux1 "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
+# - normal playback TUNER unused PLAY enable disable disable
+# - user presses "yellow" button. FILE record PAUSE enable disable enable
+# - user presess pause again FILE record PLAY enable disable enable
+# - user fast forwards FILE record FF enable disable enable
+# - end of timeshift buffer reached TUNER record PLAY enable enable disable
+# - user backwards FILE record BACK # !! enable disable enable
+#
+
+# in other words:
+# - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
+# freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
+# now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
+# - the user can now PVR around
+# - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
+# the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
+# after!
+# the seek actions will be disabled, but the timeshiftActivateActions will be enabled
+# - if the user rewinds, or press pause, timeshift will be activated again
+
+# note that a timeshift can be enabled ("recording") and
+# activated (currently time-shifting).
+
+class InfoBarTimeshift:
+ def __init__(self):
+ self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions",
+ {
+ "timeshiftStart": (self.startTimeshift, _("start timeshift")), # the "yellow key"
+ "timeshiftStop": (self.stopTimeshift, _("stop timeshift")) # currently undefined :), probably 'TV'
+ }, prio=1)
+ self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
+ {
+ "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "rewind key"
+ "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause # something like "pause key"
+ }, prio=-1) # priority over record
+
+ self.timeshift_enabled = 0
+ self.timeshift_state = 0
+ self.ts_rewind_timer = eTimer()
+ self.ts_rewind_timer.callback.append(self.rewindService)
+
+ self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
+ {
+ iPlayableService.evStart: self.__serviceStarted,
+ iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged
+ })
+
+ def getTimeshift(self):
+ service = self.session.nav.getCurrentService()
+ return service and service.timeshift()
+
+ def startTimeshift(self):
+ print "enable timeshift"
+ ts = self.getTimeshift()
+ if ts is None:
+ self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
+ print "no ts interface"
+ return 0
+
+ if self.timeshift_enabled:
+ print "hu, timeshift already enabled?"
+ else:
+ if not ts.startTimeshift():
+ self.timeshift_enabled = 1
+
+ # we remove the "relative time" for now.
+ #self.pvrStateDialog["timeshift"].setRelative(time.time())
+
+ # PAUSE.
+ #self.setSeekState(self.SEEK_STATE_PAUSE)
+ self.activateTimeshiftEnd(False)
+
+ # enable the "TimeshiftEnableActions", which will override
+ # the startTimeshift actions
+ self.__seekableStatusChanged()
+ else:
+ print "timeshift failed"
+
+ def stopTimeshift(self):
+ if not self.timeshift_enabled:
+ return 0
+ print "disable timeshift"
+ ts = self.getTimeshift()
+ if ts is None:
+ return 0
+ self.session.openWithCallback(self.stopTimeshiftConfirmed, MessageBox, _("Stop Timeshift?"), MessageBox.TYPE_YESNO)
+
+ def stopTimeshiftConfirmed(self, confirmed):
+ if not confirmed:
+ return
+
+ ts = self.getTimeshift()
+ if ts is None:
+ return
+
+ ts.stopTimeshift()
+ self.timeshift_enabled = 0
+
+ # disable actions
+ self.__seekableStatusChanged()
+
+ # activates timeshift, and seeks to (almost) the end
+ def activateTimeshiftEnd(self, back = True):
+ ts = self.getTimeshift()
+ print "activateTimeshiftEnd"
+
+ if ts is None:
+ return
+
+ if ts.isTimeshiftActive():
+ print "!! activate timeshift called - but shouldn't this be a normal pause?"
+ self.pauseService()
+ else:
+ print "play, ..."
+ ts.activateTimeshift() # activate timeshift will automatically pause
+ self.setSeekState(self.SEEK_STATE_PAUSE)
+
+ if back:
+ self.doSeek(-5) # seek some gops before end
+ self.ts_rewind_timer.start(200, 1)
+ else:
+ self.doSeek(-1) # seek 1 gop before end
+
+ def rewindService(self):
+ self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
+
+ # same as activateTimeshiftEnd, but pauses afterwards.
+ def activateTimeshiftEndAndPause(self):
+ print "activateTimeshiftEndAndPause"
+ #state = self.seekstate
+ self.activateTimeshiftEnd(False)
+
+ def __seekableStatusChanged(self):
+ enabled = False
+
+# print "self.isSeekable", self.isSeekable()
+# print "self.timeshift_enabled", self.timeshift_enabled
+
+ # when this service is not seekable, but timeshift
+ # is enabled, this means we can activate
+ # the timeshift
+ if not self.isSeekable() and self.timeshift_enabled:
+ enabled = True
+
+# print "timeshift activate:", enabled
+ self["TimeshiftActivateActions"].setEnabled(enabled)
+
+ def __serviceStarted(self):
+ self.timeshift_enabled = False
+ self.__seekableStatusChanged()
+
+from Screens.PiPSetup import PiPSetup
+
+class InfoBarExtensions:
+ EXTENSION_SINGLE = 0
+ EXTENSION_LIST = 1
+
+ def __init__(self):
+ self.list = []
+
+ self["InstantExtensionsActions"] = HelpableActionMap(self, "InfobarExtensions",
+ {
+ "extensions": (self.showExtensionSelection, _("view extensions...")),
+ })
+
+ def addExtension(self, extension, key = None, type = EXTENSION_SINGLE):
+ self.list.append((type, extension, key))
+
+ def updateExtension(self, extension, key = None):
+ self.extensionsList.append(extension)
+ if key is not None:
+ if self.extensionKeys.has_key(key):
+ key = None
+
+ if key is None:
+ for x in self.availableKeys:
+ if not self.extensionKeys.has_key(x):
+ key = x
+ break
+
+ if key is not None:
+ self.extensionKeys[key] = len(self.extensionsList) - 1
+
+ def updateExtensions(self):
+ self.extensionsList = []
+ self.availableKeys = [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "red", "green", "yellow", "blue" ]
+ self.extensionKeys = {}
+ for x in self.list:
+ if x[0] == self.EXTENSION_SINGLE:
+ self.updateExtension(x[1], x[2])
+ else:
+ for y in x[1]():
+ self.updateExtension(y[0], y[1])
+
+
+ def showExtensionSelection(self):
+ self.updateExtensions()
+ extensionsList = self.extensionsList[:]
+ keys = []
+ list = []
+ for x in self.availableKeys:
+ if self.extensionKeys.has_key(x):
+ entry = self.extensionKeys[x]
+ extension = self.extensionsList[entry]
+ if extension[2]():
+ name = str(extension[0]())
+ list.append((extension[0](), extension))
+ keys.append(x)
+ extensionsList.remove(extension)
+ else:
+ extensionsList.remove(extension)
+ for x in extensionsList:
+ list.append((x[0](), x))
+ keys += [""] * len(extensionsList)
+ self.session.openWithCallback(self.extensionCallback, ChoiceBox, title=_("Please choose an extension..."), list = list, keys = keys)
+
+ def extensionCallback(self, answer):
+ if answer is not None:
+ answer[1][1]()
+
+from Tools.BoundFunction import boundFunction
+
+# depends on InfoBarExtensions
+from Components.PluginComponent import plugins
+
+class InfoBarPlugins:
+ def __init__(self):
+ self.addExtension(extension = self.getPluginList, type = InfoBarExtensions.EXTENSION_LIST)
+
+ def getPluginName(self, name):
+ return name
+
+ def getPluginList(self):
+ list = []
+ for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EXTENSIONSMENU):
+ list.append(((boundFunction(self.getPluginName, p.name), boundFunction(self.runPlugin, p), lambda: True), None))
+ return list
+
+ def runPlugin(self, plugin):
+ plugin(session = self.session, servicelist = self.servicelist)
+
+# depends on InfoBarExtensions
+class InfoBarSleepTimer:
+ def __init__(self):
+ self.addExtension((self.getSleepTimerName, self.showSleepTimerSetup, lambda: True), "1")
+
+ def getSleepTimerName(self):
+ return _("Sleep Timer")
+
+ def showSleepTimerSetup(self):
+ self.session.open(SleepTimerEdit)
+
+# depends on InfoBarExtensions
+class InfoBarPiP:
+ def __init__(self):
+ self.session.pipshown = False
+ if SystemInfo.get("NumVideoDecoders", 1) > 1:
+ self.addExtension((self.getShowHideName, self.showPiP, lambda: True), "blue")
+ self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
+ self.addExtension((self.getSwapName, self.swapPiP, self.pipShown), "yellow")
+
+ def pipShown(self):
+ return self.session.pipshown
+
+ def pipHandles0Action(self):
+ return self.pipShown() and config.usage.pip_zero_button.value != "standard"
+
+ def getShowHideName(self):
+ if self.session.pipshown:
+ return _("Disable Picture in Picture")
+ else:
+ return _("Activate Picture in Picture")
+
+ def getSwapName(self):
+ return _("Swap Services")
+
+ def getMoveName(self):
+ return _("Move Picture in Picture")
+
+ def showPiP(self):
+ if self.session.pipshown:
+ del self.session.pip
+ self.session.pipshown = False
+ else:
+ self.session.pip = self.session.instantiateDialog(PictureInPicture)
+ self.session.pip.show()
+ newservice = self.session.nav.getCurrentlyPlayingServiceReference()
+ if self.session.pip.playService(newservice):
+ self.session.pipshown = True
+ self.session.pip.servicePath = self.servicelist.getCurrentServicePath()
+ else:
+ self.session.pipshown = False
+ del self.session.pip
+ self.session.nav.playService(newservice)
+
+ def swapPiP(self):
+ swapservice = self.session.nav.getCurrentlyPlayingServiceReference()
+ if self.session.pip.servicePath:
+ servicepath = self.servicelist.getCurrentServicePath()
+ ref=servicepath[len(servicepath)-1]
+ pipref=self.session.pip.getCurrentService()
+ self.session.pip.playService(swapservice)
+ self.servicelist.setCurrentServicePath(self.session.pip.servicePath)
+ if pipref.toString() != ref.toString(): # is a subservice ?
+ self.session.nav.stopService() # stop portal
+ self.session.nav.playService(pipref) # start subservice
+ self.session.pip.servicePath=servicepath
+
+ def movePiP(self):
+ self.session.open(PiPSetup, pip = self.session.pip)
+
+ def pipDoHandle0Action(self):
+ use = config.usage.pip_zero_button.value
+ if "swap" == use:
+ self.swapPiP()
+ elif "swapstop" == use:
+ self.swapPiP()
+ self.showPiP()
+ elif "stop" == use:
+ self.showPiP()
+
+from RecordTimer import parseEvent, RecordTimerEntry