X-Git-Url: https://git.cweiske.de/enigma2.git/blobdiff_plain/ed40f6f85c9c07c3c1224ae20601082c0309a631..b209ca0ac28a2e1ddc1f27642b0dec0f376b0ad2:/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 ba946f98..abd606d6 100644 --- a/lib/python/Plugins/Extensions/CutListEditor/plugin.py +++ b/lib/python/Plugins/Extensions/CutListEditor/plugin.py @@ -4,17 +4,21 @@ 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 -from Components.ServiceEventTracker import ServiceEventTracker +from Components.ServiceEventTracker import ServiceEventTracker, InfoBarBase +from Components.VideoWindow import VideoWindow +from Components.Label import Label from Screens.InfoBarGenerics import InfoBarSeek, InfoBarCueSheetSupport from Components.GUIComponent import GUIComponent from enigma import eListboxPythonMultiContent, eListbox, gFont, iPlayableService, RT_HALIGN_RIGHT from Screens.FixedMenu import FixedMenu +from Screens.HelpMenu import HelpableScreen +from ServiceReference import ServiceReference +from Components.Sources.List import List + import bisect def CutListEntry(where, what): - res = [ (where, what) ] w = where / 90 ms = w % 1000 s = (w / 1000) % 60 @@ -22,16 +26,17 @@ def CutListEntry(where, what): h = w / 3600000 if what == 0: type = "IN" + type_col = 0x004000 elif what == 1: type = "OUT" + type_col = 0x400000 elif what == 2: type = "MARK" + type_col = 0x000040 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 + type_col = 0x000000 + return ((where, what), "%dh:%02dm:%02ds:%03d" % (h, m, s, ms), type, type_col) class CutListContextMenu(FixedMenu): RET_STARTCUT = 0 @@ -41,11 +46,12 @@ class CutListContextMenu(FixedMenu): RET_DELETEMARK = 4 RET_REMOVEBEFORE = 5 RET_REMOVEAFTER = 6 - + RET_GRABFRAME = 7 + SHOW_STARTCUT = 0 SHOW_ENDCUT = 1 SHOW_DELETECUT = 2 - + def __init__(self, session, state, nearmark): menu = [(_("back"), self.close)] #, (None, )] @@ -74,6 +80,7 @@ class CutListContextMenu(FixedMenu): else: menu.append((_("remove this mark"), self.removeMark)) + menu.append((_("grab this frame as bitmap"), self.grabFrame)) FixedMenu.__init__(self, session, _("Cut"), menu) self.skinName = "Menu" @@ -91,82 +98,77 @@ class CutListContextMenu(FixedMenu): def removeMark(self): self.close(self.RET_DELETEMARK) - + def removeBefore(self): self.close(self.RET_REMOVEBEFORE) def removeAfter(self): self.close(self.RET_REMOVEAFTER) + def grabFrame(self): + self.close(self.RET_GRABFRAME) -class CutList(GUIComponent): - def __init__(self, list): - GUIComponent.__init__(self) - self.l = eListboxPythonMultiContent() - 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() - - GUI_WIDGET = eListbox - - def postWidgetCreate(self, instance): - instance.setContent(self.l) - instance.setItemHeight(30) - instance.selectionChanged.get().append(self.selectionChanged) - - def selectionChanged(self): - for x in self.onSelectionChanged: - x() - - def invalidateEntry(self, index): - self.l.invalidateEntry(index) - - def setIndex(self, index, data): - self.list[index] = data - self.invalidateEntry(index) - - 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, InfoBarBase, InfoBarSeek, InfoBarCueSheetSupport, HelpableScreen): skin = """ - - - - """ + + + + Format:%A %B %d, %H:%M + + + + + Name + + + Position,Detailed + + + + + + + {"template": [ + MultiContentEntryText(size=(125, 20), text = 1, backcolor = MultiContentTemplateColor(3)), + MultiContentEntryText(pos=(125,0), size=(50, 20), text = 2, flags = RT_HALIGN_RIGHT, backcolor = MultiContentTemplateColor(3)) + ], + "fonts": [gFont("Regular", 18)], + "itemHeight": 20 + } + + + + + """ + def __init__(self, session, service): self.skin = CutListEditor.skin Screen.__init__(self, session) - InfoBarSeek.__init__(self) + InfoBarSeek.__init__(self, actionmap = "CutlistSeekActions") InfoBarCueSheetSupport.__init__(self) + InfoBarBase.__init__(self, steal_current_service = True) + 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["cutlist"] = List(self.getCutlist()) + self["cutlist"].onSelectionChanged.append(self.selectionChanged) + self["SeekState"] = Label() + self.onPlayStateChanged.append(self.updateStateLabel) + self.updateStateLabel(self.seekstate) + + self["Video"] = VideoWindow(decoder = 0) + self["actions"] = HelpableActionMap(self, "CutListEditorActions", { "setIn": (self.setIn, _("Make this mark an 'in' point")), @@ -175,11 +177,11 @@ class CutListEditor(Screen, InfoBarSeek, InfoBarCueSheetSupport): "addMark": (self.__addMark, _("Add a mark")), "removeMark": (self.__removeMark, _("Remove a mark")), "leave": (self.exit, _("Exit editor")), - "showMenu": self.showMenu, + "showMenu": (self.showMenu, _("menu")), }, prio=-4) - + self.tutorial_seen = False - + self.onExecBegin.append(self.showTutorial) self.__event_tracker = ServiceEventTracker(screen=self, eventmap= { @@ -187,54 +189,55 @@ class CutListEditor(Screen, InfoBarSeek, InfoBarCueSheetSupport): }) # to track new entries we save the last version of the cutlist - self.last_cuts = [ ] + self.last_cuts = self.getCutlist() self.cut_start = None - + self.inhibit_seek = False + self.onClose.append(self.__onClose) + + def __onClose(self): + self.session.nav.playService(self.old_service, forceRestart=True) + + def updateStateLabel(self, state): + self["SeekState"].setText(state[3].strip()) + def showTutorial(self): if not self.tutorial_seen: self.tutorial_seen = True - self.session.open(MessageBox, - """Welcome to the Cutlist editor. It's still a bit strange to use, but anyway: - -Seek to the start of the stuff you want to cut away. Press OK, select 'start cut'. + self.session.open(MessageBox,_("Welcome to the Cutlist editor.\n\nSeek to the start of the stuff you want to cut away. Press OK, select 'start cut'.\n\nThen seek to the end, press OK, select 'end cut'. That's it."), MessageBox.TYPE_INFO) -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): if len(self.cut_list): self.cut_list[index] = (self.cut_list[index][0], type) - self["Cutlist"].setIndex(index, CutListEntry(*self.cut_list[index])) - + self["cutlist"].modifyEntry(index, CutListEntry(*self.cut_list[index])) + def setIn(self): - m = self["Cutlist"].getCurrentIndex() + m = self["cutlist"].getIndex() self.setType(m, 0) self.uploadCuesheet() - + def setOut(self): - m = self["Cutlist"].getCurrentIndex() + m = self["cutlist"].getIndex() self.setType(m, 1) self.uploadCuesheet() def setMark(self): - m = self["Cutlist"].getCurrentIndex() + m = self["cutlist"].getIndex() 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 = 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): @@ -244,55 +247,57 @@ Then seek to the end, press OK, select 'end cut'. That's it. return r def selectionChanged(self): - where = self["Cutlist"].getCurrent() - if where is None: - print "no selection" - return - pts = where[0][0] - seek = self.getSeek() - if seek is None: - print "no seek" - return - seek.seekTo(pts) + if not self.inhibit_seek: + where = self["cutlist"].getCurrent() + if where is None: + print "no selection" + return + pts = where[0][0] + seek = self.getSeek() + if seek is None: + print "no seek" + return + seek.seekTo(pts) def refillList(self): print "cue sheet changed, refilling" self.downloadCuesheet() - - # get the first changed entry, and select it + + # get the first changed entry, counted from the end, 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) + self["cutlist"].list = new_list + + l1 = len(new_list) + l2 = len(self.last_cuts) + for i in range(min(l1, l2)): + if new_list[l1-i-1] != self.last_cuts[l2-i-1]: + self["cutlist"].setIndex(l1-i-1) break self.last_cuts = new_list 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 - + state = -1 for (where, what) in self.cut_list: - if where < pos: - if what == 0: # in - state = 0 - elif what == 1: # out + if what in [0, 1]: + if where < pos: + state = what + elif where == pos: state = 1 + elif state == -1: + state = 1 - what + if state == -1: + state = 0 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) @@ -305,28 +310,27 @@ Then seek to the end, press OK, select 'end cut'. That's it. 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): - self.setSeekState(self.SEEK_STATE_PLAY) 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]: + 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() @@ -334,42 +338,68 @@ Then seek to the end, press OK, select 'end cut'. That's it. 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 + 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: + 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.inhibit_seek = True self.uploadCuesheet() + self.inhibit_seek = False elif result == CutListContextMenu.RET_MARK: self.__addMark() elif result == CutListContextMenu.RET_DELETEMARK: self.cut_list.remove(self.context_nearest_mark) + self.inhibit_seek = True self.uploadCuesheet() + self.inhibit_seek = False 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]: + 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.inhibit_seek = True self.uploadCuesheet() + self.inhibit_seek = False 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]: + 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.inhibit_seek = True self.uploadCuesheet() + self.inhibit_seek = False + elif result == CutListContextMenu.RET_GRABFRAME: + self.grabFrame() + + # we modify the "play" behavior 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 grabFrame(self): + path = self.session.nav.getCurrentlyPlayingServiceReference().getPath() + from Components.Console import Console + grabConsole = Console() + cmd = 'grab -vblpr%d "%s"' % (180, path.rsplit('.',1)[0] + ".png") + grabConsole.ePopen(cmd) + self.playpauseService() def main(session, service, **kwargs): session.open(CutListEditor, service)