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
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, 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">Position,Detailed</convert>
164 <widget name="Timeline" position="50,500" size="620,40" backgroundColor="#000000"
165 pointer="/usr/share/enigma2/skin_default/position_arrow.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 HelpableScreen.__init__(self)
174 self.old_service = session.nav.getCurrentlyPlayingServiceReference()
175 session.nav.playService(service)
177 service = session.nav.getCurrentService()
178 cue = service and service.cueSheet()
180 # disable cutlists. we want to freely browse around in the movie
181 print "cut lists disabled!"
182 cue.setCutListEnable(0)
184 self.downloadCuesheet()
186 self["Timeline"] = ServicePositionGauge(self.session.nav)
187 self["Cutlist"] = CutList(self.getCutlist())
188 self["Cutlist"].onSelectionChanged.append(self.selectionChanged)
190 self["Video"] = VideoWindow(decoder = 0)
192 self["actions"] = HelpableActionMap(self, "CutListEditorActions",
194 "setIn": (self.setIn, _("Make this mark an 'in' point")),
195 "setOut": (self.setOut, _("Make this mark an 'out' point")),
196 "setMark": (self.setMark, _("Make this mark just a mark")),
197 "addMark": (self.__addMark, _("Add a mark")),
198 "removeMark": (self.__removeMark, _("Remove a mark")),
199 "leave": (self.exit, _("Exit editor")),
200 "showMenu": (self.showMenu, _("menu")),
203 self.tutorial_seen = False
205 self.onExecBegin.append(self.showTutorial)
206 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
208 iPlayableService.evCuesheetChanged: self.refillList
211 # to track new entries we save the last version of the cutlist
213 self.cut_start = None
215 def showTutorial(self):
216 if not self.tutorial_seen:
217 self.tutorial_seen = True
218 self.session.open(MessageBox,
219 """Welcome to the Cutlist editor.
221 Seek to the start of the stuff you want to cut away. Press OK, select 'start cut'.
223 Then seek to the end, press OK, select 'end cut'. That's it.
224 """, MessageBox.TYPE_INFO)
226 def checkSkipShowHideLock(self):
229 def setType(self, index, type):
230 if len(self.cut_list):
231 self.cut_list[index] = (self.cut_list[index][0], type)
232 self["Cutlist"].setIndex(index, CutListEntry(*self.cut_list[index]))
235 m = self["Cutlist"].getCurrentIndex()
237 self.uploadCuesheet()
240 m = self["Cutlist"].getCurrentIndex()
242 self.uploadCuesheet()
245 m = self["Cutlist"].getCurrentIndex()
247 self.uploadCuesheet()
250 self.toggleMark(onlyadd=True, tolerance=90000) # do not allow two marks in <1s
252 def __removeMark(self):
253 m = self["Cutlist"].getCurrent()
259 self.session.nav.playService(self.old_service)
262 def getCutlist(self):
264 for e in self.cut_list:
265 r.append(CutListEntry(*e))
268 def selectionChanged(self):
269 where = self["Cutlist"].getCurrent()
274 seek = self.getSeek()
280 def refillList(self):
281 print "cue sheet changed, refilling"
282 self.downloadCuesheet()
284 # get the first changed entry, and select it
285 new_list = self.getCutlist()
286 self["Cutlist"].setList(new_list)
288 for i in range(min(len(new_list), len(self.last_cuts))):
289 if new_list[i] != self.last_cuts[i]:
290 self["Cutlist"].setSelection(i)
292 self.last_cuts = new_list
294 def getStateForPosition(self, pos):
297 # when first point is "in", the beginning is "out"
298 if len(self.cut_list) and self.cut_list[0][1] == 0:
301 for (where, what) in self.cut_list:
305 elif what == 1: # out
310 curpos = self.cueGetCurrentPosition()
314 self.setSeekState(self.SEEK_STATE_PAUSE)
316 self.context_position = curpos
318 self.context_nearest_mark = self.toggleMark(onlyreturn=True)
320 cur_state = self.getStateForPosition(curpos)
322 print "currently in 'IN'"
323 if self.cut_start is None or self.context_position < self.cut_start:
324 state = CutListContextMenu.SHOW_STARTCUT
326 state = CutListContextMenu.SHOW_ENDCUT
328 print "currently in 'OUT'"
329 state = CutListContextMenu.SHOW_DELETECUT
331 if self.context_nearest_mark is None:
336 self.session.openWithCallback(self.menuCallback, CutListContextMenu, state, nearmark)
338 def menuCallback(self, *result):
343 if result == CutListContextMenu.RET_STARTCUT:
344 self.cut_start = self.context_position
345 elif result == CutListContextMenu.RET_ENDCUT:
346 # remove in/out marks between the new cut
347 for (where, what) in self.cut_list[:]:
348 if self.cut_start <= where <= self.context_position and what in [0,1]:
349 self.cut_list.remove((where, what))
351 bisect.insort(self.cut_list, (self.cut_start, 1))
352 bisect.insort(self.cut_list, (self.context_position, 0))
353 self.uploadCuesheet()
354 self.cut_start = None
355 elif result == CutListContextMenu.RET_DELETECUT:
359 for (where, what) in self.cut_list:
360 if what == 1 and where < self.context_position: # out
361 out_before = (where, what)
362 elif what == 0 and where < self.context_position: # in, before out
364 elif what == 0 and where > self.context_position and in_after is None:
365 in_after = (where, what)
367 if out_before is not None:
368 self.cut_list.remove(out_before)
370 if in_after is not None:
371 self.cut_list.remove(in_after)
372 self.uploadCuesheet()
373 elif result == CutListContextMenu.RET_MARK:
375 elif result == CutListContextMenu.RET_DELETEMARK:
376 self.cut_list.remove(self.context_nearest_mark)
377 self.uploadCuesheet()
378 elif result == CutListContextMenu.RET_REMOVEBEFORE:
379 # remove in/out marks before current position
380 for (where, what) in self.cut_list[:]:
381 if where <= self.context_position and what in [0,1]:
382 self.cut_list.remove((where, what))
384 bisect.insort(self.cut_list, (self.context_position, 0))
385 self.uploadCuesheet()
386 elif result == CutListContextMenu.RET_REMOVEAFTER:
387 # remove in/out marks after current position
388 for (where, what) in self.cut_list[:]:
389 if where >= self.context_position and what in [0,1]:
390 self.cut_list.remove((where, what))
392 bisect.insort(self.cut_list, (self.context_position, 1))
393 self.uploadCuesheet()
395 # we modify the "play" behavior a bit:
396 # if we press pause while being in slowmotion, we will pause (and not play)
397 def playpauseService(self):
398 if self.seekstate != self.SEEK_STATE_PLAY and not self.isStateSlowMotion(self.seekstate):
399 self.unPauseService()
403 def main(session, service, **kwargs):
404 session.open(CutListEditor, service)
406 def Plugins(**kwargs):
407 return PluginDescriptor(name="Cutlist Editor", description=_("Cutlist editor..."), where = PluginDescriptor.WHERE_MOVIELIST, fnc=main)