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, InfoBarBase
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, InfoBarBase, InfoBarSeek, InfoBarCueSheetSupport, HelpableScreen):
149 <screen position="0,0" size="720,576" title="Cutlist editor" flags="wfNoBorder">
150 <eLabel text="Cutlist editor" position="65,60" size="300,25" font="Regular;20" />
151 <widget source="global.CurrentTime" render="Label" position="268,60" size="394,20" font="Regular;20" halign="right">
152 <convert type="ClockToText">Format:%A %B %d, %H:%M</convert>
154 <eLabel position="268,98" size="394,304" backgroundColor="#505555" />
155 <widget name="Video" position="270,100" zPosition="1" size="390,300" backgroundColor="transparent" />
156 <widget source="session.CurrentService" render="Label" position="135,405" size="450,50" font="Regular;22" halign="center" valign="center">
157 <convert type="ServiceName">Name</convert>
159 <widget source="session.CurrentService" render="Label" position="50,450" zPosition="1" size="620,25" font="Regular;20" halign="center" valign="center">
160 <convert type="ServicePosition">Position,Detailed</convert>
162 <eLabel position="62,98" size="179,274" backgroundColor="#505555" />
163 <eLabel position="64,100" size="175,270" backgroundColor="#000000" />
164 <widget name="Cutlist" position="64,100" zPosition="1" size="175,270" scrollbarMode="showOnDemand" transparent="1" />
165 <widget name="Timeline" position="50,485" size="615,20" backgroundColor="#505555" pointer="skin_default/position_arrow.png:3,5" foregroundColor="black" />
166 <ePixmap pixmap="skin_default/icons/mp_buttons.png" position="305,515" size="109,13" alphatest="on" />
169 def __init__(self, session, service):
170 self.skin = CutListEditor.skin
171 Screen.__init__(self, session)
172 InfoBarSeek.__init__(self, actionmap = "CutlistSeekActions")
173 InfoBarCueSheetSupport.__init__(self)
174 InfoBarBase.__init__(self, steal_current_service = True)
175 HelpableScreen.__init__(self)
176 self.old_service = session.nav.getCurrentlyPlayingServiceReference()
177 session.nav.playService(service)
179 service = session.nav.getCurrentService()
180 cue = service and service.cueSheet()
182 # disable cutlists. we want to freely browse around in the movie
183 print "cut lists disabled!"
184 cue.setCutListEnable(0)
186 self.downloadCuesheet()
188 self["Timeline"] = ServicePositionGauge(self.session.nav)
189 self["Cutlist"] = CutList(self.getCutlist())
190 self["Cutlist"].onSelectionChanged.append(self.selectionChanged)
192 self["Video"] = VideoWindow(decoder = 0)
194 self["actions"] = HelpableActionMap(self, "CutListEditorActions",
196 "setIn": (self.setIn, _("Make this mark an 'in' point")),
197 "setOut": (self.setOut, _("Make this mark an 'out' point")),
198 "setMark": (self.setMark, _("Make this mark just a mark")),
199 "addMark": (self.__addMark, _("Add a mark")),
200 "removeMark": (self.__removeMark, _("Remove a mark")),
201 "leave": (self.exit, _("Exit editor")),
202 "showMenu": (self.showMenu, _("menu")),
205 self.tutorial_seen = False
207 self.onExecBegin.append(self.showTutorial)
208 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
210 iPlayableService.evCuesheetChanged: self.refillList
213 # to track new entries we save the last version of the cutlist
215 self.cut_start = None
216 self.onClose.append(self.__onClose)
219 self.session.nav.playService(self.old_service)
221 def showTutorial(self):
222 if not self.tutorial_seen:
223 self.tutorial_seen = True
224 self.session.open(MessageBox,_("Welcome to the Cutlist editor.\n\nSeek to the start of the stuff you want to cut away. Press OK, select 'start cut'.\n\nThen seek to the end, press OK, select 'end cut'. That's it."), 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()
261 def getCutlist(self):
263 for e in self.cut_list:
264 r.append(CutListEntry(*e))
267 def selectionChanged(self):
268 where = self["Cutlist"].getCurrent()
273 seek = self.getSeek()
279 def refillList(self):
280 print "cue sheet changed, refilling"
281 self.downloadCuesheet()
283 # get the first changed entry, and select it
284 new_list = self.getCutlist()
285 self["Cutlist"].setList(new_list)
287 for i in range(min(len(new_list), len(self.last_cuts))):
288 if new_list[i] != self.last_cuts[i]:
289 self["Cutlist"].setSelection(i)
291 self.last_cuts = new_list
293 def getStateForPosition(self, pos):
296 # when first point is "in", the beginning is "out"
297 if len(self.cut_list) and self.cut_list[0][1] == 0:
300 for (where, what) in self.cut_list:
304 elif what == 1: # out
309 curpos = self.cueGetCurrentPosition()
313 self.setSeekState(self.SEEK_STATE_PAUSE)
315 self.context_position = curpos
317 self.context_nearest_mark = self.toggleMark(onlyreturn=True)
319 cur_state = self.getStateForPosition(curpos)
321 print "currently in 'IN'"
322 if self.cut_start is None or self.context_position < self.cut_start:
323 state = CutListContextMenu.SHOW_STARTCUT
325 state = CutListContextMenu.SHOW_ENDCUT
327 print "currently in 'OUT'"
328 state = CutListContextMenu.SHOW_DELETECUT
330 if self.context_nearest_mark is None:
335 self.session.openWithCallback(self.menuCallback, CutListContextMenu, state, nearmark)
337 def menuCallback(self, *result):
342 if result == CutListContextMenu.RET_STARTCUT:
343 self.cut_start = self.context_position
344 elif result == CutListContextMenu.RET_ENDCUT:
345 # remove in/out marks between the new cut
346 for (where, what) in self.cut_list[:]:
347 if self.cut_start <= where <= self.context_position and what in [0,1]:
348 self.cut_list.remove((where, what))
350 bisect.insort(self.cut_list, (self.cut_start, 1))
351 bisect.insort(self.cut_list, (self.context_position, 0))
352 self.uploadCuesheet()
353 self.cut_start = None
354 elif result == CutListContextMenu.RET_DELETECUT:
358 for (where, what) in self.cut_list:
359 if what == 1 and where < self.context_position: # out
360 out_before = (where, what)
361 elif what == 0 and where < self.context_position: # in, before out
363 elif what == 0 and where > self.context_position and in_after is None:
364 in_after = (where, what)
366 if out_before is not None:
367 self.cut_list.remove(out_before)
369 if in_after is not None:
370 self.cut_list.remove(in_after)
371 self.uploadCuesheet()
372 elif result == CutListContextMenu.RET_MARK:
374 elif result == CutListContextMenu.RET_DELETEMARK:
375 self.cut_list.remove(self.context_nearest_mark)
376 self.uploadCuesheet()
377 elif result == CutListContextMenu.RET_REMOVEBEFORE:
378 # remove in/out marks before current position
379 for (where, what) in self.cut_list[:]:
380 if where <= self.context_position and what in [0,1]:
381 self.cut_list.remove((where, what))
383 bisect.insort(self.cut_list, (self.context_position, 0))
384 self.uploadCuesheet()
385 elif result == CutListContextMenu.RET_REMOVEAFTER:
386 # remove in/out marks after current position
387 for (where, what) in self.cut_list[:]:
388 if where >= self.context_position and what in [0,1]:
389 self.cut_list.remove((where, what))
391 bisect.insort(self.cut_list, (self.context_position, 1))
392 self.uploadCuesheet()
394 # we modify the "play" behavior a bit:
395 # if we press pause while being in slowmotion, we will pause (and not play)
396 def playpauseService(self):
397 if self.seekstate != self.SEEK_STATE_PLAY and not self.isStateSlowMotion(self.seekstate):
398 self.unPauseService()
402 def main(session, service, **kwargs):
403 session.open(CutListEditor, service)
405 def Plugins(**kwargs):
406 return PluginDescriptor(name="Cutlist Editor", description=_("Cutlist editor..."), where = PluginDescriptor.WHERE_MOVIELIST, fnc=main)