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
9 from Components.VideoWindow import VideoWindow
10 from Screens.InfoBarGenerics import InfoBarSeek, InfoBarCueSheetSupport, InfoBarServiceName
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, InfoBarSeek, InfoBarCueSheetSupport, InfoBarServiceName, HelpableScreen):
149 <screen position="0,0" size="720,576" flags="wfNoBorder" backgroundColor="#444444">
150 <eLabel position="360,0" size="360,313" backgroundColor="#ffffff" />
151 <widget name="Video" position="370,10" size="340,268" backgroundColor="transparent" zPosition="1" />
153 <eLabel position="50,80" size="300,24" text="Name:" font="Regular;20" foregroundColor="#cccccc" transparent="1" />
155 <widget source="CurrentService" render="Label" position="50,110" size="300,60" font="Regular;22" >
156 <convert type="ServiceName">Name</convert>
159 <widget source="CurrentService" render="Label" position="370,278" size="340,25"
160 backgroundColor="#000000" foregroundColor="#ffffff" font="Regular;19" zPosition="1" >
161 <convert type="ServicePosition">PositionDetailed</convert>
164 <widget name="Timeline" position="50,500" size="620,40" backgroundColor="#000000"
165 pointer="/usr/share/enigma2/position_pointer.png:3,5" foregroundColor="#ffffff" />
166 <widget name="Cutlist" position="50,325" size="620,175" scrollbarMode="showOnDemand" transparent="1" />
168 def __init__(self, session, service):
169 self.skin = CutListEditor.skin
170 Screen.__init__(self, session)
171 InfoBarSeek.__init__(self, actionmap = "CutlistSeekActions")
172 InfoBarCueSheetSupport.__init__(self)
173 InfoBarServiceName.__init__(self)
174 HelpableScreen.__init__(self)
175 self.old_service = session.nav.getCurrentlyPlayingServiceReference()
176 session.nav.playService(service)
178 service = session.nav.getCurrentService()
179 cue = service and service.cueSheet()
181 # disable cutlists. we want to freely browse around in the movie
182 print "cut lists disabled!"
183 cue.setCutListEnable(0)
185 self.downloadCuesheet()
187 self["Timeline"] = ServicePositionGauge(self.session.nav)
188 self["Cutlist"] = CutList(self.getCutlist())
189 self["Cutlist"].onSelectionChanged.append(self.selectionChanged)
191 self["Video"] = VideoWindow(decoder = 0)
193 self["actions"] = HelpableActionMap(self, "CutListEditorActions",
195 "setIn": (self.setIn, _("Make this mark an 'in' point")),
196 "setOut": (self.setOut, _("Make this mark an 'out' point")),
197 "setMark": (self.setMark, _("Make this mark just a mark")),
198 "addMark": (self.__addMark, _("Add a mark")),
199 "removeMark": (self.__removeMark, _("Remove a mark")),
200 "leave": (self.exit, _("Exit editor")),
201 "showMenu": (self.showMenu, _("menu")),
204 self.tutorial_seen = False
206 self.onExecBegin.append(self.showTutorial)
207 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
209 iPlayableService.evCuesheetChanged: self.refillList
212 # to track new entries we save the last version of the cutlist
214 self.cut_start = None
216 def showTutorial(self):
217 if not self.tutorial_seen:
218 self.tutorial_seen = True
219 self.session.open(MessageBox,
220 """Welcome to the Cutlist editor.
222 Seek to the start of the stuff you want to cut away. Press OK, select 'start cut'.
224 Then seek to the end, press OK, select 'end cut'. That's it.
225 """, MessageBox.TYPE_INFO)
227 def checkSkipShowHideLock(self):
230 def setType(self, index, type):
231 if len(self.cut_list):
232 self.cut_list[index] = (self.cut_list[index][0], type)
233 self["Cutlist"].setIndex(index, CutListEntry(*self.cut_list[index]))
236 m = self["Cutlist"].getCurrentIndex()
238 self.uploadCuesheet()
241 m = self["Cutlist"].getCurrentIndex()
243 self.uploadCuesheet()
246 m = self["Cutlist"].getCurrentIndex()
248 self.uploadCuesheet()
251 self.toggleMark(onlyadd=True, tolerance=90000) # do not allow two marks in <1s
253 def __removeMark(self):
254 m = self["Cutlist"].getCurrent()
260 self.session.nav.playService(self.old_service)
263 def getCutlist(self):
265 for e in self.cut_list:
266 r.append(CutListEntry(*e))
269 def selectionChanged(self):
270 where = self["Cutlist"].getCurrent()
275 seek = self.getSeek()
281 def refillList(self):
282 print "cue sheet changed, refilling"
283 self.downloadCuesheet()
285 # get the first changed entry, and select it
286 new_list = self.getCutlist()
287 self["Cutlist"].setList(new_list)
289 for i in range(min(len(new_list), len(self.last_cuts))):
290 if new_list[i] != self.last_cuts[i]:
291 self["Cutlist"].setSelection(i)
293 self.last_cuts = new_list
295 def getStateForPosition(self, pos):
298 # when first point is "in", the beginning is "out"
299 if len(self.cut_list) and self.cut_list[0][1] == 0:
302 for (where, what) in self.cut_list:
306 elif what == 1: # out
311 curpos = self.cueGetCurrentPosition()
315 self.setSeekState(self.SEEK_STATE_PAUSE)
317 self.context_position = curpos
319 self.context_nearest_mark = self.toggleMark(onlyreturn=True)
321 cur_state = self.getStateForPosition(curpos)
323 print "currently in 'IN'"
324 if self.cut_start is None or self.context_position < self.cut_start:
325 state = CutListContextMenu.SHOW_STARTCUT
327 state = CutListContextMenu.SHOW_ENDCUT
329 print "currently in 'OUT'"
330 state = CutListContextMenu.SHOW_DELETECUT
332 if self.context_nearest_mark is None:
337 self.session.openWithCallback(self.menuCallback, CutListContextMenu, state, nearmark)
339 def menuCallback(self, *result):
344 if result == CutListContextMenu.RET_STARTCUT:
345 self.cut_start = self.context_position
346 elif result == CutListContextMenu.RET_ENDCUT:
347 # remove in/out marks between the new cut
348 for (where, what) in self.cut_list[:]:
349 if self.cut_start <= where <= self.context_position and what in [0,1]:
350 self.cut_list.remove((where, what))
352 bisect.insort(self.cut_list, (self.cut_start, 1))
353 bisect.insort(self.cut_list, (self.context_position, 0))
354 self.uploadCuesheet()
355 self.cut_start = None
356 elif result == CutListContextMenu.RET_DELETECUT:
360 for (where, what) in self.cut_list:
361 if what == 1 and where < self.context_position: # out
362 out_before = (where, what)
363 elif what == 0 and where < self.context_position: # in, before out
365 elif what == 0 and where > self.context_position and in_after is None:
366 in_after = (where, what)
368 if out_before is not None:
369 self.cut_list.remove(out_before)
371 if in_after is not None:
372 self.cut_list.remove(in_after)
373 self.uploadCuesheet()
374 elif result == CutListContextMenu.RET_MARK:
376 elif result == CutListContextMenu.RET_DELETEMARK:
377 self.cut_list.remove(self.context_nearest_mark)
378 self.uploadCuesheet()
379 elif result == CutListContextMenu.RET_REMOVEBEFORE:
380 # remove in/out marks before current position
381 for (where, what) in self.cut_list[:]:
382 if where <= self.context_position and what in [0,1]:
383 self.cut_list.remove((where, what))
385 bisect.insort(self.cut_list, (self.context_position, 0))
386 self.uploadCuesheet()
387 elif result == CutListContextMenu.RET_REMOVEAFTER:
388 # remove in/out marks after current position
389 for (where, what) in self.cut_list[:]:
390 if where >= self.context_position and what in [0,1]:
391 self.cut_list.remove((where, what))
393 bisect.insort(self.cut_list, (self.context_position, 1))
394 self.uploadCuesheet()
396 # we modify the "play" behaviour a bit:
397 # if we press pause while being in slowmotion, we will pause (and not play)
398 def playpauseService(self):
399 if self.seekstate != self.SEEK_STATE_PLAY and not self.isStateSlowMotion(self.seekstate):
400 self.unPauseService()
404 def main(session, service, **kwargs):
405 session.open(CutListEditor, service)
407 def Plugins(**kwargs):
408 return PluginDescriptor(name="Cutlist Editor", description=_("Cutlist editor..."), where = PluginDescriptor.WHERE_MOVIELIST, fnc=main)