X-Git-Url: https://git.cweiske.de/enigma2.git/blobdiff_plain/9c22bd14f76cbc38c14d417dc185cdcdfc610665..b29744104c479d3da5cd6ff7d9945947e7a1362d:/lib/python/Screens/InfoBarGenerics.py diff --git a/lib/python/Screens/InfoBarGenerics.py b/lib/python/Screens/InfoBarGenerics.py index f40feb3f..8d436013 100644 --- a/lib/python/Screens/InfoBarGenerics.py +++ b/lib/python/Screens/InfoBarGenerics.py @@ -32,6 +32,7 @@ from enigma import * import time import os +import bisect from Components.config import config, currentConfigSelectionElement @@ -41,7 +42,7 @@ from Menu import MainMenu, mdom class InfoBarDish: def __init__(self): self.dishDialog = self.session.instantiateDialog(Dish) - self.onShown.append(self.dishDialog.show) + self.onLayoutFinish.append(self.dishDialog.show) class InfoBarShowHide: """ InfoBar show/hide control, accepts toggleShow and hide actions, might start @@ -81,6 +82,10 @@ class InfoBarShowHide: def __onHide(self): self.__state = self.STATE_HIDDEN + def doShow(self): + self.show() + self.startHideTimer() + def doTimerHide(self): self.hideTimer.stop() if self.__state == self.STATE_SHOWN: @@ -95,11 +100,14 @@ class InfoBarShowHide: def lockShow(self): self.__locked = self.__locked + 1 - self.show() + if self.execing: + self.show() + self.hideTimer.stop() def unlockShow(self): self.__locked = self.__locked - 1 - self.startHideTimer() + if self.execing: + self.startHideTimer() # def startShow(self): # self.instance.m_animation.startMoveAnimation(ePoint(0, 600), ePoint(0, 380), 100) @@ -209,7 +217,7 @@ class InfoBarNumberZap: # print "You pressed number " + str(number) if number == 0: self.servicelist.recallPrevService() - self.show() + self.doShow() else: self.session.openWithCallback(self.numberEntered, NumberZap, number) @@ -270,8 +278,16 @@ class InfoBarChannelSelection: "switchChannelDown": self.switchChannelDown, "zapUp": (self.zapUp, _("next channel")), "zapDown": (self.zapDown, _("previous channel")), + "historyBack": (self.historyBack, _("previous channel in history")), + "historyNext": (self.historyNext, _("next channel in history")) }) + def historyBack(self): + self.servicelist.historyBack() + + def historyNext(self): + self.servicelist.historyNext() + def switchChannelUp(self): self.servicelist.moveUp() self.session.execDialog(self.servicelist) @@ -280,15 +296,21 @@ class InfoBarChannelSelection: self.servicelist.moveDown() self.session.execDialog(self.servicelist) - def zapUp(self): + def zapUp(self): + if currentConfigSelectionElement(config.usage.quickzap_bouquet_change) == "yes": + if self.servicelist.inBouquet() and self.servicelist.atBegin(): + self.servicelist.prevBouquet() self.servicelist.moveUp() self.servicelist.zap() - self.show() + self.doShow() - def zapDown(self): - self.servicelist.moveDown() + def zapDown(self): + if currentConfigSelectionElement(config.usage.quickzap_bouquet_change) == "yes" and self.servicelist.inBouquet() and self.servicelist.atEnd(): + self.servicelist.nextBouquet() + else: + self.servicelist.moveDown() self.servicelist.zap() - self.show() + self.doShow() class InfoBarMenu: """ Handles a menu action, to open the (main) menu """ @@ -322,7 +344,7 @@ class InfoBarEPG: self.servicelist.setCurrentSelection(service) #select the service in servicelist self.servicelist.zap() - def openBouquetEPG(self, bouquet): + def openBouquetEPG(self, bouquet, withCallback=True): ptr=eEPGCache.getInstance() services = [ ] servicelist = eServiceCenter.getInstance().list(bouquet) @@ -336,22 +358,28 @@ class InfoBarEPG: services.append(ServiceReference(service)) if len(services): self.epg_bouquet = bouquet - self.session.openWithCallback(self.closed, EPGSelection, services, self.zapToService) + if withCallback: + self.session.openWithCallback(self.closed, EPGSelection, services, self.zapToService) + else: + self.session.open(EPGSelection, services, self.zapToService) def closed(self, ret): if ret: self.close(ret) - def openMultiServiceEPG(self): + def openMultiServiceEPG(self, withCallback=True): bouquets = self.servicelist.getBouquetList() if bouquets is None: cnt = 0 else: cnt = len(bouquets) if cnt > 1: # show bouquet list - self.session.openWithCallback(self.closed, BouquetSelector, bouquets, self.openBouquetEPG) + if withCallback: + self.session.openWithCallback(self.closed, BouquetSelector, bouquets, self.openBouquetEPG) + else: + self.session.open(BouquetSelector, bouquets, self.openBouquetEPG) elif cnt == 1: - self.openBouquetEPG(bouquets[0][1]) + self.openBouquetEPG(bouquets[0][1], withCallback) def openSingleServiceEPG(self): ref=self.session.nav.getCurrentlyPlayingServiceReference() @@ -381,7 +409,7 @@ class InfoBarEPG: self.session.open(EventViewEPGSelect, self.epglist[0], ServiceReference(ref), self.eventViewCallback, self.openSingleServiceEPG, self.openMultiServiceEPG) else: print "no epg for the service avail.. so we show multiepg instead of eventinfo" - self.openMultiServiceEPG() + self.openMultiServiceEPG(False) def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying if len(self.epglist) > 1: @@ -443,7 +471,7 @@ class InfoBarEvent: self["Event_Now"] = EventInfo(self.session.nav, EventInfo.Now) self["Event_Next"] = EventInfo(self.session.nav, EventInfo.Next) - self["Event_Now_Duration"] = EventInfo(self.session.nav, EventInfo.Now_Duration) + self["Event_Now_Duration"] = EventInfo(self.session.nav, EventInfo.Now_Remaining) self["Event_Next_Duration"] = EventInfo(self.session.nav, EventInfo.Next_Duration) self["Now_ProgressBar"] = EventInfoProgress(self.session.nav, EventInfo.Now) @@ -456,7 +484,7 @@ class InfoBarSeek: """handles actions like seeking, pause""" # ispause, isff, issm - SEEK_STATE_PLAY = (0, 0, 0, "") + SEEK_STATE_PLAY = (0, 0, 0, ">") SEEK_STATE_PAUSE = (1, 0, 0, "||") SEEK_STATE_FF_2X = (0, 2, 0, ">> 2x") SEEK_STATE_FF_4X = (0, 4, 0, ">> 4x") @@ -465,7 +493,7 @@ class InfoBarSeek: SEEK_STATE_FF_64X = (0, 64, 0, ">> 64x") SEEK_STATE_FF_128X = (0, 128, 0, ">> 128x") - SEEK_STATE_BACK_4X = (0, -4, 0, "<< 4x") + SEEK_STATE_BACK_16X = (0, -16, 0, "<< 16x") SEEK_STATE_BACK_32X = (0, -32, 0, "<< 32x") SEEK_STATE_BACK_64X = (0, -64, 0, "<< 64x") SEEK_STATE_BACK_128X = (0, -128, 0, "<< 128x") @@ -520,14 +548,22 @@ class InfoBarSeek: del self.fwdKeyTimer del self.rwdKeyTimer - def isSeekable(self): + def getSeek(self): service = self.session.nav.getCurrentService() if service is None: return False - if service.seek() is None: + + seek = service.seek() + + if seek is None or not seek.isCurrentlySeekable(): + return None + + return seek + + def isSeekable(self): + if self.getSeek() is None: return False - else: - return True + return True def __seekableStatusChanged(self): print "seekable status changed!" @@ -548,7 +584,7 @@ class InfoBarSeek: if service is None: return False - if service.seek() is None: + if not self.isSeekable(): if state not in [self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE]: state = self.SEEK_STATE_PLAY @@ -594,20 +630,21 @@ class InfoBarSeek: if service is None: return - seekable = service.seek() + seekable = self.getSeek() if seekable is None: return + seekable.seekTo(90 * seektime) def seekFwd(self): print "start fwd timer" self.fwdtimer = True - self.fwdKeyTimer.start(500) + self.fwdKeyTimer.start(1000) def seekBack(self): print "start rewind timer" self.rwdtimer = True - self.rwdKeyTimer.start(500) + self.rwdKeyTimer.start(1000) def seekFwdUp(self): print "seekFwdUp" @@ -623,8 +660,8 @@ class InfoBarSeek: self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_64X, self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_128X, self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_128X, - self.SEEK_STATE_BACK_4X: self.SEEK_STATE_PLAY, - self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_4X, + self.SEEK_STATE_BACK_16X: self.SEEK_STATE_PLAY, + self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_16X, self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_32X, self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_64X, self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_HALF, @@ -640,7 +677,7 @@ class InfoBarSeek: self.rwdtimer = False lookup = { - self.SEEK_STATE_PLAY: self.SEEK_STATE_BACK_4X, + self.SEEK_STATE_PLAY: self.SEEK_STATE_BACK_16X, self.SEEK_STATE_PAUSE: self.SEEK_STATE_PAUSE, self.SEEK_STATE_FF_2X: self.SEEK_STATE_PLAY, self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_2X, @@ -648,7 +685,7 @@ class InfoBarSeek: self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_8X, self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_32X, self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_64X, - self.SEEK_STATE_BACK_4X: self.SEEK_STATE_BACK_32X, + self.SEEK_STATE_BACK_16X: self.SEEK_STATE_BACK_32X, self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_64X, self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_128X, self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_128X, @@ -667,13 +704,9 @@ class InfoBarSeek: def fwdSeekTo(self, minutes): print "Seek", minutes, "minutes forward" if minutes != 0: - service = self.session.nav.getCurrentService() - if service is None: - return - seekable = service.seek() - if seekable is None: - return - seekable.seekRelative(1, minutes * 60 * 90000) + seekable = self.getSeek() + if seekable is not None: + seekable.seekRelative(1, minutes * 60 * 90000) def rwdTimerFire(self): print "rwdTimerFire" @@ -697,10 +730,22 @@ class InfoBarSeek: self.lockedBecauseOfSkipping = True def __evEOF(self): - self.setSeekState(self.SEEK_STATE_PAUSE) + if self.seekstate != self.SEEK_STATE_PLAY: + self.setSeekState(self.SEEK_STATE_PAUSE) + # HACK + self.getSeek().seekRelative(1, -90000) + self.setSeekState(self.SEEK_STATE_PLAY) + else: + self.setSeekState(self.SEEK_STATE_PAUSE) def __evSOF(self): self.setSeekState(self.SEEK_STATE_PLAY) + self.doSeek(0) + + def seekRelative(self, diff): + seekable = self.getSeek() + if seekable is not None: + seekable.seekRelative(0, diff) from Screens.PVRState import PVRState @@ -708,12 +753,17 @@ class InfoBarPVRState: def __init__(self): self.onPlayStateChanged.append(self.__playStateChanged) self.pvrStateDialog = self.session.instantiateDialog(PVRState) - self.onShow.append(self.pvrStateDialog.show) + self.onShow.append(self.__mayShow) self.onHide.append(self.pvrStateDialog.hide) + def __mayShow(self): + if self.seekstate != self.SEEK_STATE_PLAY: + self.pvrStateDialog.show() + def __playStateChanged(self, state): playstateString = state[3] self.pvrStateDialog["state"].setText(playstateString) + self.__mayShow() class InfoBarShowMovies: @@ -767,7 +817,7 @@ class InfoBarTimeshift: { "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "pause key" "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause # something like "backward key" - }) + }, prio=-1) # priority over record self.timeshift_enabled = 0 self.timeshift_state = 0 @@ -784,9 +834,6 @@ class InfoBarTimeshift: return service.timeshift() def startTimeshift(self): - # TODO: check for harddisk! (or do this in the interface? would make - # more sense... for example radio could be timeshifted in memory, - # and the decision can't be made here) print "enable timeshift" ts = self.getTimeshift() if ts is None: @@ -809,12 +856,23 @@ class InfoBarTimeshift: else: print "timeshift failed" - # nyi def stopTimeshift(self): + if not self.timeshift_enabled: + return print "disable timeshift" ts = self.getTimeshift() if ts is None: return + 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 @@ -834,6 +892,7 @@ class InfoBarTimeshift: else: self.setSeekState(self.SEEK_STATE_PLAY) ts.activateTimeshift() + self.seekRelative(0) # same as activateTimeshiftEnd, but pauses afterwards. def activateTimeshiftEndAndPause(self): @@ -878,15 +937,14 @@ class InfoBarInstantRecord: "instantRecord": (self.instantRecord, "Instant Record..."), }) self.recording = None - self["BlinkingPoint"] = BlinkingPixmapConditional() - self.onShown.append(self["BlinkingPoint"].hideWidget) + self.onLayoutFinish.append(self["BlinkingPoint"].hideWidget) self["BlinkingPoint"].setConnect(self.session.nav.RecordTimer.isRecording) - + def stopCurrentRecording(self): self.session.nav.RecordTimer.removeEntry(self.recording) self.recording = None - + def startInstantRecording(self): serviceref = self.session.nav.getCurrentlyPlayingServiceReference() @@ -902,15 +960,8 @@ class InfoBarInstantRecord: if event is not None: data = parseEvent(event) - begin = data[0] - if begin < time.time(): - begin = time.time() - - end = data[1] - if end < begin: - end = begin - - end += 3600 * 10 + begin = time.time() + end = begin + 3600 * 10 data = (begin, end, data[2], data[3], data[4]) else: @@ -993,19 +1044,27 @@ class InfoBarAdditionalInfo: self["ButtonRed"] = PixmapConditional(withTimer = False) self["ButtonRed"].setConnect(lambda: harddiskmanager.HDDCount() > 0) - self.onShown.append(self["ButtonRed"].update) + self.onLayoutFinish.append(self["ButtonRed"].update) self["ButtonRedText"] = LabelConditional(text = _("Record"), withTimer = False) self["ButtonRedText"].setConnect(lambda: harddiskmanager.HDDCount() > 0) - self.onShown.append(self["ButtonRedText"].update) + self.onLayoutFinish.append(self["ButtonRedText"].update) self["ButtonGreen"] = Pixmap() self["ButtonGreenText"] = Label(_("Subservices")) self["ButtonYellow"] = PixmapConditional(withTimer = False) - self["ButtonYellow"].setConnect(lambda: False) + self["ButtonYellow"].setConnect(lambda: harddiskmanager.HDDCount() > 0) + self["ButtonYellowText"] = LabelConditional(text = _("Timeshifting"), withTimer = False) + self["ButtonYellowText"].setConnect(lambda: harddiskmanager.HDDCount() > 0) + self.onLayoutFinish.append(self["ButtonYellow"].update) + self.onLayoutFinish.append(self["ButtonYellowText"].update) self["ButtonBlue"] = PixmapConditional(withTimer = False) self["ButtonBlue"].setConnect(lambda: False) + self["ButtonBlueText"] = LabelConditional(text = _("Extensions"), withTimer = False) + self["ButtonBlueText"].setConnect(lambda: False) + self.onLayoutFinish.append(self["ButtonBlue"].update) + self.onLayoutFinish.append(self["ButtonBlueText"].update) self.session.nav.event.append(self.gotServiceEvent) # we like to get service events @@ -1105,3 +1164,119 @@ class InfoBarServiceNotifications: self.setSeekState(self.SEEK_STATE_PLAY) except: pass + +class InfoBarCueSheetSupport: + CUT_TYPE_IN = 0 + CUT_TYPE_OUT = 1 + CUT_TYPE_MARK = 2 + + def __init__(self): + self["CueSheetActions"] = HelpableActionMap(self, "InfobarCueSheetActions", + { + "jumpPreviousMark": (self.jumpPreviousMark, "jump to next marked position"), + "jumpNextMark": (self.jumpNextMark, "jump to previous marked position"), + "toggleMark": (self.toggleMark, "toggle a cut mark at the current position") + }, prio=1) + + self.cut_list = [ ] + self.__event_tracker = ServiceEventTracker(screen=self, eventmap= + { + iPlayableService.evStart: self.__serviceStarted, + }) + + def __serviceStarted(self): + print "new service started! trying to download cuts!" + self.downloadCuesheet() + + def __getSeekable(self): + service = self.session.nav.getCurrentService() + if service is None: + return None + return service.seek() + + def __getCurrentPosition(self): + seek = self.__getSeekable() + if seek is None: + return None + r = seek.getPlayPosition() + if r[0]: + return None + return long(r[1]) + + def jumpPreviousNextMark(self, cmp, alternative=None): + current_pos = self.__getCurrentPosition() + if current_pos is None: + return + mark = self.getNearestCutPoint(current_pos, cmp=cmp) + if mark is not None: + pts = mark[0] + elif alternative is not None: + pts = alternative + else: + return + + seekable = self.__getSeekable() + if seekable is not None: + seekable.seekTo(pts) + + def jumpPreviousMark(self): + print "jumpPreviousMark" + # we add 2 seconds, so if the play position is <2s after + # the mark, the mark before will be used + self.jumpPreviousNextMark(lambda x: -x-5*90000, alternative=0) + + def jumpNextMark(self): + print "jumpNextMark" + self.jumpPreviousNextMark(lambda x: x) + + def getNearestCutPoint(self, pts, cmp=abs): + # can be optimized + nearest = None + for cp in self.cut_list: + diff = cmp(cp[0] - pts) + if diff >= 0 and (nearest is None or cmp(nearest[0] - pts) > diff): + nearest = cp + return nearest + + def toggleMark(self): + print "toggleMark" + current_pos = self.__getCurrentPosition() + if current_pos is None: + print "not seekable" + return + + print "current position: ", current_pos + + nearest_cutpoint = self.getNearestCutPoint(current_pos) + print "nearest_cutpoint: ", nearest_cutpoint + + if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < 5*90000: + self.cut_list.remove(nearest_cutpoint) + else: + bisect.insort(self.cut_list, (current_pos, self.CUT_TYPE_MARK)) + + self.uploadCuesheet() + + def __getCuesheet(self): + service = self.session.nav.getCurrentService() + if service is None: + return None + return service.cueSheet() + + def uploadCuesheet(self): + cue = self.__getCuesheet() + + if cue is None: + print "upload failed, no cuesheet interface" + return + cue.setCutList(self.cut_list) + + def downloadCuesheet(self): + cue = self.__getCuesheet() + + if cue is None: + print "upload failed, no cuesheet interface" + return + self.cut_list = cue.getCutList() + + print "cuts:", self.cut_list