1 from Plugins.Plugin import PluginDescriptor
3 from Screens.Screen import Screen
4 from Screens.MessageBox import MessageBox
5 from Components.ServicePosition import ServicePositionGauge
6 from Components.ActionMap import HelpableActionMap
7 from Components.MenuList import MenuList
8 from Components.MultiContent import MultiContentEntryText, RT_HALIGN_RIGHT
9 from Components.ServiceEventTracker import ServiceEventTracker
10 from Screens.InfoBarGenerics import InfoBarSeek, InfoBarCueSheetSupport
11 from Components.GUIComponent import GUIComponent
12 from enigma import eListboxPythonMultiContent, eListbox, gFont, iPlayableService
13 from Screens.FixedMenu import FixedMenu
16 def CutListEntry(where, what):
17 res = [ (where, what) ]
29 res.append(MultiContentEntryText(size=(400, 20), text = "%dh:%02dm:%02ds:%03d" % (h, m, s, ms)))
30 res.append(MultiContentEntryText(pos=(400,0), size=(130, 20), text = type, flags = RT_HALIGN_RIGHT))
34 class CutListContextMenu(FixedMenu):
47 def __init__(self, session, state, nearmark):
48 menu = [(_("back"), self.close)] #, (None, )]
50 if state == self.SHOW_STARTCUT:
51 menu.append((_("start cut here"), self.startCut))
53 menu.append((_("start cut here"), ))
55 if state == self.SHOW_ENDCUT:
56 menu.append((_("end cut here"), self.endCut))
58 menu.append((_("end cut here"), ))
60 if state == self.SHOW_DELETECUT:
61 menu.append((_("delete cut"), self.deleteCut))
63 menu.append((_("delete cut"), ))
65 menu.append((_("remove before this position"), self.removeBefore))
66 menu.append((_("remove after this position"), self.removeAfter))
68 # menu.append((None, ))
71 menu.append((_("insert mark here"), self.insertMark))
73 menu.append((_("remove this mark"), self.removeMark))
75 FixedMenu.__init__(self, session, _("Cut"), menu)
76 self.skinName = "Menu"
79 self.close(self.RET_STARTCUT)
82 self.close(self.RET_ENDCUT)
85 self.close(self.RET_DELETECUT)
88 self.close(self.RET_MARK)
91 self.close(self.RET_DELETEMARK)
93 def removeBefore(self):
94 self.close(self.RET_REMOVEBEFORE)
96 def removeAfter(self):
97 self.close(self.RET_REMOVEAFTER)
100 class CutList(GUIComponent):
101 def __init__(self, list):
102 GUIComponent.__init__(self)
103 self.l = eListboxPythonMultiContent()
105 self.l.setFont(0, gFont("Regular", 20))
106 self.onSelectionChanged = [ ]
108 def getCurrent(self):
109 return self.l.getCurrentSelection()
111 def getCurrentIndex(self):
112 return self.l.getCurrentSelectionIndex()
114 def GUIcreate(self, parent):
115 self.instance = eListbox(parent)
116 self.instance.setContent(self.l)
117 self.instance.setItemHeight(30)
118 self.instance.selectionChanged.get().append(self.selectionChanged)
120 def selectionChanged(self):
121 for x in self.onSelectionChanged:
125 self.instance.selectionChanged.get().remove(self.selectionChanged)
126 self.instance.setContent(None)
129 def invalidateEntry(self, index):
130 self.l.invalidateEntry(index)
132 def setIndex(self, index, data):
133 self.list[index] = data
134 self.invalidateEntry(index)
136 def setList(self, list):
138 self.l.setList(self.list)
140 def setSelection(self, index):
141 if self.instance is not None:
142 self.instance.moveSelectionTo(index)
144 class CutListEditor(Screen, InfoBarSeek, InfoBarCueSheetSupport):
146 <screen position="100,100" size="550,400" title="Test" >
147 <widget name="Timeline" position="10,0" size="530,40"
148 pointer="/usr/share/enigma2/position_pointer.png:3,5" />
149 <widget name="Cutlist" position="10,50" size="530,300" scrollbarMode="showOnDemand" />
151 def __init__(self, session, service):
152 self.skin = CutListEditor.skin
153 Screen.__init__(self, session)
154 InfoBarSeek.__init__(self)
155 InfoBarCueSheetSupport.__init__(self)
156 self.old_service = session.nav.getCurrentlyPlayingServiceReference()
157 session.nav.playService(service)
159 service = session.nav.getCurrentService()
160 cue = service and service.cueSheet()
162 # disable cutlists. we want to freely browse around in the movie
163 print "cut lists disabled!"
164 cue.setCutListEnable(0)
166 self.downloadCuesheet()
168 self["Timeline"] = ServicePositionGauge(self.session.nav)
169 self["Cutlist"] = CutList(self.getCutlist())
170 self["Cutlist"].onSelectionChanged.append(self.selectionChanged)
172 self["actions"] = HelpableActionMap(self, "CutListEditorActions",
174 "setIn": (self.setIn, _("Make this mark an 'in' point")),
175 "setOut": (self.setOut, _("Make this mark an 'out' point")),
176 "setMark": (self.setMark, _("Make this mark just a mark")),
177 "addMark": (self.__addMark, _("Add a mark")),
178 "removeMark": (self.__removeMark, _("Remove a mark")),
179 "leave": (self.exit, _("Exit editor")),
180 "showMenu": self.showMenu,
183 self.tutorial_seen = False
185 self.onExecBegin.append(self.showTutorial)
186 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
188 iPlayableService.evCuesheetChanged: self.refillList
191 # to track new entries we save the last version of the cutlist
193 self.cut_start = None
195 def showTutorial(self):
196 if not self.tutorial_seen:
197 self.tutorial_seen = True
198 self.session.open(MessageBox,
199 """Welcome to the Cutlist editor. It's still a bit strange to use, but anyway:
201 Seek to the start of the stuff you want to cut away. Press OK, select 'start cut'.
203 Then seek to the end, press OK, select 'end cut'. That's it.
204 """, MessageBox.TYPE_INFO)
206 def checkSkipShowHideLock(self):
209 def setType(self, index, type):
210 self.cut_list[index] = (self.cut_list[index][0], type)
211 self["Cutlist"].setIndex(index, CutListEntry(*self.cut_list[index]))
214 m = self["Cutlist"].getCurrentIndex()
216 self.uploadCuesheet()
219 m = self["Cutlist"].getCurrentIndex()
221 self.uploadCuesheet()
224 m = self["Cutlist"].getCurrentIndex()
226 self.uploadCuesheet()
229 self.toggleMark(onlyadd=True, tolerance=90000) # do not allow two marks in <1s
231 def __removeMark(self):
232 m = self["Cutlist"].getCurrent()
238 self.session.nav.playService(self.old_service)
241 def getCutlist(self):
243 for e in self.cut_list:
244 r.append(CutListEntry(*e))
247 def selectionChanged(self):
248 where = self["Cutlist"].getCurrent()
253 seek = self.getSeek()
259 def refillList(self):
260 print "cue sheet changed, refilling"
261 self.downloadCuesheet()
263 # get the first changed entry, and select it
264 new_list = self.getCutlist()
265 self["Cutlist"].setList(new_list)
267 for i in range(min(len(new_list), len(self.last_cuts))):
268 if new_list[i] != self.last_cuts[i]:
269 self["Cutlist"].setSelection(i)
271 self.last_cuts = new_list
273 def getStateForPosition(self, pos):
276 # when first point is "in", the beginning is "out"
277 if len(self.cut_list) and self.cut_list[0][1] == 0:
280 for (where, what) in self.cut_list:
284 elif what == 1: # out
289 curpos = self.cueGetCurrentPosition()
293 self.setSeekState(self.SEEK_STATE_PAUSE)
295 self.context_position = curpos
297 self.context_nearest_mark = self.toggleMark(onlyreturn=True)
299 cur_state = self.getStateForPosition(curpos)
301 print "currently in 'IN'"
302 if self.cut_start is None or self.context_position < self.cut_start:
303 state = CutListContextMenu.SHOW_STARTCUT
305 state = CutListContextMenu.SHOW_ENDCUT
307 print "currently in 'OUT'"
308 state = CutListContextMenu.SHOW_DELETECUT
310 if self.context_nearest_mark is None:
315 self.session.openWithCallback(self.menuCallback, CutListContextMenu, state, nearmark)
317 def menuCallback(self, *result):
318 self.setSeekState(self.SEEK_STATE_PLAY)
323 if result == CutListContextMenu.RET_STARTCUT:
324 self.cut_start = self.context_position
325 elif result == CutListContextMenu.RET_ENDCUT:
326 # remove in/out marks between the new cut
327 for (where, what) in self.cut_list[:]:
328 if self.cut_start <= where <= self.context_position and what in [0,1]:
329 self.cut_list.remove((where, what))
331 bisect.insort(self.cut_list, (self.cut_start, 1))
332 bisect.insort(self.cut_list, (self.context_position, 0))
333 self.uploadCuesheet()
334 self.cut_start = None
335 elif result == CutListContextMenu.RET_DELETECUT:
339 for (where, what) in self.cut_list:
340 if what == 1 and where < self.context_position: # out
341 out_before = (where, what)
342 elif what == 0 and where < self.context_position: # in, before out
344 elif what == 0 and where > self.context_position and in_after is None:
345 in_after = (where, what)
347 if out_before is not None:
348 self.cut_list.remove(out_before)
350 if in_after is not None:
351 self.cut_list.remove(in_after)
352 self.uploadCuesheet()
353 elif result == CutListContextMenu.RET_MARK:
355 elif result == CutListContextMenu.RET_DELETEMARK:
356 self.cut_list.remove(self.context_nearest_mark)
357 self.uploadCuesheet()
358 elif result == CutListContextMenu.RET_REMOVEBEFORE:
359 # remove in/out marks before current position
360 for (where, what) in self.cut_list[:]:
361 if where <= self.context_position and what in [0,1]:
362 self.cut_list.remove((where, what))
364 bisect.insort(self.cut_list, (self.context_position, 0))
365 self.uploadCuesheet()
366 elif result == CutListContextMenu.RET_REMOVEAFTER:
367 # remove in/out marks after current position
368 for (where, what) in self.cut_list[:]:
369 if where >= self.context_position and what in [0,1]:
370 self.cut_list.remove((where, what))
372 bisect.insort(self.cut_list, (self.context_position, 1))
373 self.uploadCuesheet()
375 def main(session, service, **kwargs):
376 session.open(CutListEditor, service)
378 def Plugins(**kwargs):
379 return PluginDescriptor(name="Cutlist Editor", description=_("Cutlist editor..."), where = PluginDescriptor.WHERE_MOVIELIST, fnc=main)