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
15 from ServiceReference import ServiceReference
18 def CutListEntry(where, what):
19 res = [ (where, what) ]
33 res.append(MultiContentEntryText(size=(400, 20), text = "%dh:%02dm:%02ds:%03d" % (h, m, s, ms)))
34 res.append(MultiContentEntryText(pos=(400,0), size=(130, 20), text = type, flags = RT_HALIGN_RIGHT))
38 class CutListContextMenu(FixedMenu):
52 def __init__(self, session, state, nearmark):
53 menu = [(_("back"), self.close)] #, (None, )]
55 if state == self.SHOW_STARTCUT:
56 menu.append((_("start cut here"), self.startCut))
58 menu.append((_("start cut here"), ))
60 if state == self.SHOW_ENDCUT:
61 menu.append((_("end cut here"), self.endCut))
63 menu.append((_("end cut here"), ))
65 if state == self.SHOW_DELETECUT:
66 menu.append((_("delete cut"), self.deleteCut))
68 menu.append((_("delete cut"), ))
70 menu.append((_("remove before this position"), self.removeBefore))
71 menu.append((_("remove after this position"), self.removeAfter))
73 # menu.append((None, ))
76 menu.append((_("insert mark here"), self.insertMark))
78 menu.append((_("remove this mark"), self.removeMark))
80 menu.append((("grab this frame as bitmap"), self.grabFrame))
81 FixedMenu.__init__(self, session, _("Cut"), menu)
82 self.skinName = "Menu"
85 self.close(self.RET_STARTCUT)
88 self.close(self.RET_ENDCUT)
91 self.close(self.RET_DELETECUT)
94 self.close(self.RET_MARK)
97 self.close(self.RET_DELETEMARK)
99 def removeBefore(self):
100 self.close(self.RET_REMOVEBEFORE)
102 def removeAfter(self):
103 self.close(self.RET_REMOVEAFTER)
106 self.close(self.RET_GRABFRAME)
108 class CutList(GUIComponent):
109 def __init__(self, list):
110 GUIComponent.__init__(self)
111 self.l = eListboxPythonMultiContent()
113 self.l.setFont(0, gFont("Regular", 20))
114 self.onSelectionChanged = [ ]
116 def getCurrent(self):
117 return self.l.getCurrentSelection()
119 def getCurrentIndex(self):
120 return self.l.getCurrentSelectionIndex()
122 GUI_WIDGET = eListbox
124 def postWidgetCreate(self, instance):
125 instance.setContent(self.l)
126 instance.setItemHeight(30)
127 instance.selectionChanged.get().append(self.selectionChanged)
129 def preWidgetRemove(self, instance):
130 instance.setContent(None)
131 instance.selectionChanged.get().remove(self.selectionChanged)
133 def selectionChanged(self):
134 for x in self.onSelectionChanged:
137 def invalidateEntry(self, index):
138 self.l.invalidateEntry(index)
140 def setIndex(self, index, data):
141 self.list[index] = data
142 self.invalidateEntry(index)
144 def setList(self, list):
146 self.l.setList(self.list)
148 def setSelection(self, index):
149 if self.instance is not None:
150 self.instance.moveSelectionTo(index)
152 class CutListEditor(Screen, InfoBarBase, InfoBarSeek, InfoBarCueSheetSupport, HelpableScreen):
154 <screen position="0,0" size="720,576" title="Cutlist editor" flags="wfNoBorder">
155 <eLabel text="Cutlist editor" position="65,60" size="300,25" font="Regular;20" />
156 <widget source="global.CurrentTime" render="Label" position="268,60" size="394,20" font="Regular;20" halign="right">
157 <convert type="ClockToText">Format:%A %B %d, %H:%M</convert>
159 <eLabel position="268,98" size="394,304" backgroundColor="#505555" />
160 <widget name="Video" position="270,100" zPosition="1" size="390,300" backgroundColor="transparent" />
161 <widget source="session.CurrentService" render="Label" position="135,405" size="450,50" font="Regular;22" halign="center" valign="center">
162 <convert type="ServiceName">Name</convert>
164 <widget source="session.CurrentService" render="Label" position="50,450" zPosition="1" size="620,25" font="Regular;20" halign="center" valign="center">
165 <convert type="ServicePosition">Position,Detailed</convert>
167 <eLabel position="62,98" size="179,274" backgroundColor="#505555" />
168 <eLabel position="64,100" size="175,270" backgroundColor="#000000" />
169 <widget name="Cutlist" position="64,100" zPosition="1" size="175,270" scrollbarMode="showOnDemand" transparent="1" />
170 <widget name="Timeline" position="50,485" size="615,20" backgroundColor="#505555" pointer="skin_default/position_arrow.png:3,5" foregroundColor="black" />
171 <ePixmap pixmap="skin_default/icons/mp_buttons.png" position="305,515" size="109,13" alphatest="on" />
174 def __init__(self, session, service):
175 self.skin = CutListEditor.skin
176 Screen.__init__(self, session)
177 InfoBarSeek.__init__(self, actionmap = "CutlistSeekActions")
178 InfoBarCueSheetSupport.__init__(self)
179 InfoBarBase.__init__(self, steal_current_service = True)
180 HelpableScreen.__init__(self)
181 self.old_service = session.nav.getCurrentlyPlayingServiceReference()
182 session.nav.playService(service)
184 service = session.nav.getCurrentService()
185 cue = service and service.cueSheet()
187 # disable cutlists. we want to freely browse around in the movie
188 print "cut lists disabled!"
189 cue.setCutListEnable(0)
191 self.downloadCuesheet()
193 self["Timeline"] = ServicePositionGauge(self.session.nav)
194 self["Cutlist"] = CutList(self.getCutlist())
195 self["Cutlist"].onSelectionChanged.append(self.selectionChanged)
197 self["Video"] = VideoWindow(decoder = 0)
199 self["actions"] = HelpableActionMap(self, "CutListEditorActions",
201 "setIn": (self.setIn, _("Make this mark an 'in' point")),
202 "setOut": (self.setOut, _("Make this mark an 'out' point")),
203 "setMark": (self.setMark, _("Make this mark just a mark")),
204 "addMark": (self.__addMark, _("Add a mark")),
205 "removeMark": (self.__removeMark, _("Remove a mark")),
206 "leave": (self.exit, _("Exit editor")),
207 "showMenu": (self.showMenu, _("menu")),
210 self.tutorial_seen = False
212 self.onExecBegin.append(self.showTutorial)
213 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
215 iPlayableService.evCuesheetChanged: self.refillList
218 # to track new entries we save the last version of the cutlist
220 self.cut_start = None
221 self.onClose.append(self.__onClose)
224 self.session.nav.playService(self.old_service)
226 def showTutorial(self):
227 if not self.tutorial_seen:
228 self.tutorial_seen = True
229 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)
231 def checkSkipShowHideLock(self):
234 def setType(self, index, type):
235 if len(self.cut_list):
236 self.cut_list[index] = (self.cut_list[index][0], type)
237 self["Cutlist"].setIndex(index, CutListEntry(*self.cut_list[index]))
240 m = self["Cutlist"].getCurrentIndex()
242 self.uploadCuesheet()
245 m = self["Cutlist"].getCurrentIndex()
247 self.uploadCuesheet()
250 m = self["Cutlist"].getCurrentIndex()
252 self.uploadCuesheet()
255 self.toggleMark(onlyadd=True, tolerance=90000) # do not allow two marks in <1s
257 def __removeMark(self):
258 m = self["Cutlist"].getCurrent()
266 def getCutlist(self):
268 for e in self.cut_list:
269 r.append(CutListEntry(*e))
272 def selectionChanged(self):
273 where = self["Cutlist"].getCurrent()
278 seek = self.getSeek()
284 def refillList(self):
285 print "cue sheet changed, refilling"
286 self.downloadCuesheet()
288 # get the first changed entry, and select it
289 new_list = self.getCutlist()
290 self["Cutlist"].setList(new_list)
292 for i in range(min(len(new_list), len(self.last_cuts))):
293 if new_list[i] != self.last_cuts[i]:
294 self["Cutlist"].setSelection(i)
296 self.last_cuts = new_list
298 def getStateForPosition(self, pos):
301 # when first point is "in", the beginning is "out"
302 if len(self.cut_list) and self.cut_list[0][1] == 0:
305 for (where, what) in self.cut_list:
309 elif what == 1: # out
314 curpos = self.cueGetCurrentPosition()
318 self.setSeekState(self.SEEK_STATE_PAUSE)
320 self.context_position = curpos
322 self.context_nearest_mark = self.toggleMark(onlyreturn=True)
324 cur_state = self.getStateForPosition(curpos)
326 print "currently in 'IN'"
327 if self.cut_start is None or self.context_position < self.cut_start:
328 state = CutListContextMenu.SHOW_STARTCUT
330 state = CutListContextMenu.SHOW_ENDCUT
332 print "currently in 'OUT'"
333 state = CutListContextMenu.SHOW_DELETECUT
335 if self.context_nearest_mark is None:
340 self.session.openWithCallback(self.menuCallback, CutListContextMenu, state, nearmark)
342 def menuCallback(self, *result):
347 if result == CutListContextMenu.RET_STARTCUT:
348 self.cut_start = self.context_position
349 elif result == CutListContextMenu.RET_ENDCUT:
350 # remove in/out marks between the new cut
351 for (where, what) in self.cut_list[:]:
352 if self.cut_start <= where <= self.context_position and what in [0,1]:
353 self.cut_list.remove((where, what))
355 bisect.insort(self.cut_list, (self.cut_start, 1))
356 bisect.insort(self.cut_list, (self.context_position, 0))
357 self.uploadCuesheet()
358 self.cut_start = None
359 elif result == CutListContextMenu.RET_DELETECUT:
363 for (where, what) in self.cut_list:
364 if what == 1 and where < self.context_position: # out
365 out_before = (where, what)
366 elif what == 0 and where < self.context_position: # in, before out
368 elif what == 0 and where > self.context_position and in_after is None:
369 in_after = (where, what)
371 if out_before is not None:
372 self.cut_list.remove(out_before)
374 if in_after is not None:
375 self.cut_list.remove(in_after)
376 self.uploadCuesheet()
377 elif result == CutListContextMenu.RET_MARK:
379 elif result == CutListContextMenu.RET_DELETEMARK:
380 self.cut_list.remove(self.context_nearest_mark)
381 self.uploadCuesheet()
382 elif result == CutListContextMenu.RET_REMOVEBEFORE:
383 # remove in/out marks before 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, 0))
389 self.uploadCuesheet()
390 elif result == CutListContextMenu.RET_REMOVEAFTER:
391 # remove in/out marks after current position
392 for (where, what) in self.cut_list[:]:
393 if where >= self.context_position and what in [0,1]:
394 self.cut_list.remove((where, what))
396 bisect.insort(self.cut_list, (self.context_position, 1))
397 self.uploadCuesheet()
398 elif result == CutListContextMenu.RET_GRABFRAME:
401 # we modify the "play" behavior a bit:
402 # if we press pause while being in slowmotion, we will pause (and not play)
403 def playpauseService(self):
404 if self.seekstate != self.SEEK_STATE_PLAY and not self.isStateSlowMotion(self.seekstate):
405 self.unPauseService()
410 path = self.session.nav.getCurrentlyPlayingServiceReference().getPath()
411 from Components.Console import Console
412 grabConsole = Console()
413 cmd = 'grab -vblpr%d "%s"' % (180, path.rsplit('.',1)[0] + ".png")
414 grabConsole.ePopen(cmd)
415 self.playpauseService()
417 def main(session, service, **kwargs):
418 session.open(CutListEditor, service)
420 def Plugins(**kwargs):
421 return PluginDescriptor(name="Cutlist Editor", description=_("Cutlist editor..."), where = PluginDescriptor.WHERE_MOVIELIST, fnc=main)