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
16 from Components.Sources.List import List
20 def CutListEntry(where, what):
38 return ((where, what), "%dh:%02dm:%02ds:%03d" % (h, m, s, ms), type, type_col)
40 class CutListContextMenu(FixedMenu):
54 def __init__(self, session, state, nearmark):
55 menu = [(_("back"), self.close)] #, (None, )]
57 if state == self.SHOW_STARTCUT:
58 menu.append((_("start cut here"), self.startCut))
60 menu.append((_("start cut here"), ))
62 if state == self.SHOW_ENDCUT:
63 menu.append((_("end cut here"), self.endCut))
65 menu.append((_("end cut here"), ))
67 if state == self.SHOW_DELETECUT:
68 menu.append((_("delete cut"), self.deleteCut))
70 menu.append((_("delete cut"), ))
72 menu.append((_("remove before this position"), self.removeBefore))
73 menu.append((_("remove after this position"), self.removeAfter))
75 # menu.append((None, ))
78 menu.append((_("insert mark here"), self.insertMark))
80 menu.append((_("remove this mark"), self.removeMark))
82 menu.append((_("grab this frame as bitmap"), self.grabFrame))
83 FixedMenu.__init__(self, session, _("Cut"), menu)
84 self.skinName = "Menu"
87 self.close(self.RET_STARTCUT)
90 self.close(self.RET_ENDCUT)
93 self.close(self.RET_DELETECUT)
96 self.close(self.RET_MARK)
99 self.close(self.RET_DELETEMARK)
101 def removeBefore(self):
102 self.close(self.RET_REMOVEBEFORE)
104 def removeAfter(self):
105 self.close(self.RET_REMOVEAFTER)
108 self.close(self.RET_GRABFRAME)
110 class CutListEditor(Screen, InfoBarBase, InfoBarSeek, InfoBarCueSheetSupport, HelpableScreen):
112 <screen position="0,0" size="720,576" title="Cutlist editor" flags="wfNoBorder">
113 <eLabel text="Cutlist editor" position="65,60" size="300,25" font="Regular;20" />
114 <widget source="global.CurrentTime" render="Label" position="268,60" size="394,20" font="Regular;20" halign="right">
115 <convert type="ClockToText">Format:%A %B %d, %H:%M</convert>
117 <eLabel position="268,98" size="394,304" backgroundColor="#505555" />
118 <widget name="Video" position="270,100" zPosition="1" size="390,300" backgroundColor="transparent" />
119 <widget source="session.CurrentService" render="Label" position="135,405" size="450,50" font="Regular;22" halign="center" valign="center">
120 <convert type="ServiceName">Name</convert>
122 <widget source="session.CurrentService" render="Label" position="50,450" zPosition="1" size="620,25" font="Regular;20" halign="center" valign="center">
123 <convert type="ServicePosition">Position,Detailed</convert>
125 <eLabel position="62,98" size="179,274" backgroundColor="#505555" />
126 <eLabel position="64,100" size="175,270" backgroundColor="#000000" />
127 <widget source="cutlist" position="64,100" zPosition="1" size="175,270" scrollbarMode="showOnDemand" transparent="1" render="Listbox" >
128 <convert type="TemplatedMultiContent">
130 MultiContentEntryText(size=(125, 20), text = 1, backcolor = MultiContentTemplateColor(3)),
131 MultiContentEntryText(pos=(125,0), size=(50, 20), text = 2, flags = RT_HALIGN_RIGHT, backcolor = MultiContentTemplateColor(3))
133 "fonts": [gFont("Regular", 18)],
138 <widget name="Timeline" position="50,485" size="615,20" backgroundColor="#505555" pointer="skin_default/position_arrow.png:3,5" foregroundColor="black" />
139 <ePixmap pixmap="skin_default/icons/mp_buttons.png" position="305,515" size="109,13" alphatest="on" />
142 def __init__(self, session, service):
143 self.skin = CutListEditor.skin
144 Screen.__init__(self, session)
145 InfoBarSeek.__init__(self, actionmap = "CutlistSeekActions")
146 InfoBarCueSheetSupport.__init__(self)
147 InfoBarBase.__init__(self, steal_current_service = True)
148 HelpableScreen.__init__(self)
149 self.old_service = session.nav.getCurrentlyPlayingServiceReference()
150 session.nav.playService(service)
152 service = session.nav.getCurrentService()
153 cue = service and service.cueSheet()
155 # disable cutlists. we want to freely browse around in the movie
156 print "cut lists disabled!"
157 cue.setCutListEnable(0)
159 self.downloadCuesheet()
161 self["Timeline"] = ServicePositionGauge(self.session.nav)
162 self["cutlist"] = List(self.getCutlist())
163 self["cutlist"].onSelectionChanged.append(self.selectionChanged)
165 self["Video"] = VideoWindow(decoder = 0)
167 self["actions"] = HelpableActionMap(self, "CutListEditorActions",
169 "setIn": (self.setIn, _("Make this mark an 'in' point")),
170 "setOut": (self.setOut, _("Make this mark an 'out' point")),
171 "setMark": (self.setMark, _("Make this mark just a mark")),
172 "addMark": (self.__addMark, _("Add a mark")),
173 "removeMark": (self.__removeMark, _("Remove a mark")),
174 "leave": (self.exit, _("Exit editor")),
175 "showMenu": (self.showMenu, _("menu")),
178 self.tutorial_seen = False
180 self.onExecBegin.append(self.showTutorial)
181 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
183 iPlayableService.evCuesheetChanged: self.refillList
186 # to track new entries we save the last version of the cutlist
188 self.cut_start = None
189 self.onClose.append(self.__onClose)
192 self.session.nav.playService(self.old_service)
194 def showTutorial(self):
195 if not self.tutorial_seen:
196 self.tutorial_seen = True
197 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)
199 def checkSkipShowHideLock(self):
202 def setType(self, index, type):
203 if len(self.cut_list):
204 self.cut_list[index] = (self.cut_list[index][0], type)
205 self["cutlist"].modifyEntry(index, CutListEntry(*self.cut_list[index]))
208 m = self["cutlist"].getIndex()
210 self.uploadCuesheet()
213 m = self["cutlist"].getIndex()
215 self.uploadCuesheet()
218 m = self["cutlist"].getIndex()
220 self.uploadCuesheet()
223 self.toggleMark(onlyadd=True, tolerance=90000) # do not allow two marks in <1s
225 def __removeMark(self):
226 m = self["cutlist"].getCurrent()
234 def getCutlist(self):
236 for e in self.cut_list:
237 r.append(CutListEntry(*e))
240 def selectionChanged(self):
241 where = self["cutlist"].getCurrent()
246 seek = self.getSeek()
252 def refillList(self):
253 print "cue sheet changed, refilling"
254 self.downloadCuesheet()
256 # get the first changed entry, and select it
257 new_list = self.getCutlist()
258 self["cutlist"].list = new_list
260 for i in range(min(len(new_list), len(self.last_cuts))):
261 if new_list[i] != self.last_cuts[i]:
262 self["cutlist"].setIndex(i)
264 self.last_cuts = new_list
266 def getStateForPosition(self, pos):
269 # when first point is "in", the beginning is "out"
270 if len(self.cut_list) and self.cut_list[0][1] == 0:
273 for (where, what) in self.cut_list:
277 elif what == 1: # out
282 curpos = self.cueGetCurrentPosition()
286 self.setSeekState(self.SEEK_STATE_PAUSE)
288 self.context_position = curpos
290 self.context_nearest_mark = self.toggleMark(onlyreturn=True)
292 cur_state = self.getStateForPosition(curpos)
294 print "currently in 'IN'"
295 if self.cut_start is None or self.context_position < self.cut_start:
296 state = CutListContextMenu.SHOW_STARTCUT
298 state = CutListContextMenu.SHOW_ENDCUT
300 print "currently in 'OUT'"
301 state = CutListContextMenu.SHOW_DELETECUT
303 if self.context_nearest_mark is None:
308 self.session.openWithCallback(self.menuCallback, CutListContextMenu, state, nearmark)
310 def menuCallback(self, *result):
315 if result == CutListContextMenu.RET_STARTCUT:
316 self.cut_start = self.context_position
317 elif result == CutListContextMenu.RET_ENDCUT:
318 # remove in/out marks between the new cut
319 for (where, what) in self.cut_list[:]:
320 if self.cut_start <= where <= self.context_position and what in (0,1):
321 self.cut_list.remove((where, what))
323 bisect.insort(self.cut_list, (self.cut_start, 1))
324 bisect.insort(self.cut_list, (self.context_position, 0))
325 self.uploadCuesheet()
326 self.cut_start = None
327 elif result == CutListContextMenu.RET_DELETECUT:
331 for (where, what) in self.cut_list:
332 if what == 1 and where < self.context_position: # out
333 out_before = (where, what)
334 elif what == 0 and where < self.context_position: # in, before out
336 elif what == 0 and where > self.context_position and in_after is None:
337 in_after = (where, what)
339 if out_before is not None:
340 self.cut_list.remove(out_before)
342 if in_after is not None:
343 self.cut_list.remove(in_after)
344 self.uploadCuesheet()
345 elif result == CutListContextMenu.RET_MARK:
347 elif result == CutListContextMenu.RET_DELETEMARK:
348 self.cut_list.remove(self.context_nearest_mark)
349 self.uploadCuesheet()
350 elif result == CutListContextMenu.RET_REMOVEBEFORE:
351 # remove in/out marks before current position
352 for (where, what) in self.cut_list[:]:
353 if where <= self.context_position and what in (0,1):
354 self.cut_list.remove((where, what))
356 bisect.insort(self.cut_list, (self.context_position, 0))
357 self.uploadCuesheet()
358 elif result == CutListContextMenu.RET_REMOVEAFTER:
359 # remove in/out marks after current position
360 for (where, what) in self.cut_list[:]:
361 if where >= self.context_position and what in (0,1):
362 self.cut_list.remove((where, what))
364 bisect.insort(self.cut_list, (self.context_position, 1))
365 self.uploadCuesheet()
366 elif result == CutListContextMenu.RET_GRABFRAME:
369 # we modify the "play" behavior a bit:
370 # if we press pause while being in slowmotion, we will pause (and not play)
371 def playpauseService(self):
372 if self.seekstate != self.SEEK_STATE_PLAY and not self.isStateSlowMotion(self.seekstate):
373 self.unPauseService()
378 path = self.session.nav.getCurrentlyPlayingServiceReference().getPath()
379 from Components.Console import Console
380 grabConsole = Console()
381 cmd = 'grab -vblpr%d "%s"' % (180, path.rsplit('.',1)[0] + ".png")
382 grabConsole.ePopen(cmd)
383 self.playpauseService()
385 def main(session, service, **kwargs):
386 session.open(CutListEditor, service)
388 def Plugins(**kwargs):
389 return PluginDescriptor(name="Cutlist Editor", description=_("Cutlist editor..."), where = PluginDescriptor.WHERE_MOVIELIST, fnc=main)