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
9 from Components.ServiceEventTracker import ServiceEventTracker
10 from Components.VideoWindow import VideoWindow
11 from Screens.InfoBarGenerics import InfoBarSeek, InfoBarCueSheetSupport, InfoBarServiceName
12 from Components.GUIComponent import GUIComponent
13 from enigma import eListboxPythonMultiContent, eListbox, gFont, iPlayableService, RT_HALIGN_RIGHT
14 from Screens.FixedMenu import FixedMenu
15 from Screens.HelpMenu import HelpableScreen
18 def CutListEntry(where, what):
19 res = [ (where, what) ]
33 res.append(MultiContentEntryText(size=(400, 20), text = "%dh:%02dm:%02ds:%03d" % (h, m, s, ms)))
34 res.append(MultiContentEntryText(pos=(400,0), size=(130, 20), text = type, flags = RT_HALIGN_RIGHT))
38 class CutListContextMenu(FixedMenu):
51 def __init__(self, session, state, nearmark):
52 menu = [(_("back"), self.close)] #, (None, )]
54 if state == self.SHOW_STARTCUT:
55 menu.append((_("start cut here"), self.startCut))
57 menu.append((_("start cut here"), ))
59 if state == self.SHOW_ENDCUT:
60 menu.append((_("end cut here"), self.endCut))
62 menu.append((_("end cut here"), ))
64 if state == self.SHOW_DELETECUT:
65 menu.append((_("delete cut"), self.deleteCut))
67 menu.append((_("delete cut"), ))
69 menu.append((_("remove before this position"), self.removeBefore))
70 menu.append((_("remove after this position"), self.removeAfter))
72 # menu.append((None, ))
75 menu.append((_("insert mark here"), self.insertMark))
77 menu.append((_("remove this mark"), self.removeMark))
79 FixedMenu.__init__(self, session, _("Cut"), menu)
80 self.skinName = "Menu"
83 self.close(self.RET_STARTCUT)
86 self.close(self.RET_ENDCUT)
89 self.close(self.RET_DELETECUT)
92 self.close(self.RET_MARK)
95 self.close(self.RET_DELETEMARK)
97 def removeBefore(self):
98 self.close(self.RET_REMOVEBEFORE)
100 def removeAfter(self):
101 self.close(self.RET_REMOVEAFTER)
104 class CutList(GUIComponent):
105 def __init__(self, list):
106 GUIComponent.__init__(self)
107 self.l = eListboxPythonMultiContent()
109 self.l.setFont(0, gFont("Regular", 20))
110 self.onSelectionChanged = [ ]
112 def getCurrent(self):
113 return self.l.getCurrentSelection()
115 def getCurrentIndex(self):
116 return self.l.getCurrentSelectionIndex()
118 GUI_WIDGET = eListbox
120 def postWidgetCreate(self, instance):
121 instance.setContent(self.l)
122 instance.setItemHeight(30)
123 instance.selectionChanged.get().append(self.selectionChanged)
125 def selectionChanged(self):
126 for x in self.onSelectionChanged:
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, InfoBarServiceName, HelpableScreen):
146 <screen position="0,0" size="720,576" flags="wfNoBorder" backgroundColor="#444444">
147 <eLabel position="360,0" size="360,313" backgroundColor="#ffffff" />
148 <widget name="Video" position="370,10" size="340,268" backgroundColor="transparent" zPosition="1" />
150 <eLabel position="50,80" size="300,24" text="Name:" font="Regular;20" foregroundColor="#cccccc" transparent="1" />
152 <widget source="CurrentService" render="Label" position="50,110" size="300,60" font="Regular;22" >
153 <convert type="ServiceName">Name</convert>
156 <widget source="CurrentService" render="Label" position="370,278" size="340,25"
157 backgroundColor="#000000" foregroundColor="#ffffff" font="Regular;19" zPosition="1" >
158 <convert type="ServicePosition">PositionDetailed</convert>
161 <widget name="Timeline" position="50,500" size="620,40" backgroundColor="#000000"
162 pointer="/usr/share/enigma2/position_pointer.png:3,5" foregroundColor="#ffffff" />
163 <widget name="Cutlist" position="50,325" size="620,175" scrollbarMode="showOnDemand" transparent="1" />
165 def __init__(self, session, service):
166 self.skin = CutListEditor.skin
167 Screen.__init__(self, session)
168 InfoBarSeek.__init__(self, actionmap = "CutlistSeekActions")
169 InfoBarCueSheetSupport.__init__(self)
170 InfoBarServiceName.__init__(self)
171 HelpableScreen.__init__(self)
172 self.old_service = session.nav.getCurrentlyPlayingServiceReference()
173 session.nav.playService(service)
175 service = session.nav.getCurrentService()
176 cue = service and service.cueSheet()
178 # disable cutlists. we want to freely browse around in the movie
179 print "cut lists disabled!"
180 cue.setCutListEnable(0)
182 self.downloadCuesheet()
184 self["Timeline"] = ServicePositionGauge(self.session.nav)
185 self["Cutlist"] = CutList(self.getCutlist())
186 self["Cutlist"].onSelectionChanged.append(self.selectionChanged)
188 self["Video"] = VideoWindow(decoder = 0)
190 self["actions"] = HelpableActionMap(self, "CutListEditorActions",
192 "setIn": (self.setIn, _("Make this mark an 'in' point")),
193 "setOut": (self.setOut, _("Make this mark an 'out' point")),
194 "setMark": (self.setMark, _("Make this mark just a mark")),
195 "addMark": (self.__addMark, _("Add a mark")),
196 "removeMark": (self.__removeMark, _("Remove a mark")),
197 "leave": (self.exit, _("Exit editor")),
198 "showMenu": (self.showMenu, _("menu")),
201 self.tutorial_seen = False
203 self.onExecBegin.append(self.showTutorial)
204 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
206 iPlayableService.evCuesheetChanged: self.refillList
209 # to track new entries we save the last version of the cutlist
211 self.cut_start = None
213 def showTutorial(self):
214 if not self.tutorial_seen:
215 self.tutorial_seen = True
216 self.session.open(MessageBox,
217 """Welcome to the Cutlist editor.
219 Seek to the start of the stuff you want to cut away. Press OK, select 'start cut'.
221 Then seek to the end, press OK, select 'end cut'. That's it.
222 """, MessageBox.TYPE_INFO)
224 def checkSkipShowHideLock(self):
227 def setType(self, index, type):
228 if len(self.cut_list):
229 self.cut_list[index] = (self.cut_list[index][0], type)
230 self["Cutlist"].setIndex(index, CutListEntry(*self.cut_list[index]))
233 m = self["Cutlist"].getCurrentIndex()
235 self.uploadCuesheet()
238 m = self["Cutlist"].getCurrentIndex()
240 self.uploadCuesheet()
243 m = self["Cutlist"].getCurrentIndex()
245 self.uploadCuesheet()
248 self.toggleMark(onlyadd=True, tolerance=90000) # do not allow two marks in <1s
250 def __removeMark(self):
251 m = self["Cutlist"].getCurrent()
257 self.session.nav.playService(self.old_service)
260 def getCutlist(self):
262 for e in self.cut_list:
263 r.append(CutListEntry(*e))
266 def selectionChanged(self):
267 where = self["Cutlist"].getCurrent()
272 seek = self.getSeek()
278 def refillList(self):
279 print "cue sheet changed, refilling"
280 self.downloadCuesheet()
282 # get the first changed entry, and select it
283 new_list = self.getCutlist()
284 self["Cutlist"].setList(new_list)
286 for i in range(min(len(new_list), len(self.last_cuts))):
287 if new_list[i] != self.last_cuts[i]:
288 self["Cutlist"].setSelection(i)
290 self.last_cuts = new_list
292 def getStateForPosition(self, pos):
295 # when first point is "in", the beginning is "out"
296 if len(self.cut_list) and self.cut_list[0][1] == 0:
299 for (where, what) in self.cut_list:
303 elif what == 1: # out
308 curpos = self.cueGetCurrentPosition()
312 self.setSeekState(self.SEEK_STATE_PAUSE)
314 self.context_position = curpos
316 self.context_nearest_mark = self.toggleMark(onlyreturn=True)
318 cur_state = self.getStateForPosition(curpos)
320 print "currently in 'IN'"
321 if self.cut_start is None or self.context_position < self.cut_start:
322 state = CutListContextMenu.SHOW_STARTCUT
324 state = CutListContextMenu.SHOW_ENDCUT
326 print "currently in 'OUT'"
327 state = CutListContextMenu.SHOW_DELETECUT
329 if self.context_nearest_mark is None:
334 self.session.openWithCallback(self.menuCallback, CutListContextMenu, state, nearmark)
336 def menuCallback(self, *result):
341 if result == CutListContextMenu.RET_STARTCUT:
342 self.cut_start = self.context_position
343 elif result == CutListContextMenu.RET_ENDCUT:
344 # remove in/out marks between the new cut
345 for (where, what) in self.cut_list[:]:
346 if self.cut_start <= where <= self.context_position and what in [0,1]:
347 self.cut_list.remove((where, what))
349 bisect.insort(self.cut_list, (self.cut_start, 1))
350 bisect.insort(self.cut_list, (self.context_position, 0))
351 self.uploadCuesheet()
352 self.cut_start = None
353 elif result == CutListContextMenu.RET_DELETECUT:
357 for (where, what) in self.cut_list:
358 if what == 1 and where < self.context_position: # out
359 out_before = (where, what)
360 elif what == 0 and where < self.context_position: # in, before out
362 elif what == 0 and where > self.context_position and in_after is None:
363 in_after = (where, what)
365 if out_before is not None:
366 self.cut_list.remove(out_before)
368 if in_after is not None:
369 self.cut_list.remove(in_after)
370 self.uploadCuesheet()
371 elif result == CutListContextMenu.RET_MARK:
373 elif result == CutListContextMenu.RET_DELETEMARK:
374 self.cut_list.remove(self.context_nearest_mark)
375 self.uploadCuesheet()
376 elif result == CutListContextMenu.RET_REMOVEBEFORE:
377 # remove in/out marks before current position
378 for (where, what) in self.cut_list[:]:
379 if where <= self.context_position and what in [0,1]:
380 self.cut_list.remove((where, what))
382 bisect.insort(self.cut_list, (self.context_position, 0))
383 self.uploadCuesheet()
384 elif result == CutListContextMenu.RET_REMOVEAFTER:
385 # remove in/out marks after current position
386 for (where, what) in self.cut_list[:]:
387 if where >= self.context_position and what in [0,1]:
388 self.cut_list.remove((where, what))
390 bisect.insort(self.cut_list, (self.context_position, 1))
391 self.uploadCuesheet()
393 # we modify the "play" behaviour a bit:
394 # if we press pause while being in slowmotion, we will pause (and not play)
395 def playpauseService(self):
396 if self.seekstate not in [self.SEEK_STATE_PLAY, self.SEEK_STATE_SM_HALF, self.SEEK_STATE_SM_QUARTER, self.SEEK_STATE_SM_EIGHTH]:
397 self.unPauseService()
401 def main(session, service, **kwargs):
402 session.open(CutListEditor, service)
404 def Plugins(**kwargs):
405 return PluginDescriptor(name="Cutlist Editor", description=_("Cutlist editor..."), where = PluginDescriptor.WHERE_MOVIELIST, fnc=main)