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.MultiContent import MultiContentEntryText
8 from Components.ServiceEventTracker import ServiceEventTracker, InfoBarBase
9 from Components.VideoWindow import VideoWindow
10 from Screens.InfoBarGenerics import InfoBarSeek, InfoBarCueSheetSupport
11 from Components.GUIComponent import GUIComponent
12 from enigma import eListboxPythonMultiContent, eListbox, gFont, iPlayableService, RT_HALIGN_RIGHT
13 from Screens.FixedMenu import FixedMenu
14 from Screens.HelpMenu import HelpableScreen
17 def CutListEntry(where, what):
18 res = [ (where, what) ]
32 res.append(MultiContentEntryText(size=(400, 20), text = "%dh:%02dm:%02ds:%03d" % (h, m, s, ms)))
33 res.append(MultiContentEntryText(pos=(400,0), size=(130, 20), text = type, flags = RT_HALIGN_RIGHT))
37 class CutListContextMenu(FixedMenu):
50 def __init__(self, session, state, nearmark):
51 menu = [(_("back"), self.close)] #, (None, )]
53 if state == self.SHOW_STARTCUT:
54 menu.append((_("start cut here"), self.startCut))
56 menu.append((_("start cut here"), ))
58 if state == self.SHOW_ENDCUT:
59 menu.append((_("end cut here"), self.endCut))
61 menu.append((_("end cut here"), ))
63 if state == self.SHOW_DELETECUT:
64 menu.append((_("delete cut"), self.deleteCut))
66 menu.append((_("delete cut"), ))
68 menu.append((_("remove before this position"), self.removeBefore))
69 menu.append((_("remove after this position"), self.removeAfter))
71 # menu.append((None, ))
74 menu.append((_("insert mark here"), self.insertMark))
76 menu.append((_("remove this mark"), self.removeMark))
78 FixedMenu.__init__(self, session, _("Cut"), menu)
79 self.skinName = "Menu"
82 self.close(self.RET_STARTCUT)
85 self.close(self.RET_ENDCUT)
88 self.close(self.RET_DELETECUT)
91 self.close(self.RET_MARK)
94 self.close(self.RET_DELETEMARK)
96 def removeBefore(self):
97 self.close(self.RET_REMOVEBEFORE)
99 def removeAfter(self):
100 self.close(self.RET_REMOVEAFTER)
103 class CutList(GUIComponent):
104 def __init__(self, list):
105 GUIComponent.__init__(self)
106 self.l = eListboxPythonMultiContent()
108 self.l.setFont(0, gFont("Regular", 20))
109 self.onSelectionChanged = [ ]
111 def getCurrent(self):
112 return self.l.getCurrentSelection()
114 def getCurrentIndex(self):
115 return self.l.getCurrentSelectionIndex()
117 GUI_WIDGET = eListbox
119 def postWidgetCreate(self, instance):
120 instance.setContent(self.l)
121 instance.setItemHeight(30)
122 instance.selectionChanged.get().append(self.selectionChanged)
124 def preWidgetRemove(self, instance):
125 instance.setContent(None)
126 instance.selectionChanged.get().remove(self.selectionChanged)
128 def selectionChanged(self):
129 for x in self.onSelectionChanged:
132 def invalidateEntry(self, index):
133 self.l.invalidateEntry(index)
135 def setIndex(self, index, data):
136 self.list[index] = data
137 self.invalidateEntry(index)
139 def setList(self, list):
141 self.l.setList(self.list)
143 def setSelection(self, index):
144 if self.instance is not None:
145 self.instance.moveSelectionTo(index)
147 class CutListEditor(Screen, InfoBarBase, InfoBarSeek, InfoBarCueSheetSupport, HelpableScreen):
149 <screen position="0,0" size="720,576" title="Cutlist editor" flags="wfNoBorder">
150 <eLabel text="Cutlist editor" position="65,60" size="300,25" font="Regular;20" />
151 <widget source="global.CurrentTime" render="Label" position="268,60" size="394,20" font="Regular;20" halign="right">
152 <convert type="ClockToText">Format:%A %B %d, %H:%M</convert>
154 <eLabel position="268,98" size="394,304" backgroundColor="#505555" />
155 <widget name="Video" position="270,100" zPosition="1" size="390,300" backgroundColor="transparent" />
156 <widget source="session.CurrentService" render="Label" position="135,405" size="450,50" font="Regular;22" halign="center" valign="center">
157 <convert type="ServiceName">Name</convert>
159 <widget source="session.CurrentService" render="Label" position="50,450" zPosition="1" size="620,25" font="Regular;20" halign="center" valign="center">
160 <convert type="ServicePosition">Position,Detailed</convert>
162 <eLabel position="62,98" size="179,274" backgroundColor="#505555" />
163 <eLabel position="64,100" size="175,270" backgroundColor="#000000" />
164 <widget name="Cutlist" position="64,100" zPosition="1" size="175,270" scrollbarMode="showOnDemand" transparent="1" />
165 <widget name="Timeline" position="50,485" size="615,20" backgroundColor="#505555" pointer="skin_default/position_arrow.png:3,5" foregroundColor="black" />
166 <ePixmap pixmap="skin_default/icons/mp_buttons.png" position="305,515" size="109,13" alphatest="on" />
169 def __init__(self, session, service):
170 self.skin = CutListEditor.skin
171 Screen.__init__(self, session)
172 InfoBarSeek.__init__(self, actionmap = "CutlistSeekActions")
173 InfoBarCueSheetSupport.__init__(self)
174 InfoBarBase.__init__(self, steal_current_service = True)
175 HelpableScreen.__init__(self)
176 self.old_service = session.nav.getCurrentlyPlayingServiceReference()
177 session.nav.playService(service)
179 service = session.nav.getCurrentService()
180 cue = service and service.cueSheet()
182 # disable cutlists. we want to freely browse around in the movie
183 print "cut lists disabled!"
184 cue.setCutListEnable(0)
186 self.downloadCuesheet()
188 self["Timeline"] = ServicePositionGauge(self.session.nav)
189 self["Cutlist"] = CutList(self.getCutlist())
190 self["Cutlist"].onSelectionChanged.append(self.selectionChanged)
192 self["Video"] = VideoWindow(decoder = 0)
194 self["actions"] = HelpableActionMap(self, "CutListEditorActions",
196 "setIn": (self.setIn, _("Make this mark an 'in' point")),
197 "setOut": (self.setOut, _("Make this mark an 'out' point")),
198 "setMark": (self.setMark, _("Make this mark just a mark")),
199 "addMark": (self.__addMark, _("Add a mark")),
200 "removeMark": (self.__removeMark, _("Remove a mark")),
201 "leave": (self.exit, _("Exit editor")),
202 "showMenu": (self.showMenu, _("menu")),
205 self.tutorial_seen = False
207 self.onExecBegin.append(self.showTutorial)
208 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
210 iPlayableService.evCuesheetChanged: self.refillList
213 # to track new entries we save the last version of the cutlist
215 self.cut_start = None
216 self.onClose.append(self.__onClose)
219 self.session.nav.playService(self.old_service)
221 def showTutorial(self):
222 if not self.tutorial_seen:
223 self.tutorial_seen = True
224 self.session.open(MessageBox,
225 """Welcome to the Cutlist editor.
227 Seek to the start of the stuff you want to cut away. Press OK, select 'start cut'.
229 Then seek to the end, press OK, select 'end cut'. That's it.
230 """, MessageBox.TYPE_INFO)
232 def checkSkipShowHideLock(self):
235 def setType(self, index, type):
236 if len(self.cut_list):
237 self.cut_list[index] = (self.cut_list[index][0], type)
238 self["Cutlist"].setIndex(index, CutListEntry(*self.cut_list[index]))
241 m = self["Cutlist"].getCurrentIndex()
243 self.uploadCuesheet()
246 m = self["Cutlist"].getCurrentIndex()
248 self.uploadCuesheet()
251 m = self["Cutlist"].getCurrentIndex()
253 self.uploadCuesheet()
256 self.toggleMark(onlyadd=True, tolerance=90000) # do not allow two marks in <1s
258 def __removeMark(self):
259 m = self["Cutlist"].getCurrent()
267 def getCutlist(self):
269 for e in self.cut_list:
270 r.append(CutListEntry(*e))
273 def selectionChanged(self):
274 where = self["Cutlist"].getCurrent()
279 seek = self.getSeek()
285 def refillList(self):
286 print "cue sheet changed, refilling"
287 self.downloadCuesheet()
289 # get the first changed entry, and select it
290 new_list = self.getCutlist()
291 self["Cutlist"].setList(new_list)
293 for i in range(min(len(new_list), len(self.last_cuts))):
294 if new_list[i] != self.last_cuts[i]:
295 self["Cutlist"].setSelection(i)
297 self.last_cuts = new_list
299 def getStateForPosition(self, pos):
302 # when first point is "in", the beginning is "out"
303 if len(self.cut_list) and self.cut_list[0][1] == 0:
306 for (where, what) in self.cut_list:
310 elif what == 1: # out
315 curpos = self.cueGetCurrentPosition()
319 self.setSeekState(self.SEEK_STATE_PAUSE)
321 self.context_position = curpos
323 self.context_nearest_mark = self.toggleMark(onlyreturn=True)
325 cur_state = self.getStateForPosition(curpos)
327 print "currently in 'IN'"
328 if self.cut_start is None or self.context_position < self.cut_start:
329 state = CutListContextMenu.SHOW_STARTCUT
331 state = CutListContextMenu.SHOW_ENDCUT
333 print "currently in 'OUT'"
334 state = CutListContextMenu.SHOW_DELETECUT
336 if self.context_nearest_mark is None:
341 self.session.openWithCallback(self.menuCallback, CutListContextMenu, state, nearmark)
343 def menuCallback(self, *result):
348 if result == CutListContextMenu.RET_STARTCUT:
349 self.cut_start = self.context_position
350 elif result == CutListContextMenu.RET_ENDCUT:
351 # remove in/out marks between the new cut
352 for (where, what) in self.cut_list[:]:
353 if self.cut_start <= where <= self.context_position and what in [0,1]:
354 self.cut_list.remove((where, what))
356 bisect.insort(self.cut_list, (self.cut_start, 1))
357 bisect.insort(self.cut_list, (self.context_position, 0))
358 self.uploadCuesheet()
359 self.cut_start = None
360 elif result == CutListContextMenu.RET_DELETECUT:
364 for (where, what) in self.cut_list:
365 if what == 1 and where < self.context_position: # out
366 out_before = (where, what)
367 elif what == 0 and where < self.context_position: # in, before out
369 elif what == 0 and where > self.context_position and in_after is None:
370 in_after = (where, what)
372 if out_before is not None:
373 self.cut_list.remove(out_before)
375 if in_after is not None:
376 self.cut_list.remove(in_after)
377 self.uploadCuesheet()
378 elif result == CutListContextMenu.RET_MARK:
380 elif result == CutListContextMenu.RET_DELETEMARK:
381 self.cut_list.remove(self.context_nearest_mark)
382 self.uploadCuesheet()
383 elif result == CutListContextMenu.RET_REMOVEBEFORE:
384 # remove in/out marks before current position
385 for (where, what) in self.cut_list[:]:
386 if where <= self.context_position and what in [0,1]:
387 self.cut_list.remove((where, what))
389 bisect.insort(self.cut_list, (self.context_position, 0))
390 self.uploadCuesheet()
391 elif result == CutListContextMenu.RET_REMOVEAFTER:
392 # remove in/out marks after current position
393 for (where, what) in self.cut_list[:]:
394 if where >= self.context_position and what in [0,1]:
395 self.cut_list.remove((where, what))
397 bisect.insort(self.cut_list, (self.context_position, 1))
398 self.uploadCuesheet()
400 # we modify the "play" behavior a bit:
401 # if we press pause while being in slowmotion, we will pause (and not play)
402 def playpauseService(self):
403 if self.seekstate != self.SEEK_STATE_PLAY and not self.isStateSlowMotion(self.seekstate):
404 self.unPauseService()
408 def main(session, service, **kwargs):
409 session.open(CutListEditor, service)
411 def Plugins(**kwargs):
412 return PluginDescriptor(name="Cutlist Editor", description=_("Cutlist editor..."), where = PluginDescriptor.WHERE_MOVIELIST, fnc=main)