X-Git-Url: https://git.cweiske.de/enigma2.git/blobdiff_plain/9db4b5a47686981facc54c3eeab1113a814a961f..5e942862b2017443ec34831f649f890f8215a534:/lib/python/Plugins/Extensions/CutListEditor/plugin.py diff --git a/lib/python/Plugins/Extensions/CutListEditor/plugin.py b/lib/python/Plugins/Extensions/CutListEditor/plugin.py index 0ffc7008..76a7bdc8 100644 --- a/lib/python/Plugins/Extensions/CutListEditor/plugin.py +++ b/lib/python/Plugins/Extensions/CutListEditor/plugin.py @@ -4,15 +4,15 @@ from Screens.Screen import Screen from Screens.MessageBox import MessageBox from Components.ServicePosition import ServicePositionGauge from Components.ActionMap import HelpableActionMap -from Components.MenuList import MenuList -from Components.MultiContent import MultiContentEntryText, RT_HALIGN_RIGHT +from Components.MultiContent import MultiContentEntryText from Components.ServiceEventTracker import ServiceEventTracker - -from Screens.InfoBarGenerics import InfoBarSeek, InfoBarCueSheetSupport - +from Components.VideoWindow import VideoWindow +from Screens.InfoBarGenerics import InfoBarSeek, InfoBarCueSheetSupport, InfoBarServiceName from Components.GUIComponent import GUIComponent - -from enigma import eListboxPythonMultiContent, eListbox, gFont, iPlayableService +from enigma import eListboxPythonMultiContent, eListbox, gFont, iPlayableService, RT_HALIGN_RIGHT +from Screens.FixedMenu import FixedMenu +from Screens.HelpMenu import HelpableScreen +import bisect def CutListEntry(where, what): res = [ (where, what) ] @@ -27,11 +27,79 @@ def CutListEntry(where, what): type = "OUT" elif what == 2: type = "MARK" + elif what == 3: + type = "LAST" res.append(MultiContentEntryText(size=(400, 20), text = "%dh:%02dm:%02ds:%03d" % (h, m, s, ms))) res.append(MultiContentEntryText(pos=(400,0), size=(130, 20), text = type, flags = RT_HALIGN_RIGHT)) return res +class CutListContextMenu(FixedMenu): + RET_STARTCUT = 0 + RET_ENDCUT = 1 + RET_DELETECUT = 2 + RET_MARK = 3 + RET_DELETEMARK = 4 + RET_REMOVEBEFORE = 5 + RET_REMOVEAFTER = 6 + + SHOW_STARTCUT = 0 + SHOW_ENDCUT = 1 + SHOW_DELETECUT = 2 + + def __init__(self, session, state, nearmark): + menu = [(_("back"), self.close)] #, (None, )] + + if state == self.SHOW_STARTCUT: + menu.append((_("start cut here"), self.startCut)) + else: + menu.append((_("start cut here"), )) + + if state == self.SHOW_ENDCUT: + menu.append((_("end cut here"), self.endCut)) + else: + menu.append((_("end cut here"), )) + + if state == self.SHOW_DELETECUT: + menu.append((_("delete cut"), self.deleteCut)) + else: + menu.append((_("delete cut"), )) + + menu.append((_("remove before this position"), self.removeBefore)) + menu.append((_("remove after this position"), self.removeAfter)) + +# menu.append((None, )) + + if not nearmark: + menu.append((_("insert mark here"), self.insertMark)) + else: + menu.append((_("remove this mark"), self.removeMark)) + + FixedMenu.__init__(self, session, _("Cut"), menu) + self.skinName = "Menu" + + def startCut(self): + self.close(self.RET_STARTCUT) + + def endCut(self): + self.close(self.RET_ENDCUT) + + def deleteCut(self): + self.close(self.RET_DELETECUT) + + def insertMark(self): + self.close(self.RET_MARK) + + def removeMark(self): + self.close(self.RET_DELETEMARK) + + def removeBefore(self): + self.close(self.RET_REMOVEBEFORE) + + def removeAfter(self): + self.close(self.RET_REMOVEAFTER) + + class CutList(GUIComponent): def __init__(self, list): GUIComponent.__init__(self) @@ -39,31 +107,31 @@ class CutList(GUIComponent): self.setList(list) self.l.setFont(0, gFont("Regular", 20)) self.onSelectionChanged = [ ] - + def getCurrent(self): return self.l.getCurrentSelection() - + def getCurrentIndex(self): return self.l.getCurrentSelectionIndex() - - def GUIcreate(self, parent): - self.instance = eListbox(parent) - self.instance.setContent(self.l) - self.instance.setItemHeight(30) - self.instance.selectionChanged.get().append(self.selectionChanged) + + GUI_WIDGET = eListbox + + def postWidgetCreate(self, instance): + instance.setContent(self.l) + instance.setItemHeight(30) + instance.selectionChanged.get().append(self.selectionChanged) + + def preWidgetRemove(self, instance): + instance.setContent(None) + instance.selectionChanged.get().remove(self.selectionChanged) def selectionChanged(self): for x in self.onSelectionChanged: x() - - def GUIdelete(self): - self.instance.selectionChanged.get().remove(self.selectionChanged) - self.instance.setContent(None) - self.instance = None - + def invalidateEntry(self, index): self.l.invalidateEntry(index) - + def setIndex(self, index, data): self.list[index] = data self.invalidateEntry(index) @@ -71,38 +139,57 @@ class CutList(GUIComponent): def setList(self, list): self.list = list self.l.setList(self.list) - + def setSelection(self, index): if self.instance is not None: self.instance.moveSelectionTo(index) -class CutListEditor(Screen, InfoBarSeek, InfoBarCueSheetSupport): +class CutListEditor(Screen, InfoBarSeek, InfoBarCueSheetSupport, InfoBarServiceName, HelpableScreen): skin = """ - - - + + + + + + + + Name + + + + PositionDetailed + + + + """ def __init__(self, session, service): self.skin = CutListEditor.skin Screen.__init__(self, session) - InfoBarSeek.__init__(self) + InfoBarSeek.__init__(self, actionmap = "CutlistSeekActions") InfoBarCueSheetSupport.__init__(self) + InfoBarServiceName.__init__(self) + HelpableScreen.__init__(self) + self.old_service = session.nav.getCurrentlyPlayingServiceReference() session.nav.playService(service) - + service = session.nav.getCurrentService() cue = service and service.cueSheet() if cue is not None: # disable cutlists. we want to freely browse around in the movie print "cut lists disabled!" cue.setCutListEnable(0) - + self.downloadCuesheet() - + self["Timeline"] = ServicePositionGauge(self.session.nav) self["Cutlist"] = CutList(self.getCutlist()) self["Cutlist"].onSelectionChanged.append(self.selectionChanged) - + + self["Video"] = VideoWindow(decoder = 0) + self["actions"] = HelpableActionMap(self, "CutListEditorActions", { "setIn": (self.setIn, _("Make this mark an 'in' point")), @@ -110,11 +197,12 @@ class CutListEditor(Screen, InfoBarSeek, InfoBarCueSheetSupport): "setMark": (self.setMark, _("Make this mark just a mark")), "addMark": (self.__addMark, _("Add a mark")), "removeMark": (self.__removeMark, _("Remove a mark")), - "leave": (self.exit, _("Exit editor")) - }) - + "leave": (self.exit, _("Exit editor")), + "showMenu": (self.showMenu, _("menu")), + }, prio=-4) + self.tutorial_seen = False - + self.onExecBegin.append(self.showTutorial) self.__event_tracker = ServiceEventTracker(screen=self, eventmap= { @@ -123,30 +211,32 @@ class CutListEditor(Screen, InfoBarSeek, InfoBarCueSheetSupport): # to track new entries we save the last version of the cutlist self.last_cuts = [ ] - + self.cut_start = None + def showTutorial(self): if not self.tutorial_seen: self.tutorial_seen = True self.session.open(MessageBox, - """Welcome to the Cutlist editor. It has a *very* unintuitive handling: + """Welcome to the Cutlist editor. + +Seek to the start of the stuff you want to cut away. Press OK, select 'start cut'. -You can add use the color keys to move around in the recorded movie. -By pressing shift-yellow, you can add a mark or remove an existing one. -You can then assign them to be either 'in' or 'out' positions by selecting them in the list and pressing 1 or 2. +Then seek to the end, press OK, select 'end cut'. That's it. """, MessageBox.TYPE_INFO) - + def checkSkipShowHideLock(self): pass - + def setType(self, index, type): - self.cut_list[index] = (self.cut_list[index][0], type) - self["Cutlist"].setIndex(index, CutListEntry(*self.cut_list[index])) - + if len(self.cut_list): + self.cut_list[index] = (self.cut_list[index][0], type) + self["Cutlist"].setIndex(index, CutListEntry(*self.cut_list[index])) + def setIn(self): m = self["Cutlist"].getCurrentIndex() self.setType(m, 0) self.uploadCuesheet() - + def setOut(self): m = self["Cutlist"].getCurrentIndex() self.setType(m, 1) @@ -156,17 +246,18 @@ You can then assign them to be either 'in' or 'out' positions by selecting them m = self["Cutlist"].getCurrentIndex() self.setType(m, 2) self.uploadCuesheet() - + def __addMark(self): self.toggleMark(onlyadd=True, tolerance=90000) # do not allow two marks in <1s - + def __removeMark(self): m = self["Cutlist"].getCurrent() m = m and m[0] if m is not None: self.removeMark(m) - + def exit(self): + self.session.nav.playService(self.old_service) self.close() def getCutlist(self): @@ -190,19 +281,128 @@ You can then assign them to be either 'in' or 'out' positions by selecting them def refillList(self): print "cue sheet changed, refilling" self.downloadCuesheet() - + # get the first changed entry, and select it new_list = self.getCutlist() self["Cutlist"].setList(new_list) - + for i in range(min(len(new_list), len(self.last_cuts))): if new_list[i] != self.last_cuts[i]: self["Cutlist"].setSelection(i) break self.last_cuts = new_list -def main(session, service): + def getStateForPosition(self, pos): + state = 0 # in + + # when first point is "in", the beginning is "out" + if len(self.cut_list) and self.cut_list[0][1] == 0: + state = 1 + + for (where, what) in self.cut_list: + if where < pos: + if what == 0: # in + state = 0 + elif what == 1: # out + state = 1 + return state + + def showMenu(self): + curpos = self.cueGetCurrentPosition() + if curpos is None: + return + + self.setSeekState(self.SEEK_STATE_PAUSE) + + self.context_position = curpos + + self.context_nearest_mark = self.toggleMark(onlyreturn=True) + + cur_state = self.getStateForPosition(curpos) + if cur_state == 0: + print "currently in 'IN'" + if self.cut_start is None or self.context_position < self.cut_start: + state = CutListContextMenu.SHOW_STARTCUT + else: + state = CutListContextMenu.SHOW_ENDCUT + else: + print "currently in 'OUT'" + state = CutListContextMenu.SHOW_DELETECUT + + if self.context_nearest_mark is None: + nearmark = False + else: + nearmark = True + + self.session.openWithCallback(self.menuCallback, CutListContextMenu, state, nearmark) + + def menuCallback(self, *result): + if not len(result): + return + result = result[0] + + if result == CutListContextMenu.RET_STARTCUT: + self.cut_start = self.context_position + elif result == CutListContextMenu.RET_ENDCUT: + # remove in/out marks between the new cut + for (where, what) in self.cut_list[:]: + if self.cut_start <= where <= self.context_position and what in [0,1]: + self.cut_list.remove((where, what)) + + bisect.insort(self.cut_list, (self.cut_start, 1)) + bisect.insort(self.cut_list, (self.context_position, 0)) + self.uploadCuesheet() + self.cut_start = None + elif result == CutListContextMenu.RET_DELETECUT: + out_before = None + in_after = None + + for (where, what) in self.cut_list: + if what == 1 and where < self.context_position: # out + out_before = (where, what) + elif what == 0 and where < self.context_position: # in, before out + out_before = None + elif what == 0 and where > self.context_position and in_after is None: + in_after = (where, what) + + if out_before is not None: + self.cut_list.remove(out_before) + + if in_after is not None: + self.cut_list.remove(in_after) + self.uploadCuesheet() + elif result == CutListContextMenu.RET_MARK: + self.__addMark() + elif result == CutListContextMenu.RET_DELETEMARK: + self.cut_list.remove(self.context_nearest_mark) + self.uploadCuesheet() + elif result == CutListContextMenu.RET_REMOVEBEFORE: + # remove in/out marks before current position + for (where, what) in self.cut_list[:]: + if where <= self.context_position and what in [0,1]: + self.cut_list.remove((where, what)) + # add 'in' point + bisect.insort(self.cut_list, (self.context_position, 0)) + self.uploadCuesheet() + elif result == CutListContextMenu.RET_REMOVEAFTER: + # remove in/out marks after current position + for (where, what) in self.cut_list[:]: + if where >= self.context_position and what in [0,1]: + self.cut_list.remove((where, what)) + # add 'out' point + bisect.insort(self.cut_list, (self.context_position, 1)) + self.uploadCuesheet() + + # we modify the "play" behaviour a bit: + # if we press pause while being in slowmotion, we will pause (and not play) + def playpauseService(self): + if self.seekstate != self.SEEK_STATE_PLAY and not self.isStateSlowMotion(self.seekstate): + self.unPauseService() + else: + self.pauseService() + +def main(session, service, **kwargs): session.open(CutListEditor, service) -def Plugins(): +def Plugins(**kwargs): return PluginDescriptor(name="Cutlist Editor", description=_("Cutlist editor..."), where = PluginDescriptor.WHERE_MOVIELIST, fnc=main)