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)
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
217 def showTutorial(self):
218 if not self.tutorial_seen:
219 self.tutorial_seen = True
220 self.session.open(MessageBox,
221 """Welcome to the Cutlist editor.
223 Seek to the start of the stuff you want to cut away. Press OK, select 'start cut'.
225 Then seek to the end, press OK, select 'end cut'. That's it.
226 """, MessageBox.TYPE_INFO)
228 def checkSkipShowHideLock(self):
231 def setType(self, index, type):
232 if len(self.cut_list):
233 self.cut_list[index] = (self.cut_list[index][0], type)
234 self["Cutlist"].setIndex(index, CutListEntry(*self.cut_list[index]))
237 m = self["Cutlist"].getCurrentIndex()
239 self.uploadCuesheet()
242 m = self["Cutlist"].getCurrentIndex()
244 self.uploadCuesheet()
247 m = self["Cutlist"].getCurrentIndex()
249 self.uploadCuesheet()
252 self.toggleMark(onlyadd=True, tolerance=90000) # do not allow two marks in <1s
254 def __removeMark(self):
255 m = self["Cutlist"].getCurrent()
261 self.session.nav.playService(self.old_service)
264 def getCutlist(self):
266 for e in self.cut_list:
267 r.append(CutListEntry(*e))
270 def selectionChanged(self):
271 where = self["Cutlist"].getCurrent()
276 seek = self.getSeek()
282 def refillList(self):
283 print "cue sheet changed, refilling"
284 self.downloadCuesheet()
286 # get the first changed entry, and select it
287 new_list = self.getCutlist()
288 self["Cutlist"].setList(new_list)
290 for i in range(min(len(new_list), len(self.last_cuts))):
291 if new_list[i] != self.last_cuts[i]:
292 self["Cutlist"].setSelection(i)
294 self.last_cuts = new_list
296 def getStateForPosition(self, pos):
299 # when first point is "in", the beginning is "out"
300 if len(self.cut_list) and self.cut_list[0][1] == 0:
303 for (where, what) in self.cut_list:
307 elif what == 1: # out
312 curpos = self.cueGetCurrentPosition()
316 self.setSeekState(self.SEEK_STATE_PAUSE)
318 self.context_position = curpos
320 self.context_nearest_mark = self.toggleMark(onlyreturn=True)
322 cur_state = self.getStateForPosition(curpos)
324 print "currently in 'IN'"
325 if self.cut_start is None or self.context_position < self.cut_start:
326 state = CutListContextMenu.SHOW_STARTCUT
328 state = CutListContextMenu.SHOW_ENDCUT
330 print "currently in 'OUT'"
331 state = CutListContextMenu.SHOW_DELETECUT
333 if self.context_nearest_mark is None:
338 self.session.openWithCallback(self.menuCallback, CutListContextMenu, state, nearmark)
340 def menuCallback(self, *result):
345 if result == CutListContextMenu.RET_STARTCUT:
346 self.cut_start = self.context_position
347 elif result == CutListContextMenu.RET_ENDCUT:
348 # remove in/out marks between the new cut
349 for (where, what) in self.cut_list[:]:
350 if self.cut_start <= where <= self.context_position and what in [0,1]:
351 self.cut_list.remove((where, what))
353 bisect.insort(self.cut_list, (self.cut_start, 1))
354 bisect.insort(self.cut_list, (self.context_position, 0))
355 self.uploadCuesheet()
356 self.cut_start = None
357 elif result == CutListContextMenu.RET_DELETECUT:
361 for (where, what) in self.cut_list:
362 if what == 1 and where < self.context_position: # out
363 out_before = (where, what)
364 elif what == 0 and where < self.context_position: # in, before out
366 elif what == 0 and where > self.context_position and in_after is None:
367 in_after = (where, what)
369 if out_before is not None:
370 self.cut_list.remove(out_before)
372 if in_after is not None:
373 self.cut_list.remove(in_after)
374 self.uploadCuesheet()
375 elif result == CutListContextMenu.RET_MARK:
377 elif result == CutListContextMenu.RET_DELETEMARK:
378 self.cut_list.remove(self.context_nearest_mark)
379 self.uploadCuesheet()
380 elif result == CutListContextMenu.RET_REMOVEBEFORE:
381 # remove in/out marks before current position
382 for (where, what) in self.cut_list[:]:
383 if where <= self.context_position and what in [0,1]:
384 self.cut_list.remove((where, what))
386 bisect.insort(self.cut_list, (self.context_position, 0))
387 self.uploadCuesheet()
388 elif result == CutListContextMenu.RET_REMOVEAFTER:
389 # remove in/out marks after current position
390 for (where, what) in self.cut_list[:]:
391 if where >= self.context_position and what in [0,1]:
392 self.cut_list.remove((where, what))
394 bisect.insort(self.cut_list, (self.context_position, 1))
395 self.uploadCuesheet()
397 # we modify the "play" behavior a bit:
398 # if we press pause while being in slowmotion, we will pause (and not play)
399 def playpauseService(self):
400 if self.seekstate != self.SEEK_STATE_PLAY and not self.isStateSlowMotion(self.seekstate):
401 self.unPauseService()
405 def main(session, service, **kwargs):
406 session.open(CutListEditor, service)
408 def Plugins(**kwargs):
409 return PluginDescriptor(name="Cutlist Editor", description=_("Cutlist editor..."), where = PluginDescriptor.WHERE_MOVIELIST, fnc=main)