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
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):
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" transparent="1"
161 pointer="/usr/share/enigma2/position_pointer.png:3,5" foregroundColor="#225b7395" />
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 self.old_service = session.nav.getCurrentlyPlayingServiceReference()
171 session.nav.playService(service)
173 service = session.nav.getCurrentService()
174 cue = service and service.cueSheet()
176 # disable cutlists. we want to freely browse around in the movie
177 print "cut lists disabled!"
178 cue.setCutListEnable(0)
180 self.downloadCuesheet()
182 self["Timeline"] = ServicePositionGauge(self.session.nav)
183 self["Cutlist"] = CutList(self.getCutlist())
184 self["Cutlist"].onSelectionChanged.append(self.selectionChanged)
186 self["Video"] = VideoWindow(decoder = 0)
188 self["actions"] = HelpableActionMap(self, "CutListEditorActions",
190 "setIn": (self.setIn, _("Make this mark an 'in' point")),
191 "setOut": (self.setOut, _("Make this mark an 'out' point")),
192 "setMark": (self.setMark, _("Make this mark just a mark")),
193 "addMark": (self.__addMark, _("Add a mark")),
194 "removeMark": (self.__removeMark, _("Remove a mark")),
195 "leave": (self.exit, _("Exit editor")),
196 "showMenu": self.showMenu,
199 self.tutorial_seen = False
201 self.onExecBegin.append(self.showTutorial)
202 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
204 iPlayableService.evCuesheetChanged: self.refillList
207 # to track new entries we save the last version of the cutlist
209 self.cut_start = None
211 def showTutorial(self):
212 if not self.tutorial_seen:
213 self.tutorial_seen = True
214 self.session.open(MessageBox,
215 """Welcome to the Cutlist editor. It's still a bit strange to use, but anyway:
217 Seek to the start of the stuff you want to cut away. Press OK, select 'start cut'.
219 Then seek to the end, press OK, select 'end cut'. That's it.
220 """, MessageBox.TYPE_INFO)
222 def checkSkipShowHideLock(self):
225 def setType(self, index, type):
226 if len(self.cut_list):
227 self.cut_list[index] = (self.cut_list[index][0], type)
228 self["Cutlist"].setIndex(index, CutListEntry(*self.cut_list[index]))
231 m = self["Cutlist"].getCurrentIndex()
233 self.uploadCuesheet()
236 m = self["Cutlist"].getCurrentIndex()
238 self.uploadCuesheet()
241 m = self["Cutlist"].getCurrentIndex()
243 self.uploadCuesheet()
246 self.toggleMark(onlyadd=True, tolerance=90000) # do not allow two marks in <1s
248 def __removeMark(self):
249 m = self["Cutlist"].getCurrent()
255 self.session.nav.playService(self.old_service)
258 def getCutlist(self):
260 for e in self.cut_list:
261 r.append(CutListEntry(*e))
264 def selectionChanged(self):
265 where = self["Cutlist"].getCurrent()
270 seek = self.getSeek()
276 def refillList(self):
277 print "cue sheet changed, refilling"
278 self.downloadCuesheet()
280 # get the first changed entry, and select it
281 new_list = self.getCutlist()
282 self["Cutlist"].setList(new_list)
284 for i in range(min(len(new_list), len(self.last_cuts))):
285 if new_list[i] != self.last_cuts[i]:
286 self["Cutlist"].setSelection(i)
288 self.last_cuts = new_list
290 def getStateForPosition(self, pos):
293 # when first point is "in", the beginning is "out"
294 if len(self.cut_list) and self.cut_list[0][1] == 0:
297 for (where, what) in self.cut_list:
301 elif what == 1: # out
306 curpos = self.cueGetCurrentPosition()
310 self.setSeekState(self.SEEK_STATE_PAUSE)
312 self.context_position = curpos
314 self.context_nearest_mark = self.toggleMark(onlyreturn=True)
316 cur_state = self.getStateForPosition(curpos)
318 print "currently in 'IN'"
319 if self.cut_start is None or self.context_position < self.cut_start:
320 state = CutListContextMenu.SHOW_STARTCUT
322 state = CutListContextMenu.SHOW_ENDCUT
324 print "currently in 'OUT'"
325 state = CutListContextMenu.SHOW_DELETECUT
327 if self.context_nearest_mark is None:
332 self.session.openWithCallback(self.menuCallback, CutListContextMenu, state, nearmark)
334 def menuCallback(self, *result):
339 if result == CutListContextMenu.RET_STARTCUT:
340 self.cut_start = self.context_position
341 elif result == CutListContextMenu.RET_ENDCUT:
342 # remove in/out marks between the new cut
343 for (where, what) in self.cut_list[:]:
344 if self.cut_start <= where <= self.context_position and what in [0,1]:
345 self.cut_list.remove((where, what))
347 bisect.insort(self.cut_list, (self.cut_start, 1))
348 bisect.insort(self.cut_list, (self.context_position, 0))
349 self.uploadCuesheet()
350 self.cut_start = None
351 elif result == CutListContextMenu.RET_DELETECUT:
355 for (where, what) in self.cut_list:
356 if what == 1 and where < self.context_position: # out
357 out_before = (where, what)
358 elif what == 0 and where < self.context_position: # in, before out
360 elif what == 0 and where > self.context_position and in_after is None:
361 in_after = (where, what)
363 if out_before is not None:
364 self.cut_list.remove(out_before)
366 if in_after is not None:
367 self.cut_list.remove(in_after)
368 self.uploadCuesheet()
369 elif result == CutListContextMenu.RET_MARK:
371 elif result == CutListContextMenu.RET_DELETEMARK:
372 self.cut_list.remove(self.context_nearest_mark)
373 self.uploadCuesheet()
374 elif result == CutListContextMenu.RET_REMOVEBEFORE:
375 # remove in/out marks before current position
376 for (where, what) in self.cut_list[:]:
377 if where <= self.context_position and what in [0,1]:
378 self.cut_list.remove((where, what))
380 bisect.insort(self.cut_list, (self.context_position, 0))
381 self.uploadCuesheet()
382 elif result == CutListContextMenu.RET_REMOVEAFTER:
383 # remove in/out marks after current position
384 for (where, what) in self.cut_list[:]:
385 if where >= self.context_position and what in [0,1]:
386 self.cut_list.remove((where, what))
388 bisect.insort(self.cut_list, (self.context_position, 1))
389 self.uploadCuesheet()
391 # we modify the "play" behaviour a bit:
392 # if we press pause while being in slowmotion, we will pause (and not play)
393 def playpauseService(self):
394 if self.seekstate not in [self.SEEK_STATE_PLAY, self.SEEK_STATE_SM_HALF, self.SEEK_STATE_SM_QUARTER, self.SEEK_STATE_SM_EIGHTH]:
395 self.unPauseService()
399 def main(session, service, **kwargs):
400 session.open(CutListEditor, service)
402 def Plugins(**kwargs):
403 return PluginDescriptor(name="Cutlist Editor", description=_("Cutlist editor..."), where = PluginDescriptor.WHERE_MOVIELIST, fnc=main)