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 selectionChanged(self):
125 for x in self.onSelectionChanged:
128 def invalidateEntry(self, index):
129 self.l.invalidateEntry(index)
131 def setIndex(self, index, data):
132 self.list[index] = data
133 self.invalidateEntry(index)
135 def setList(self, list):
137 self.l.setList(self.list)
139 def setSelection(self, index):
140 if self.instance is not None:
141 self.instance.moveSelectionTo(index)
143 class CutListEditor(Screen, InfoBarSeek, InfoBarCueSheetSupport, InfoBarServiceName, HelpableScreen):
145 <screen position="0,0" size="720,576" flags="wfNoBorder" backgroundColor="#444444">
146 <eLabel position="360,0" size="360,313" backgroundColor="#ffffff" />
147 <widget name="Video" position="370,10" size="340,268" backgroundColor="transparent" zPosition="1" />
149 <eLabel position="50,80" size="300,24" text="Name:" font="Regular;20" foregroundColor="#cccccc" transparent="1" />
151 <widget source="CurrentService" render="Label" position="50,110" size="300,60" font="Regular;22" >
152 <convert type="ServiceName">Name</convert>
155 <widget source="CurrentService" render="Label" position="370,278" size="340,25"
156 backgroundColor="#000000" foregroundColor="#ffffff" font="Regular;19" zPosition="1" >
157 <convert type="ServicePosition">PositionDetailed</convert>
160 <widget name="Timeline" position="50,500" size="620,40" backgroundColor="#000000"
161 pointer="/usr/share/enigma2/position_pointer.png:3,5" foregroundColor="#ffffff" />
162 <widget name="Cutlist" position="50,325" size="620,175" scrollbarMode="showOnDemand" transparent="1" />
164 def __init__(self, session, service):
165 self.skin = CutListEditor.skin
166 Screen.__init__(self, session)
167 InfoBarSeek.__init__(self, actionmap = "CutlistSeekActions")
168 InfoBarCueSheetSupport.__init__(self)
169 InfoBarServiceName.__init__(self)
170 HelpableScreen.__init__(self)
171 self.old_service = session.nav.getCurrentlyPlayingServiceReference()
172 session.nav.playService(service)
174 service = session.nav.getCurrentService()
175 cue = service and service.cueSheet()
177 # disable cutlists. we want to freely browse around in the movie
178 print "cut lists disabled!"
179 cue.setCutListEnable(0)
181 self.downloadCuesheet()
183 self["Timeline"] = ServicePositionGauge(self.session.nav)
184 self["Cutlist"] = CutList(self.getCutlist())
185 self["Cutlist"].onSelectionChanged.append(self.selectionChanged)
187 self["Video"] = VideoWindow(decoder = 0)
189 self["actions"] = HelpableActionMap(self, "CutListEditorActions",
191 "setIn": (self.setIn, _("Make this mark an 'in' point")),
192 "setOut": (self.setOut, _("Make this mark an 'out' point")),
193 "setMark": (self.setMark, _("Make this mark just a mark")),
194 "addMark": (self.__addMark, _("Add a mark")),
195 "removeMark": (self.__removeMark, _("Remove a mark")),
196 "leave": (self.exit, _("Exit editor")),
197 "showMenu": (self.showMenu, _("menu")),
200 self.tutorial_seen = False
202 self.onExecBegin.append(self.showTutorial)
203 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
205 iPlayableService.evCuesheetChanged: self.refillList
208 # to track new entries we save the last version of the cutlist
210 self.cut_start = None
212 def showTutorial(self):
213 if not self.tutorial_seen:
214 self.tutorial_seen = True
215 self.session.open(MessageBox,
216 """Welcome to the Cutlist editor.
218 Seek to the start of the stuff you want to cut away. Press OK, select 'start cut'.
220 Then seek to the end, press OK, select 'end cut'. That's it.
221 """, MessageBox.TYPE_INFO)
223 def checkSkipShowHideLock(self):
226 def setType(self, index, type):
227 if len(self.cut_list):
228 self.cut_list[index] = (self.cut_list[index][0], type)
229 self["Cutlist"].setIndex(index, CutListEntry(*self.cut_list[index]))
232 m = self["Cutlist"].getCurrentIndex()
234 self.uploadCuesheet()
237 m = self["Cutlist"].getCurrentIndex()
239 self.uploadCuesheet()
242 m = self["Cutlist"].getCurrentIndex()
244 self.uploadCuesheet()
247 self.toggleMark(onlyadd=True, tolerance=90000) # do not allow two marks in <1s
249 def __removeMark(self):
250 m = self["Cutlist"].getCurrent()
256 self.session.nav.playService(self.old_service)
259 def getCutlist(self):
261 for e in self.cut_list:
262 r.append(CutListEntry(*e))
265 def selectionChanged(self):
266 where = self["Cutlist"].getCurrent()
271 seek = self.getSeek()
277 def refillList(self):
278 print "cue sheet changed, refilling"
279 self.downloadCuesheet()
281 # get the first changed entry, and select it
282 new_list = self.getCutlist()
283 self["Cutlist"].setList(new_list)
285 for i in range(min(len(new_list), len(self.last_cuts))):
286 if new_list[i] != self.last_cuts[i]:
287 self["Cutlist"].setSelection(i)
289 self.last_cuts = new_list
291 def getStateForPosition(self, pos):
294 # when first point is "in", the beginning is "out"
295 if len(self.cut_list) and self.cut_list[0][1] == 0:
298 for (where, what) in self.cut_list:
302 elif what == 1: # out
307 curpos = self.cueGetCurrentPosition()
311 self.setSeekState(self.SEEK_STATE_PAUSE)
313 self.context_position = curpos
315 self.context_nearest_mark = self.toggleMark(onlyreturn=True)
317 cur_state = self.getStateForPosition(curpos)
319 print "currently in 'IN'"
320 if self.cut_start is None or self.context_position < self.cut_start:
321 state = CutListContextMenu.SHOW_STARTCUT
323 state = CutListContextMenu.SHOW_ENDCUT
325 print "currently in 'OUT'"
326 state = CutListContextMenu.SHOW_DELETECUT
328 if self.context_nearest_mark is None:
333 self.session.openWithCallback(self.menuCallback, CutListContextMenu, state, nearmark)
335 def menuCallback(self, *result):
340 if result == CutListContextMenu.RET_STARTCUT:
341 self.cut_start = self.context_position
342 elif result == CutListContextMenu.RET_ENDCUT:
343 # remove in/out marks between the new cut
344 for (where, what) in self.cut_list[:]:
345 if self.cut_start <= where <= self.context_position and what in [0,1]:
346 self.cut_list.remove((where, what))
348 bisect.insort(self.cut_list, (self.cut_start, 1))
349 bisect.insort(self.cut_list, (self.context_position, 0))
350 self.uploadCuesheet()
351 self.cut_start = None
352 elif result == CutListContextMenu.RET_DELETECUT:
356 for (where, what) in self.cut_list:
357 if what == 1 and where < self.context_position: # out
358 out_before = (where, what)
359 elif what == 0 and where < self.context_position: # in, before out
361 elif what == 0 and where > self.context_position and in_after is None:
362 in_after = (where, what)
364 if out_before is not None:
365 self.cut_list.remove(out_before)
367 if in_after is not None:
368 self.cut_list.remove(in_after)
369 self.uploadCuesheet()
370 elif result == CutListContextMenu.RET_MARK:
372 elif result == CutListContextMenu.RET_DELETEMARK:
373 self.cut_list.remove(self.context_nearest_mark)
374 self.uploadCuesheet()
375 elif result == CutListContextMenu.RET_REMOVEBEFORE:
376 # remove in/out marks before current position
377 for (where, what) in self.cut_list[:]:
378 if where <= self.context_position and what in [0,1]:
379 self.cut_list.remove((where, what))
381 bisect.insort(self.cut_list, (self.context_position, 0))
382 self.uploadCuesheet()
383 elif result == CutListContextMenu.RET_REMOVEAFTER:
384 # remove in/out marks after 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, 1))
390 self.uploadCuesheet()
392 # we modify the "play" behaviour a bit:
393 # if we press pause while being in slowmotion, we will pause (and not play)
394 def playpauseService(self):
395 if self.seekstate not in [self.SEEK_STATE_PLAY, self.SEEK_STATE_SM_HALF, self.SEEK_STATE_SM_QUARTER, self.SEEK_STATE_SM_EIGHTH]:
396 self.unPauseService()
400 def main(session, service, **kwargs):
401 session.open(CutListEditor, service)
403 def Plugins(**kwargs):
404 return PluginDescriptor(name="Cutlist Editor", description=_("Cutlist editor..."), where = PluginDescriptor.WHERE_MOVIELIST, fnc=main)