Merge lp:~nico-inattendu/luciole/bp-kdenlive-export into lp:luciole/0.8

Proposed by NicoInattendu
Status: Merged
Merged at revision: not available
Proposed branch: lp:~nico-inattendu/luciole/bp-kdenlive-export
Merge into: lp:luciole/0.8
Prerequisite: lp:~nico-inattendu/luciole/bp-pitivi-export
Diff against target: 3402 lines (+1674/-1470)
16 files modified
lucioLib/gui/luciole_export_tool_window.py (+117/-93)
lucioLib/gui/luciole_export_window.py (+7/-7)
lucioLib/lcl_export/__init__.py (+24/-0)
lucioLib/lcl_export/lcl_export_cinelerra.py (+114/-0)
lucioLib/lcl_export/lcl_export_kdenlive.py (+289/-0)
lucioLib/lcl_export/lcl_export_pitivi.py (+199/-0)
lucioLib/lcl_export/lcl_export_tool_base.py (+203/-0)
lucioLib/lcl_export/lcl_export_video.py (+656/-0)
lucioLib/lucioExport/__init__.py (+0/-26)
lucioLib/lucioExport/lcl_export_pitivi.py (+0/-217)
lucioLib/lucioExport/lcl_export_tool_base.py (+0/-188)
lucioLib/lucioExport/luciole_export.py (+0/-656)
lucioLib/lucioExport/luciole_export_tool.py (+0/-281)
lucioLib/luciole_et.py (+9/-0)
templates/kdenlive_template.kdenlive (+46/-0)
ui/export_file.glade (+10/-2)
To merge this branch: bzr merge lp:~nico-inattendu/luciole/bp-kdenlive-export
Reviewer Review Type Date Requested Status
NicoInattendu Pending
Review via email: mp+16795@code.launchpad.net
To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lucioLib/gui/luciole_export_tool_window.py'
2--- lucioLib/gui/luciole_export_tool_window.py 2010-01-04 17:42:18 +0000
3+++ lucioLib/gui/luciole_export_tool_window.py 2010-01-04 17:42:19 +0000
4@@ -1,10 +1,10 @@
5 #!/usr/bin/env python
6 # -*- coding: utf-8 -*-
7 # -*- Mode: Python -*-
8-# vi:si:ai:et:sw=4:sts=4:ts=4
9-#
10-#
11-# Copyright Nicolas Bertrand (nico@inattendu.org), 2009
12+# vim:si:ai:et:sw=4:sts=4:ts=4
13+#
14+#
15+# Copyright Nicolas Bertrand (nico@inattendu.org), 2010
16 #
17 # This file is part of Luciole.
18 #
19@@ -31,12 +31,15 @@
20 import gtk
21 import os.path
22
23-from .. import lucioExport as LEF
24-from ..lucioExport import lcl_export_pitivi as LEP
25+from ..lcl_export import lcl_export_cinelerra as LEC
26+from ..lcl_export import lcl_export_pitivi as LEP
27+from ..lcl_export import lcl_export_kdenlive as LEK
28
29 import dialog as G_DIALOG
30
31-BASE_DIR = './'
32+# TODO : base dir. should be provided by luciole . Not computed here
33+BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
34+TEMPLATE_DIR = os.path.join(BASE_DIR,'templates')
35
36 class SimpleBuilderApp(gtk.Builder):
37 def __init__(self):
38@@ -44,32 +47,57 @@
39 gtk.Builder.__init__(self)
40
41
42-
43-
44 class dialog_export_file(SimpleBuilderApp):
45-
46-
47- def __init__(self, path="export_file.glade", root="dialog_export_file", domain="", form=None, project=None, **kwargs):
48+
49+ EXPORTERS = {
50+ 'cinelerra' : {
51+ 'ext' : '.xml',
52+ 'template' : os.path.join(TEMPLATE_DIR, "cinelerra_template.xml"),
53+ 'func' : LEC.lcl_export_cinelerra
54+ },
55+ 'pitivi': {
56+ 'ext' : '.xptv',
57+ 'template' : os.path.join(TEMPLATE_DIR,"pitivi_template.xptv"),
58+ 'func' : LEP.lcl_export_pitivi
59+ },
60+ 'kdenlive' : {
61+ 'ext' : '.kdenlive',
62+ 'template' : os.path.join(TEMPLATE_DIR,"kdenlive_template.kdenlive"),
63+ 'func' : LEK.lcl_export_kdenlive
64+ },
65+ 'openshot' : {
66+ 'ext' : '.osp',
67+ 'template' : os.path.join(TEMPLATE_DIR,"openshot_template"),
68+ 'func' : None
69+ },
70+ }
71+
72+
73+ def __init__(self, path="export_file.glade", root="dialog_export_file", domain="", form=None, project=None, **kwargs):
74+ """ init dialog_export_file class : Manage the export tool window
75+ path : the path to the glade file to use
76+ root : main window name
77+ domain : i18n TBD / Not used
78+ form : TBD / Not used
79+ project : luciole project data
80+
81+
82+ """
83
84- # set export template dir
85- self.TEMPLATE_DIR = os.path.join(BASE_DIR,'templates')
86- self.templates = dict()
87 self.project=project
88- self.templates = {
89- "cinelerra" : os.path.join(self.TEMPLATE_DIR, "cinelerra_template.xml") ,
90- "pitivi" : os.path.join(self.TEMPLATE_DIR, "pitivi_template.xptv") ,
91- "kdenlive" : os.path.join(self.TEMPLATE_DIR, "kdenlive_template") ,
92- "openshot" : os.path.join(self.TEMPLATE_DIR, "openshot_template") }
93-
94+
95+ #
96+ # initialize window
97+ #
98 SimpleBuilderApp.__init__(self)
99 self.add_from_file(os.path.join(self.GLADE_DIR,path))
100 self.connect_signals(self)
101-
102 self.window = self.get_object(root)
103+
104+
105 # Default Export type
106 self.export_type = "cinelerra"
107- self.extension=".xml"
108- self.BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
109+
110 #
111 # widgets configuration
112 #
113@@ -79,124 +107,120 @@
114 initial_folder = os.path.expandvars('$HOME')
115 self._filechooser = self.get_object("filechooserbutton1")
116 self._filechooser.set_current_folder(initial_folder)
117-
118+
119+ # Status bar initialization
120 self._status_bar_widget = self.get_object("statusbar1")
121- #self._status_bar_widget_cid = self._status_bar_widget.get_context_id()
122 self._status_bar_widget_cid = 1
123 msg = _("Select an application and a file name name for export")
124 self._status_bar_widget.push(self._status_bar_widget_cid,msg)
125-
126+
127+ # show window
128 self.window.show()
129+
130+ #
131+ # SIGNAL methods
132+ #
133+
134 def on_dialog_export_file_destroy(self, widget, data=None):
135+ """ Signal destroy : close window """
136 self.window.destroy()
137
138 def on_dialog_export_file_close(self, widget, data=None):
139+ """ Signal close window : close window """
140 self.window.destroy()
141
142- def on_dialog_export_file_response(self, widget, data=None):
143- pass
144-
145
146 def on_filechooserbutton1_file_set(self, widget, data=None):
147+ """ Signal filechooser : get filename """
148 self.export_folder = widget.get_filename()
149
150
151 def on_radio_cine_toggled(self, widget, data=None):
152+ """ Signal cinelerra button toggled : set the export type
153+ and update filename window """
154 self.export_type ="cinelerra"
155- self.extension=".xml"
156-
157+ self.__update_fn_ext()
158
159 def on_radio_kdenlive_toggled(self, widget, data=None):
160+ """ Signal kdenlive button toggled : set the export type
161+ and update filename window """
162 self.export_type ="kdenlive"
163+ self.__update_fn_ext()
164+
165
166 def on_radio_pitivi_toggled(self, widget, data=None):
167+ """ Signal pitivi button toggled : set the export type
168+ and update filename window """
169 self.export_type ="pitivi"
170- self.extension=".xptv"
171+ self.__update_fn_ext()
172
173 def on_radio_openshot_toggled(self, widget, data=None):
174+ """ Signal openshot button toggled : set the export type
175+ and update filename window """
176 self.export_type ="openshot"
177-
178- def on_entry_fn_activate(self, widget, data=None):
179- print __file__,"DEBUG : on_entry_fn_activate",data
180- self.filename = "filename"
181-
182-
183- def make_export(self) :
184+ self.__update_fn_ext()
185+
186+ def __update_fn_ext(self) :
187+ """ update filename entry with the correct extension """
188+ (filename, extension)=os.path.splitext(self._fn_widget.get_text())
189+ if extension != self.EXPORTERS[self.export_type]['ext'] :
190+ self._fn_widget.set_text(filename+self.EXPORTERS[self.export_type]['ext'] )
191+
192+
193+ def __make_export(self) :
194+ """ Render export, according export type, folder, and filename"""
195+
196+ # clear status bar
197 self._status_bar_widget.pop(self._status_bar_widget_cid)
198
199- #get filename form entry object
200- (self.filename, self.extension)=os.path.splitext(self._fn_widget.get_text())
201+ # get filename from entry object
202+ (self.filename, __extension)=os.path.splitext(self._fn_widget.get_text())
203
204 # get folder
205 self.export_folder = self._filechooser.get_filename()
206
207- self.full_export_path = os.path.join(self.export_folder,self.filename+self.extension)
208- print __file__," DEBUG :", self.full_export_path
209-
210- ForceExport = True # by default overide of export filename is allowed
211-
212+ # correct extension if needed
213+ if __extension != self.EXPORTERS[self.export_type]['ext'] :
214+ __extension = self.EXPORTERS[self.export_type]['ext']
215+ self._fn_widget.set_text(self.filename+__extension )
216+
217+
218+ self.full_export_path = os.path.join(self.export_folder,self.filename+__extension)
219+
220+ ForceExport = True # by default override of export filename is allowed
221+
222+ # display a message to allow overwrite or not
223 if os.path.exists(self.full_export_path):
224 # add message display
225 msg =_("File %s already exists. Replace file ?") % self.full_export_path
226 ForceExport = G_DIALOG.Dialog.QuestionMessage(self.window,msg)
227+
228
229-
230 if ForceExport == True :
231- if self.export_type == "cinelerra" :
232- if self.extension == ".xml" :
233- self.make_export_cinelerra()
234- else :
235- msg = _("Not a valid extenstion. Should be .xml")
236- self._status_bar_widget.push(self._status_bar_widget_cid,msg)
237- elif self.export_type == "pitivi" :
238- if self.extension == ".xptv" :
239- self.make_export_pitivi()
240- else :
241- msg = _("Not a valide extenstion. Should be .xptv")
242- self._status_bar_widget.push(self._status_bar_widget_cid,msg)
243- else :
244- print "Nothing (yet !) to export"
245+ # launch export according retrieved data
246+ X = self.EXPORTERS[self.export_type]['func'](
247+ lcl_project = self.project,
248+ template= self.EXPORTERS[self.export_type]['template'],
249+ export_file = self.full_export_path
250+ )
251+ X.generate()
252+
253+ msg = _("Export Done")
254+ self._status_bar_widget.push(self._status_bar_widget_cid,msg)
255
256- def make_export_cinelerra(self):
257-
258- X = LEF.luciole_export_tool_cinelerra(
259- lcl_project = self.project,
260- template= self.templates["cinelerra"],
261- export_file = self.full_export_path)
262- X.generate()
263-
264- msg = _("Export Done")
265- self._status_bar_widget.push(self._status_bar_widget_cid,msg)
266-
267- def make_export_pitivi(self):
268- X = LEP.lcl_export_pitivi(
269- lcl_project = self.project,
270- template= self.templates["pitivi"],
271- export_file = self.full_export_path)
272- X.generate()
273-
274- msg = _("Export Done")
275- self._status_bar_widget.push(self._status_bar_widget_cid,msg)
276-
277
278 def run(self) :
279+ """ main function in window, wait for an action of the user"""
280 exit_loop = False
281 while (exit_loop == False) :
282- response = self.window.run() #wait for a reponse
283+ response = self.window.run() #wait for a response
284 if response == 2 :
285 # export button
286- self.make_export()
287+ self.__make_export()
288 else :
289 # exit
290 exit_loop = True
291
292+ #exit export window
293 self.window.destroy()
294
295-def main():
296-
297- frmExportVideo1 = dialog_export_file()
298-
299- frmExportVideo1.run()
300-
301-if __name__ == "__main__":
302- main()
303
304=== modified file 'lucioLib/gui/luciole_export_window.py'
305--- lucioLib/gui/luciole_export_window.py 2009-11-23 06:28:27 +0000
306+++ lucioLib/gui/luciole_export_window.py 2010-01-04 17:42:19 +0000
307@@ -29,7 +29,7 @@
308 from gettext import gettext as _
309
310 import dialog as G_DIALOG
311-from .. import lucioExport
312+from ..lcl_export import lcl_export_video as LEXP
313
314 class luciole_export_window(object) :
315 """ This class manages the window used for video export"""
316@@ -124,7 +124,7 @@
317 self._progressbar.set_text('')
318
319 # create an export object : Need the poroject tmp dir and widget to interact with
320- expObj = lucioExport.luciole_export(os.path.join(project_data['project_dir'],project_data['tmp_dir']), self)
321+ expObj = LEXP.lcl_export_video(os.path.join(project_data['project_dir'],project_data['tmp_dir']), self)
322
323 # create export_data dictionnary who goes to collect data needed for export tools
324 export_data = {}
325@@ -155,7 +155,7 @@
326 # get image list from chrono/montage
327 #
328
329- export_data['image_input']= lucioExport.IMAGE_LIST # export data is type IMAGE_LIST
330+ export_data['image_input']= LEXP.IMAGE_LIST # export data is type IMAGE_LIST
331
332 # Verify that a video name is entered and correct
333 l_pattern = re.compile(r'^\w+$')
334@@ -176,13 +176,13 @@
335 # get the type of export requested form the combobox
336 comboVal = self._combobox.get_active()
337 if comboVal == 0 :
338- exportType= lucioExport.EXPORT_DV
339+ exportType= LEXP.EXPORT_DV
340 elif comboVal == 1 :
341 #export do dvd
342- exportType= lucioExport.EXPORT_DVD
343+ exportType= LEXP.EXPORT_DVD
344 elif comboVal == 2 :
345 #export do xvid
346- exportType= lucioExport.EXPORT_XVID
347+ exportType= LEXP.EXPORT_XVID
348 else :
349 print _('unknown video export command')
350 exportType=None
351@@ -193,7 +193,7 @@
352 ForceExport = False # by default overide of export filename is not allowed
353 (ResExport, videopath) = expObj.export(export_data , False)
354
355- if (ResExport == lucioExport.ERR_FILE_EXIST) :
356+ if (ResExport == LEXP.ERR_FILE_EXIST) :
357 # Launch Dialog window to ask if export file can be overide
358 msg =_("File %s already exists. Replace file ?") % videopath
359 ForceExport = G_DIALOG.Dialog.QuestionMessage(self._dialog,msg)
360
361=== added directory 'lucioLib/lcl_export'
362=== added file 'lucioLib/lcl_export/__init__.py'
363--- lucioLib/lcl_export/__init__.py 1970-01-01 00:00:00 +0000
364+++ lucioLib/lcl_export/__init__.py 2010-01-04 17:42:19 +0000
365@@ -0,0 +1,24 @@
366+#!/usr/bin/env python
367+# -*- coding: utf-8 -*-
368+# -*- Mode: Python -*-
369+# vi:si:ai:et:sw=4:sts=4:ts=4
370+#
371+#
372+# Copyright Nicolas Bertrand (nico@inattendu.org), 2009
373+#
374+# This file is part of Luciole.
375+#
376+# Luciole is free software: you can redistribute it and/or modify
377+# it under the terms of the GNU General Public License as published by
378+# the Free Software Foundation, either version 3 of the License, or
379+# (at your option) any later version.
380+#
381+# Luciole is distributed in the hope that it will be useful,
382+# but WITHOUT ANY WARRANTY; without even the implied warranty of
383+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
384+# GNU General Public License for more details.
385+#
386+# You should have received a copy of the GNU General Public License
387+# along with Luciole. If not, see <http://www.gnu.org/licenses/>.
388+#
389+#
390
391=== added file 'lucioLib/lcl_export/lcl_export_cinelerra.py'
392--- lucioLib/lcl_export/lcl_export_cinelerra.py 1970-01-01 00:00:00 +0000
393+++ lucioLib/lcl_export/lcl_export_cinelerra.py 2010-01-04 17:42:19 +0000
394@@ -0,0 +1,114 @@
395+#!/usr/bin/env python
396+# -*- coding: utf-8 -*-
397+# -*- Mode: Python -*-
398+# vi:si:ai:et:sw=4:sts=4:ts=4
399+#
400+#
401+# Copyright Nicolas Bertrand (nico@inattendu.org), 2010
402+#
403+# This file is part of Luciole.
404+#
405+# Luciole is free software: you can redistribute it and/or modify
406+# it under the terms of the GNU General Public License as published by
407+# the Free Software Foundation, either version 3 of the License, or
408+# (at your option) any later version.
409+#
410+# Luciole is distributed in the hope that it will be useful,
411+# but WITHOUT ANY WARRANTY; without even the implied warranty of
412+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
413+# GNU General Public License for more details.
414+#
415+# You should have received a copy of the GNU General Public License
416+# along with Luciole. If not, see <http://www.gnu.org/licenses/>.
417+#
418+#
419+
420+# for i18n
421+from gettext import gettext as _
422+
423+import os.path
424+
425+import lcl_export_tool_base as LETBASE
426+
427+
428+class lcl_export_cinelerra (LETBASE.luciole_export_tool):
429+ """
430+ class to export luciole project in cinelerra format
431+ """
432+
433+ """ ATTRIBUTES
434+ N/A
435+ """
436+
437+ def __init__(self, lcl_project = "./luciole_project.xml", template="./template.xml", export_file = "./export.xml"):
438+ """
439+ initialization of class lcl_export_cinelerra
440+
441+ @param object lcl_project : Luciole project or file
442+ @param string template : Path to template
443+ @param string export_file : Path to export_file
444+ """
445+ super(lcl_export_cinelerra, self).__init__(lcl_project,template,export_file )
446+
447+ def _gen_ressources(self):
448+ """
449+ ressource is called asset in cinelerra template
450+ """
451+ resources_tag = self._template_xml.find("//ASSETS")
452+ resource_elem_tpl = self._template_xml.find("//ASSET")
453+
454+ for image in self._lcl_project_data['resources_images'] :
455+ # build image_path
456+ image_p = os.path.join(self._lcl_project_data['image_path'], image)
457+ # create a resource element --> i.e an ASSET element
458+ new_element = self._template_xml.SubElement(resources_tag,"ASSET",{"SRC":image_p})
459+ # copy subelement ASSET template tag in new element
460+ for elem_tpl in resource_elem_tpl.getchildren() :
461+ new_element.append(elem_tpl)
462+
463+ #remove template tag
464+ resources_tag.remove(resource_elem_tpl)
465+
466+ def _gen_timeline(self):
467+ """
468+ generate timeline
469+ """
470+ (timeline_tag, timeline_item_tpl) = self.__get_timeline_tags()
471+ for image in self._lcl_project_data['timelines_images'] :
472+ # build image_path
473+ image_p = os.path.join(self._lcl_project_data['image_path'], image)
474+
475+ # create New EDIT element per image
476+ for frame in range(self._lcl_project_data['fpi']):
477+ framerate = str(self._lcl_project_data['fpi'])
478+ new_element = self._template_xml.SubElement(timeline_tag,"EDIT",{"STARTSOURCE":"0","CHANNEL":"0", "LENGTH":"1"})
479+ # create FILE subElement of EDIT
480+ self._template_xml.SubElement(new_element,"FILE",{"SRC":image_p})
481+
482+ # remove EDIT template tag
483+ timeline_tag.remove(timeline_item_tpl)
484+
485+ #
486+ # class PRIVATE methods
487+ #
488+ def __get_timeline_tags(self) :
489+ """
490+ in cinelerra timeline tags is TRACK with type video, and EDITS/EDIT tag
491+ """
492+ video_track = None
493+ tracks = self._template_xml.findall("//TRACK")
494+ for track in tracks :
495+ if track.get("TYPE") == "VIDEO" :
496+ # video track is found
497+ video_track = track
498+ break
499+ # get EDIT tag
500+ return ( video_track.find("EDITS"), video_track.find("EDITS/EDIT"))
501+
502+
503+
504+
505+if __name__ == '__main__' :
506+ X = lcl_export_cinelerra( lcl_project = "/home/nico/temp/testLuciole/luciole_project_isight/luciole_project_isight.xml",template="./cinelerra_template.xml",export_file = "./cine_export.xml" )
507+ X.generate()
508+
509
510=== added file 'lucioLib/lcl_export/lcl_export_kdenlive.py'
511--- lucioLib/lcl_export/lcl_export_kdenlive.py 1970-01-01 00:00:00 +0000
512+++ lucioLib/lcl_export/lcl_export_kdenlive.py 2010-01-04 17:42:19 +0000
513@@ -0,0 +1,289 @@
514+#!/usr/bin/env python
515+# -*- coding: utf-8 -*-
516+# -*- Mode: Python -*-
517+# vim:si:ai:et:sw=4:sts=4:ts=4
518+#
519+#
520+# Copyright Nicolas Bertrand (nico@inattendu.org), 2010
521+#
522+# This file is part of Luciole.
523+#
524+# Luciole is free software: you can redistribute it and/or modify
525+# it under the terms of the GNU General Public License as published by
526+# the Free Software Foundation, either version 3 of the License, or
527+# (at your option) any later version.
528+#
529+# Luciole is distributed in the hope that it will be useful,
530+# but WITHOUT ANY WARRANTY; without even the implied warranty of
531+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
532+# GNU General Public License for more details.
533+#
534+# You should have received a copy of the GNU General Public License
535+# along with Luciole. If not, see <http://www.gnu.org/licenses/>.
536+#
537+#
538+
539+# for i18n
540+from gettext import gettext as _
541+
542+import os.path
543+import subprocess as SP
544+import os
545+import stat
546+
547+import lcl_export_tool_base as LETBASE
548+
549+from .. import luciole_constants as LCONST
550+
551+class lcl_export_kdenlive (LETBASE.luciole_export_tool):
552+ """
553+ class to export luciole project in cinelerra format
554+ kdenlive files are xml files based on MLT framework
555+
556+ """
557+
558+ """ ATTRIBUTES
559+ @___element_id : Xml tag id for kdenlive export file
560+ @__list_id_producer : List of kdenlive producer tag's id
561+ @__total_duration : Total duration, in frame, or movie in timeline
562+
563+ """
564+
565+ """ CONSTANTS
566+ """
567+ __PRODUCER_AUDIO_MAX = "0"
568+ __PRODUCER_VIDEO_MAX = "0"
569+ __PRODUCER_CHANNELS = "0"
570+ __PRODUCER_DEFAULT_AUDIO = "0"
571+ __PRODUCER_FRAME_SIZE = str(LCONST.VIDEO_PAL_RES[0])+"x"+ str(LCONST.VIDEO_PAL_RES[1])
572+ __PRODUCER_FREQUENCY = "0"
573+ __PRODUCER_IN = "0"
574+ __PRODUCER_ASPECT_RATIO = "1.000000"
575+ __PRODUCER_TYPE ="5"
576+ __PRODUCER_DEFAULT_VIDEO = "0"
577+ __PRODUCER_POS = 4
578+ __PLAYLIST_ID = "lcl_track"
579+
580+ __META_HOMEDIR = os.path.expandvars('$HOME')
581+ __META_PROJECFOLDER = os.path.join(os.path.expandvars('$HOME'),"kdenlive")
582+ __META_PROJECT_NAME = "Submission from luciole"
583+
584+
585+
586+
587+ def __init__(self, lcl_project = "./luciole_project.xml", template="templates/kdenlive_template.kdenlive", export_file = "./export.xptv"):
588+ """
589+ initialization of class lcl_export_kdenlive
590+
591+ @param object lcl_project : luciole project or file
592+ @param string template : Path to template
593+ @param string export_file : Path to export_file
594+ @return :
595+ @author
596+ """
597+ super(lcl_export_kdenlive, self).__init__(lcl_project,template,export_file )
598+
599+ self.__element_id = 1
600+ self.__list_id_producer = []
601+ self.__total_duration = 0
602+
603+ def _gen_meta(self):
604+ """
605+ generate meta information for kdenlive file
606+ """
607+ mlt_tag = self._template_xml.getroot()
608+ mlt_tag.attrib["root"] = self.__META_HOMEDIR
609+ mlt_tag.attrib["title"] = self.__META_PROJECT_NAME
610+ kdenlivedoc = self._template_xml.find("//kdenlivedoc")
611+ kdenlivedoc.attrib["projectfolder"] = self.__META_PROJECFOLDER
612+
613+ def _gen_ressources(self):
614+ """
615+ resources is called <kdenlive_producer> and is stored in <kdenlive_doc>
616+ """
617+ resources_tag = self._template_xml.find("//kdenlivedoc")
618+
619+ duration = self.__calc_duration( self._lcl_project_data['fpi'])
620+ for image in self._lcl_project_data['resources_images'] :
621+ # build image_path
622+ image_p = os.path.join(self._lcl_project_data['image_path'], image)
623+ # create a resource element --> i.e a kdenlive_producer element
624+
625+ new_element = self._template_xml.SubElement(resources_tag,"kdenlive_producer",
626+ {"audio_max": self.__PRODUCER_AUDIO_MAX,
627+ "channels" : self.__PRODUCER_CHANNELS,
628+ "duration" : str(duration),
629+ "default_audio" : self.__PRODUCER_DEFAULT_AUDIO,
630+ "video_max" : self.__PRODUCER_VIDEO_MAX,
631+ "frame_size" : self.__PRODUCER_FRAME_SIZE,
632+ "frequency" : self.__PRODUCER_FREQUENCY,
633+ "in" : self.__PRODUCER_IN,
634+ "file_size" : self.__calc_file_size(image_p),
635+ "aspect_ratio" : self.__PRODUCER_ASPECT_RATIO,
636+ "out" : str(duration-1),
637+ "file_hash" : self.__calc_file_hash(image_p),
638+ "type" : self.__PRODUCER_TYPE,
639+ "id" : self.__new_element_id(),
640+ "name" : image,
641+ "default_video" : self.__PRODUCER_DEFAULT_VIDEO,
642+ "resource" : image_p,
643+ })
644+
645+ def _gen_timeline(self):
646+ """
647+ The timeline in kdenlive is divided in 3 steps : the <producer> tag, the <playlist>, and <tractor>
648+ """
649+ self.__gen_producer()
650+ self.__gen_playlist()
651+ self.__gen_tractor()
652+
653+
654+ #
655+ # class PRIVATE methods
656+ #
657+
658+ def __new_element_id(self):
659+ """ generates an element id """
660+ element_id = self.__element_id
661+ self.__element_id += 1
662+ return str(element_id)
663+
664+ def __calc_file_hash(self,file) :
665+ """ compute the md5sum of a file.
666+ These function needs robustness in cas on non success of md5sum command"""
667+ output =SP.Popen(["md5sum",file], stdout=SP.PIPE).communicate()[0]
668+ # output is a srting who looks like : hash_code file
669+ # return only the hashcode
670+ return output.split()[0]
671+
672+ def __calc_file_size(self,file) :
673+ """ compute the file size """
674+ fsz = os.stat(file)[stat.ST_SIZE]
675+ return str(fsz)
676+
677+ def __calc_duration(self,fpi) :
678+ """ kdenlive does not support ressource with duration less then 3 frames ...."""
679+ fr = int(fpi)
680+ if fr < 3 : fr =3
681+ return fr
682+
683+ def __get_timeline_video_tags(self) :
684+ """
685+ in pitivi timeline tag is <timeline>
686+ video is on <track>/<stream> with typepitivi.stream.VideoStream, """
687+ video_track = None
688+ tracks = self._template_xml.findall("//track")
689+ for track in tracks:
690+ stream = track.find("stream")
691+ if stream.attrib["type"] == self.STREAM_TYPE :
692+ video_track = track
693+ break
694+ return video_track
695+
696+
697+ def __find_black_track(self) :
698+ """ get back track play list. return the entry element """
699+ pl_found = None
700+ for pl in self._template_xml.findall("//playlist") :
701+ if pl.attrib["id"] == "black_track" :
702+ pl_found = pl
703+ break
704+ return pl_found.find("entry")
705+
706+ def __find_kdenlive_producer_id(self,image) :
707+ """ find the id of a <kdenlive_producer> tag """
708+ kd_producer_tags = self._template_xml.findall("//kdenlive_producer")
709+ id_found = None
710+ for kd_producer_tag in kd_producer_tags :
711+ if kd_producer_tag.attrib['name'] == image :
712+ id_found = kd_producer_tag.attrib['id']
713+ # raise error if id not found
714+ return id_found
715+
716+ def __gen_proprety_tag(self,producer_tag,name,text) :
717+ """ generate property tag """
718+ prop_tag = self._template_xml.SubElement(producer_tag,"property",{
719+ "name":name})
720+ prop_tag.text = text
721+
722+
723+ def __gen_producer_properties(self,producer_tag, image_p) :
724+ """ properties of producer tag. Dirty generation. """
725+ self.__gen_proprety_tag(producer_tag,"mlt_type","producer")
726+ self.__gen_proprety_tag(producer_tag,"aspect_ratio","1")
727+ self.__gen_proprety_tag(producer_tag,"length","15000")
728+ self.__gen_proprety_tag(producer_tag,"eof","pause")
729+ self.__gen_proprety_tag(producer_tag,"resource",image_p)
730+ self.__gen_proprety_tag(producer_tag,"ttl","25")
731+ self.__gen_proprety_tag(producer_tag,"progressive","1")
732+ self.__gen_proprety_tag(producer_tag,"mlt_service","pixbuf")
733+
734+
735+ def __gen_producer(self) :
736+ """ <producer> tag have attributes and property subtag.
737+ Producer tag is a son of <mlt> tag """
738+ mlt_tag = self._template_xml.getroot()
739+ producer_pos = self.__PRODUCER_POS
740+
741+ duration = str(self._lcl_project_data['fpi'])
742+ # loop on luciole project timeline images
743+ for (index,image) in enumerate(self._lcl_project_data['timelines_images']) :
744+ id_kd_producer = self.__find_kdenlive_producer_id(image)
745+ if id_kd_producer != None :
746+ # create the tag
747+ producer_tag= self._template_xml.Element("producer")
748+ producer_tag.set("in",self.__PRODUCER_IN)
749+ producer_tag.set("out",duration)
750+ producer_tag.set("id",id_kd_producer)
751+ mlt_tag.insert(producer_pos,producer_tag)
752+
753+ # create <property> tags
754+ image_p = os.path.join(self._lcl_project_data['image_path'], image)
755+ self.__gen_producer_properties(producer_tag, image_p)
756+
757+ # remember the id for playlist generation
758+ self.__list_id_producer.append(id_kd_producer)
759+
760+ #increment position for next element
761+ producer_pos += 1
762+
763+ def __gen_playlist_entry(self,pl_tag,out,producer) :
764+ """ playlist entry tag"""
765+ self._template_xml.SubElement(pl_tag,"entry",{
766+ "in":self.__PRODUCER_IN,
767+ "out":out,
768+ "producer":producer})
769+
770+
771+ def __gen_playlist(self) :
772+ """ generate playlist """
773+ mlt_tag = self._template_xml.getroot()
774+
775+ # get playlist tag
776+ playlists_tag = self._template_xml.findall("//playlist")
777+ playlist_tag = None
778+ for tag in playlists_tag :
779+ if tag.attrib["id"] == self.__PLAYLIST_ID :
780+ playlist_tag = tag
781+ # generate entry tag and compute duration of all timeline
782+ if playlist_tag != None :
783+ # compute duration
784+ duration = int(self._lcl_project_data['fpi'])
785+
786+ for id_p in self.__list_id_producer :
787+ self.__gen_playlist_entry(playlist_tag,str(duration),id_p)
788+ self.__total_duration += duration
789+
790+ self.__total_duration = self.__total_duration -1
791+
792+ def __gen_tractor(self) :
793+ """ modify tractor tag"""
794+ tractor_tag = self._template_xml.find("tractor")
795+ tractor_tag.attrib['out'] = str(self.__total_duration)
796+
797+ #update duration in black_track playlist
798+ pl_entry=self.__find_black_track()
799+ pl_entry.attrib["out"] = str(self.__total_duration)
800+
801+
802+
803
804=== added file 'lucioLib/lcl_export/lcl_export_pitivi.py'
805--- lucioLib/lcl_export/lcl_export_pitivi.py 1970-01-01 00:00:00 +0000
806+++ lucioLib/lcl_export/lcl_export_pitivi.py 2010-01-04 17:42:19 +0000
807@@ -0,0 +1,199 @@
808+#!/usr/bin/env python
809+# -*- coding: utf-8 -*-
810+# -*- Mode: Python -*-
811+# vim:si:ai:et:sw=4:sts=4:ts=4
812+#
813+#
814+# Copyright Nicolas Bertrand (nico@inattendu.org), 2010
815+#
816+# This file is part of Luciole.
817+#
818+# Luciole is free software: you can redistribute it and/or modify
819+# it under the terms of the GNU General Public License as published by
820+# the Free Software Foundation, either version 3 of the License, or
821+# (at your option) any later version.
822+#
823+# Luciole is distributed in the hope that it will be useful,
824+# but WITHOUT ANY WARRANTY; without even the implied warranty of
825+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
826+# GNU General Public License for more details.
827+#
828+# You should have received a copy of the GNU General Public License
829+# along with Luciole. If not, see <http://www.gnu.org/licenses/>.
830+#
831+#
832+
833+# for i18n
834+from gettext import gettext as _
835+
836+import os.path
837+
838+import lcl_export_tool_base as LETBASE
839+
840+
841+class lcl_export_pitivi (LETBASE.luciole_export_tool):
842+ """
843+ class to export luciole project in pitivi format
844+
845+ """
846+
847+ """ ATTRIBUTES
848+ @__element_id : Xml tag id for pitivi export file
849+
850+ """
851+
852+ """ CONSTANTS
853+ """
854+ # hard coded value in pitivi template
855+ __SOURCE_DURATION ="18446744073709551615"
856+ # pitivi type
857+ __SOURCE_TYPE = "pitivi.factories.file.PictureFileSourceFactory"
858+
859+ __STREAM_CAPS = "video/x-raw-yuv, format=(fourcc)I420, width=(int)720, height=(int)576, framerate=(fraction)0/1"
860+ __STREAM_NAME = "src0"
861+ __STREAM_TYPE = "pitivi.stream.VideoStream"
862+
863+ __BASE_DURATION_NS = 1000000000
864+
865+ __TIMELINE_TYPE = "pitivi.timeline.track.SourceTrackObject"
866+ __TIMELINE_IN_POINT = "(gint64)0"
867+ __TIMELINE_PRIORITY = "(int)0"
868+
869+
870+
871+ def __init__(self, lcl_project = "./luciole_project.xml", template="./pitivi_template.xptv", export_file = "./export.xptv"):
872+ """
873+ initialization of class lcl_export_pitivi
874+
875+ @param object lcl_project : luciole project or file
876+ @param string template : Path to template
877+ @param string export_file : Path to export_file
878+ """
879+ super(lcl_export_pitivi, self).__init__(lcl_project,template,export_file )
880+
881+
882+ #
883+ # Attributes init.
884+ #
885+ self.__element_id = 0
886+
887+ def _gen_ressources(self):
888+ """
889+ heritage method
890+ resources is called sources in pitivi
891+ """
892+ resources_tag = self._template_xml.find("//sources")
893+
894+ for image in self._lcl_project_data['resources_images'] :
895+ # build image_path
896+ image_p = os.path.join(self._lcl_project_data['image_path'], image)
897+ image_p = "file://"+image_p
898+ # create a resource element --> i.e a source element
899+ new_element = self._template_xml.SubElement(resources_tag,"source",
900+ {"filename":image_p,
901+ "type":self.__SOURCE_TYPE,
902+ "duration":self.__SOURCE_DURATION,
903+ "id":self.__new_element_id(),
904+ "default_duration":self.__calc_duration(int(self._lcl_project_data['fpi']))
905+ })
906+
907+ # create <output-stream> and <stream> sub element
908+ new_output_stream = self._template_xml.SubElement(new_element,"output-streams")
909+ new_stream = self._template_xml.SubElement(new_output_stream,"stream",
910+ {"caps":self.__STREAM_CAPS,
911+ "name":self.__STREAM_NAME,
912+ "id":self.__new_element_id(),
913+ "type":self.__STREAM_TYPE
914+ })
915+
916+ def _gen_timeline(self):
917+ """
918+ heritage method
919+ generate timeline :
920+ in pitivi each image is a track object with ref to <ourrce>
921+
922+ """
923+ # get video track on timeline
924+ video_track = self.__get_timeline_video_tags()
925+
926+ # create <track-objects> for each resource
927+ track_objects = video_track.find('track-objects')
928+
929+ # loop on luciole project timeline images
930+ for (index,image) in enumerate(self._lcl_project_data['timelines_images']) :
931+
932+ # compute duration and start point
933+ track_duration = int( self.__calc_duration(int(self._lcl_project_data['fpi'])))
934+ start_point = index * int(track_duration)
935+
936+
937+ # create a track_object
938+ new_track_object = self._template_xml.SubElement(track_objects,"track-object",
939+ { "id":self.__new_element_id(),
940+ "type": self.__TIMELINE_TYPE,
941+ "duration" :self.__convert_to_gint64(track_duration),
942+ "media_duration" : self.__convert_to_gint64(track_duration),
943+ "start": self.__convert_to_gint64(start_point),
944+ "in_point": self.__TIMELINE_IN_POINT,
945+ "priority": self.__TIMELINE_PRIORITY
946+ })
947+
948+ # get and create ref tags with sources
949+ (factory_id, stream_id) = self.__find_track_id(image)
950+ self._template_xml.SubElement(new_track_object,"factory-ref", { "id":factory_id})
951+ self._template_xml.SubElement(new_track_object,"stream-ref", { "id":stream_id})
952+
953+
954+ #
955+ # class PRIVATE methods
956+ #
957+ def __new_element_id(self):
958+ """ generates an element id """
959+ element_id = self.__element_id
960+ self.__element_id += 1
961+ return str(element_id)
962+
963+ def __calc_duration(self,fpi) :
964+ """ compute duration in pitivi format.
965+ Input is the fpi : number of frame per image
966+ """
967+ duration = (1.0/fpi)*self.__BASE_DURATION_NS
968+ # use of str and int : int for a rounded value and str for string in element tree
969+ return str(int(duration))
970+
971+ def __get_timeline_video_tags(self) :
972+ """
973+ in pitivi timeline tag is <timeline>
974+ video is on <track>/<stream> with typepitivi.stream.VideoStream
975+ """
976+ video_track = None
977+ tracks = self._template_xml.findall("//track")
978+ for track in tracks:
979+ stream = track.find("stream")
980+ if stream.attrib["type"] == self.__STREAM_TYPE :
981+ video_track = track
982+ break
983+ return video_track
984+
985+ def __convert_to_gint64(self,value) :
986+ """ convert to gint64 format. Only str conversion"""
987+ return "(gint64)"+str(value)
988+
989+ def __find_track_id(self,image) :
990+ """ find image ids for pitivi according image name """
991+ sources = self._template_xml.findall("//source")
992+ (source_id, stream_id) = (None,None)
993+ for source in sources :
994+ filename = source.attrib["filename"]
995+ filename = filename.split('/')[-1]
996+ if filename == image :
997+ # get source id
998+ source_id = source.attrib['id']
999+ # find stream id
1000+ stream_id = source.find('output-streams/stream').attrib['id']
1001+ break
1002+ return (source_id, stream_id)
1003+
1004+
1005+
1006+
1007
1008=== added file 'lucioLib/lcl_export/lcl_export_tool_base.py'
1009--- lucioLib/lcl_export/lcl_export_tool_base.py 1970-01-01 00:00:00 +0000
1010+++ lucioLib/lcl_export/lcl_export_tool_base.py 2010-01-04 17:42:19 +0000
1011@@ -0,0 +1,203 @@
1012+#!/usr/bin/env python
1013+# -*- coding: utf-8 -*-
1014+# -*- Mode: Python -*-
1015+# vim:si:ai:et:sw=4:sts=4:ts=4
1016+#
1017+#
1018+# Copyright Nicolas Bertrand (nico@inattendu.org), 2010
1019+#
1020+# This file is part of Luciole.
1021+#
1022+# Luciole is free software: you can redistribute it and/or modify
1023+# it under the terms of the GNU General Public License as published by
1024+# the Free Software Foundation, either version 3 of the License, or
1025+# (at your option) any later version.
1026+#
1027+# Luciole is distributed in the hope that it will be useful,
1028+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1029+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1030+# GNU General Public License for more details.
1031+#
1032+# You should have received a copy of the GNU General Public License
1033+# along with Luciole. If not, see <http://www.gnu.org/licenses/>.
1034+#
1035+#
1036+"""
1037+lcl_export_tool_base.py
1038+Base export to tool definition and class
1039+
1040+"""
1041+# for i18n
1042+from gettext import gettext as _
1043+
1044+import os.path
1045+from .. import luciole_et as LE
1046+from .. import luciole_exceptions as LEXCEP
1047+from .. import luciole_project as LPROJECT
1048+
1049+class luciole_export_tool(object) :
1050+ """
1051+ Base class for export to tool
1052+ """
1053+
1054+
1055+ """ ATTRIBUTES
1056+ @_export_file : Path to file to export
1057+ @_template : Path to template
1058+ @_lcl_project : The luciole project a file or a dict
1059+ @_lcl_project_data : luciole project data dictionary filled with ecport data
1060+ @_export_template : File to update with export data
1061+ @_template_xml : the export template loaded as xml file
1062+ """
1063+
1064+
1065+
1066+ def __init__(self, lcl_project = "./luciole_project.xml", template="./template.xml", export_file = "./export.xml"):
1067+ """
1068+ @param lcl_project : luciole project
1069+ @param template : Path to template
1070+ @param export_file : Path to export_file
1071+ """
1072+ #
1073+ # Atributes init
1074+ #
1075+ self._lcl_project = lcl_project
1076+ self._template = template
1077+ self._export_file = export_file
1078+ self._lcl_project_data = {}
1079+ self._lcl_project_data['resources_images'] = []
1080+ self._lcl_project_data['timelines_images'] = []
1081+
1082+ self._export_template = None
1083+ self._template_xml = None
1084+
1085+ if (type(lcl_project) == str) :
1086+ # test file type
1087+ if os.path.exists(lcl_project) :
1088+ #filetype exits load it
1089+ self.__load_lcl_file()
1090+ else :
1091+ #Raise Error
1092+ excep_message = "%s is ot a file",lcl_project
1093+ raise LEXCEP.LucioException,excep_message
1094+ elif (type(lcl_project) == LPROJECT.project_dico) :
1095+ # input is a dictionary
1096+ self.__load_lcl_dict()
1097+ else :
1098+ # template does not exists : raise an exception
1099+ excep_message = "Invalid projetc type nor file nor luciole_project"
1100+ raise LEXCEP.LucioException,excep_message
1101+
1102+ #
1103+ # Test template
1104+ #
1105+ if os.path.exists(self._template) :
1106+ self._load_template()
1107+ else :
1108+ # template does not exists : raise an exception
1109+ excep_message = " Template file %s does not exist"%self._template
1110+ raise LEXCEP.LucioException,excep_message
1111+
1112+
1113+
1114+
1115+ def generate(self):
1116+ """
1117+
1118+
1119+ @return :
1120+ @author
1121+ """
1122+ self._gen_meta()
1123+ self._gen_ressources()
1124+ self._gen_timeline()
1125+ self.__write_export_file()
1126+
1127+ def _load_template(self):
1128+ """
1129+
1130+
1131+ @return :
1132+ @author
1133+ """
1134+
1135+ self._template_xml = LE.lcl_et(self._template)
1136+
1137+ def _gen_ressources(self):
1138+ """
1139+ generate date for resources
1140+
1141+ @return :
1142+ @author
1143+ """
1144+ pass
1145+
1146+ def _gen_timeline(self):
1147+ """
1148+ Generate data for timeline
1149+
1150+ @return :
1151+ @author
1152+ """
1153+ pass
1154+
1155+ def _gen_meta(self) :
1156+ """
1157+ Generate meta information for project
1158+
1159+ @return :
1160+ @author
1161+ """
1162+ pass
1163+
1164+
1165+
1166+ def __load_lcl_file(self):
1167+ """
1168+ Load a Luciole project from a file dictionary
1169+
1170+ @return :
1171+ @author
1172+ """
1173+ try :
1174+ _lcl_project_xml = LE.lcl_et(self._lcl_project)
1175+ except :
1176+ pass
1177+ else :
1178+ self._lcl_project_data['image_path'] = os.path.join(_lcl_project_xml.findtext("metas/projectPath").strip() ,
1179+ _lcl_project_xml.findtext("metas/rush_dir").strip() )
1180+ self._lcl_project_data['fpi'] = int(_lcl_project_xml.findtext("metas/fpi").strip())
1181+
1182+ # get resources images from project capture data
1183+ list_image = _lcl_project_xml.find("captureData").getiterator('image')
1184+ [ self._lcl_project_data['resources_images'].append(image.text.strip()) for image in list_image]
1185+
1186+ # get timelines images from project chrono data
1187+ list_image = _lcl_project_xml.find("chronoData").getiterator('image')
1188+ [self._lcl_project_data['timelines_images'].append(image.text.strip()) for image in list_image]
1189+
1190+
1191+ def __load_lcl_dict(self):
1192+ """
1193+ Load a Luciole project from a dictionary
1194+
1195+ @return :
1196+ @author
1197+ """
1198+
1199+ self._lcl_project_data['image_path'] = os.path.join(self._lcl_project['project_dir'], self._lcl_project['rush_dir'])
1200+ self._lcl_project_data['fpi'] = int(self._lcl_project['fpi'])
1201+ self._lcl_project_data['resources_images'] = self._lcl_project['capture_images']
1202+ self._lcl_project_data['timelines_images'] = self._lcl_project['chrono_images']
1203+
1204+
1205+ def __write_export_file(self):
1206+ """
1207+
1208+
1209+ @return :
1210+ @author
1211+ """
1212+ self._template_xml.write(self._export_file)
1213+
1214+
1215
1216=== added file 'lucioLib/lcl_export/lcl_export_video.py'
1217--- lucioLib/lcl_export/lcl_export_video.py 1970-01-01 00:00:00 +0000
1218+++ lucioLib/lcl_export/lcl_export_video.py 2010-01-04 17:42:19 +0000
1219@@ -0,0 +1,656 @@
1220+#!/usr/bin/env python
1221+# -*- coding: utf-8 -*-
1222+# -*- Mode: Python -*-
1223+# vim:si:ai:et:sw=4:sts=4:ts=4
1224+#
1225+#
1226+# Copyright Nicolas Bertrand (nico@inattendu.org), 2009
1227+#
1228+# This file is part of Luciole.
1229+#
1230+# Luciole is free software: you can redistribute it and/or modify
1231+# it under the terms of the GNU General Public License as published by
1232+# the Free Software Foundation, either version 3 of the License, or
1233+# (at your option) any later version.
1234+#
1235+# Luciole is distributed in the hope that it will be useful,
1236+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1237+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1238+# GNU General Public License for more details.
1239+#
1240+# You should have received a copy of the GNU General Public License
1241+# along with Luciole. If not, see <http://www.gnu.org/licenses/>.
1242+#
1243+#
1244+
1245+import subprocess as SP
1246+import os
1247+import os.path
1248+import dircache
1249+import fnmatch
1250+import string
1251+import tempfile
1252+import dircache
1253+import re
1254+
1255+from .. import luciole_tools as MT
1256+from .. import luciole_exceptions as LEXCEP
1257+
1258+import gobject
1259+import threading
1260+import Queue
1261+import time
1262+
1263+# for i18n
1264+from gettext import gettext as _
1265+
1266+
1267+import os
1268+import signal
1269+
1270+(IMAGE_LIST,IMAGE_DIR)=range(2)
1271+ERR_FILE_EXIST = 1
1272+(EXPORT_DV,EXPORT_DVD,EXPORT_XVID) = range(3)
1273+
1274+# Mencoder commands for export , according export type
1275+dictPal= {
1276+ #EXPORT_DV : "-vf scale=720:576 -ofps 25 -ovc lavc -oac pcm",
1277+ EXPORT_DV : "-target pal-dv -aspect 4:3",
1278+ EXPORT_DVD : "-vf scale=720:576 -ofps 25 -oac pcm -ovc lavc -lavcopts vcodec=mpeg2video:vbitrate=9600:aspect=4/3",
1279+ EXPORT_XVID : "-vf scale=720:576 -ofps 25 -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=2000:aspect=4/3 -oac mp3lame -lameopts cbr:br=128",
1280+}
1281+
1282+dictEncoder= {
1283+ "PAL":dictPal,
1284+ }
1285+
1286+
1287+class MyThreadConvertToYuv(threading.Thread):
1288+ """ Thread class for convert a sequence of image to a YUV file """
1289+
1290+ def _get_abort(self): return self.__abort
1291+ def _set_abort(self, value):
1292+ if value in (True,False) : self.__abort = value
1293+ abort = property(_get_abort, _set_abort, None, "abort thread work")
1294+
1295+
1296+ def __init__(self,imageInputType,imageDir,imageList,ppmtoy4m_opts,framesPerImage,tmpDir,VideoName,finish_queue,export_gui_obj):
1297+ """ Thread class initialisation """
1298+ super(MyThreadConvertToYuv, self).__init__()
1299+ self.__imageInputType = imageInputType
1300+ self.__imageDir = imageDir
1301+ self.__imageList = imageList
1302+ self.__ppmtoy4m_opts = ppmtoy4m_opts
1303+ self.__framesPerImage = framesPerImage
1304+ self.__tmpDir = tmpDir
1305+ self.__VideoName = VideoName
1306+ self.__queue= finish_queue
1307+
1308+
1309+ self._export_gui_obj = export_gui_obj
1310+ self._export_gui_obj.progress_bar_text = _("Pass 1/2")
1311+ self._export_gui_obj.progress_bar_fraction = 0
1312+
1313+ self.__abort = False # reset abort value
1314+
1315+ def _on_finish(self, file_path,abort):
1316+ """Callback function. Raised when conversions is finished"""
1317+ #preparation of next pass if no abort
1318+ if abort == False :
1319+ self._export_gui_obj.progress_bar_text = _("Pass 2/2")
1320+ self._export_gui_obj.progress_bar_fraction = 0.0
1321+ # TBD : missing update of file path in export obj
1322+ self.__abort = False # reset abort value
1323+ return False
1324+
1325+ def _on_progress(self, value):
1326+ """Callback function. used to know the conversion progress """
1327+ self._export_gui_obj.progress_bar_fraction = value
1328+ return False
1329+
1330+ def _convertToYuv(self) :
1331+ """ This memthod is used to convert the sequence of image in a raw
1332+ video in YUV4MPEG format. This method use external tools as
1333+ imagemagick and tools (ppmtoy4m) from mjpegtools suite."""
1334+ # Get first the list of image To co_nvert is depending of the type
1335+
1336+ if self.__imageInputType == IMAGE_DIR :
1337+ # input images are stored in a directory
1338+ imagesToConv = MT.filesInDir(self.__imageDir,"*.jpeg")
1339+ else :
1340+ # input images are in a list
1341+ imagesToConv = self.__imageList
1342+ # copmute the number of frames to genarate = Number of images * nf of frame per image
1343+ nbFrames = len(imagesToConv)*self.__framesPerImage
1344+ self.__ppmtoy4m_opts['-n'] = str(nbFrames) #update the numner of frames for
1345+
1346+ video_temp_base=os.path.join(self.__tmpDir,self.__VideoName)
1347+ # build ppmtoy4m_cmd
1348+ ppmtoy4m_cmd = ""
1349+ for key,value in self.__ppmtoy4m_opts.items() :
1350+ mystring = " "+ key
1351+ if value : mystring = mystring + " "+value
1352+ ppmtoy4m_cmd = mystring + ppmtoy4m_cmd
1353+ ppmtoy4m_cmd = 'ppmtoy4m'+ppmtoy4m_cmd
1354+
1355+ # launch ppmtoy4m, this process run during all the image convesrion
1356+ # and receive in stdin images converted in ppm format
1357+ # the ppm images are sent to this subprocess by the convert operation
1358+ # see p2 below
1359+ fd5 = os.open(video_temp_base+".yuv",os.O_WRONLY|os.O_CREAT)
1360+ fd6 = os.open(video_temp_base+".log",os.O_WRONLY|os.O_CREAT)
1361+
1362+ ppm_proc = SP.Popen(ppmtoy4m_cmd, shell=True, stdin=SP.PIPE,stdout=fd5,stderr=fd6)
1363+ frame_cpt = 0
1364+ for (count_image,image) in enumerate(imagesToConv) :
1365+ # check abort
1366+ if self.__abort == True : break
1367+ #loop on images to convert
1368+ #conversion and rezizing of cuurent image
1369+ imagePath = os.path.join(self.__imageDir,image)
1370+ montage_cmd = "montage -type TrueColor -quality 100 -geometry 720x576 "+imagePath+ " " + video_temp_base+".jpg"
1371+ pMontage =SP.call(montage_cmd,shell=True)
1372+ convert_cmd = "convert -type TrueColor -quality 100 "+video_temp_base+".jpg " +video_temp_base+".pnm "
1373+ pconvert =SP.call(convert_cmd,shell=True)
1374+
1375+ for i in range(self.__framesPerImage) :
1376+ # check abort
1377+ if self.__abort == True : break
1378+ frame_cpt = frame_cpt +1
1379+ convert_cmd = "convert -depth 8 ppm:"+video_temp_base+".pnm -"
1380+ p1 = SP.Popen(convert_cmd,shell=True, stdout=ppm_proc.stdin,stderr=SP.PIPE)
1381+ p1.wait()
1382+ # progress bar update
1383+ progression = ((count_image+1.0)/len(imagesToConv))
1384+ gobject.idle_add(self._on_progress,progression)
1385+ os.fsync(fd5)
1386+ os.close(fd5)
1387+ os.close(fd6)
1388+ #return the path to yuv file
1389+ return video_temp_base+".yuv"
1390+
1391+ def run(self) :
1392+ """ Thread start --> launch images to video conversion """
1393+ yuvfile_path = self._convertToYuv()
1394+ self.__queue.put(yuvfile_path)
1395+ gobject.idle_add(self._on_finish,yuvfile_path,self.__abort)
1396+
1397+
1398+
1399+class MyExportThread(threading.Thread):
1400+ """ Export Thread. Thread in charge to enconde video in Yuv format
1401+ to DV, DVD or XVID format
1402+ """
1403+
1404+ def _get_abort(self): return self.__abort
1405+ def _set_abort(self, value):
1406+ if value in (True,False) : self.__abort = value
1407+ abort = property(_get_abort, _set_abort, None, "abort thread work")
1408+
1409+ def __init__(self, exportType, videoOutPath, yuv_queue, export_gui_obj) :
1410+ """ Init export video Thread"""
1411+ super(MyExportThread, self).__init__()
1412+ self.__abort = False
1413+ (self._exportType,self._videoInPath,self._videoOutPath) = (exportType,None,videoOutPath)
1414+ self.__queue = yuv_queue
1415+ self._export_gui_obj = export_gui_obj
1416+
1417+ def _on_finish(self,abort):
1418+ """Callback function. Raised when conversions is finished"""
1419+ # update gui progress bar
1420+ if abort == False :
1421+ # Terminated normaly
1422+ self._export_gui_obj.progress_bar_text = _("Export Done")
1423+ self._export_gui_obj.progress_bar_fraction = 1.0
1424+ else :
1425+ self._export_gui_obj.progress_bar_text = _("Export Canceled")
1426+ self._export_gui_obj.progress_bar_fraction = 0.0
1427+ self.__abort = False # reset abort
1428+ return False
1429+
1430+ def _on_progress(self, value):
1431+ """Callback function. used to know the conversion progress """
1432+ self._export_gui_obj.progress_bar_fraction = value
1433+ return False
1434+
1435+ def run(self):
1436+ """ Thread execution --> generate export """
1437+ """ Build and launch ffmpeg command. """
1438+ yuv_is_finished=False
1439+ data = None
1440+ while (yuv_is_finished == False) :
1441+ time.sleep(0.1)
1442+ try :
1443+ data=self.__queue.get(block=False)
1444+ yuv_is_finished=True
1445+ except Queue.Empty :
1446+ pass
1447+ # check abort if true leave it
1448+ if self.__abort == True :
1449+ data = None
1450+ break
1451+ if data :
1452+ self._videoInPath = data
1453+ if self._exportType == EXPORT_DV :
1454+ self._ffmpeg_launcher()
1455+ else :
1456+ self._mencoder_launcher()
1457+ gobject.idle_add(self._on_finish,self.__abort)
1458+
1459+ def _ffmpeg_launcher(self):
1460+ """ Video conversion with ffmpeg : only used for DV conversion """
1461+ self.__videoFormat = "PAL" # only PAL is supported
1462+ self.__withSound = False # no sound by default
1463+ if ( (self._exportType == EXPORT_DV) and ( self._videoInPath ) and ( self._videoOutPath ) ) :
1464+ #
1465+ # Build ffmpeg command
1466+ #
1467+ ffmpeg_cmd = []
1468+ ffmpeg_cmd.append("ffmpeg")
1469+ # add input video name
1470+ ffmpeg_cmd.append('-i')
1471+ ffmpeg_cmd.append(self._videoInPath)
1472+ # add video conversion options
1473+ coding_command = dictEncoder["PAL"][EXPORT_DV]
1474+ for ffmpeg_w in coding_command.split() : ffmpeg_cmd.append(ffmpeg_w)
1475+ # add output video name
1476+ ffmpeg_cmd.append(self._videoOutPath)
1477+
1478+ #
1479+ # launch ffmpeg subprocess
1480+ #
1481+ print "[",self.__class__.__name__,"] ","command :", ffmpeg_cmd
1482+ sb_encoder =SP.Popen(ffmpeg_cmd,stdout=SP.PIPE,stderr=SP.PIPE)
1483+
1484+ #
1485+ # check encoding process and finish
1486+ #
1487+ encode_not_finish = True
1488+ pulse_counter = 0
1489+ pulse_modulo = 20
1490+ while encode_not_finish == True:
1491+ ret_code = sb_encoder.poll()
1492+ if type(ret_code) == int :
1493+ # encoding is finished when a code is returned
1494+ encode_not_finish = False
1495+ if pulse_counter % pulse_modulo == 0 :
1496+ # update progress bar only with pulses
1497+ gobject.idle_add(self._on_progress, 2.0 )
1498+ pulse_counter = pulse_counter +1
1499+
1500+ def _mencoder_launcher(self) :
1501+ """ Video conversion with mencoder : used for convestion in DVD and XVID format"""
1502+ self.__videoFormat = "PAL" # only PAL is supported
1503+ self.__withSound = False # no sound by default
1504+ if ( (self._exportType in (EXPORT_DVD,EXPORT_XVID)) and ( self._videoInPath ) and ( self._videoOutPath ) ) :
1505+ #
1506+ # Build mencoder command
1507+ #
1508+ mencoder_cmd =[]
1509+ mencoder_cmd.append("mencoder")
1510+ # add export video options
1511+ if self.__videoFormat == "PAL" :
1512+ coding_command = dictEncoder["PAL"][self._exportType]
1513+ for mencoder_w in coding_command.split() : mencoder_cmd.append(mencoder_w)
1514+ #finally add the input and output :
1515+ mencoder_cmd.append(self._videoInPath)
1516+ mencoder_cmd.append('-o')
1517+ mencoder_cmd.append(self._videoOutPath)
1518+
1519+ #
1520+ # launch mencoder command
1521+ #
1522+ print "[",self.__class__.__name__,"] ","command :", mencoder_cmd
1523+ sb_encoder =SP.Popen(mencoder_cmd,stdout=SP.PIPE,stderr=SP.PIPE)
1524+
1525+ #
1526+ # check encoding process and finish
1527+ #
1528+ encode_not_finish = True
1529+ res_value = 0
1530+ res_value_old = 0
1531+ while encode_not_finish == True:
1532+
1533+ # First loop until child is finished
1534+ buffer = []
1535+ ret_code = sb_encoder.poll()
1536+ if type(ret_code) == int :
1537+ encode_not_finish = False
1538+ while True:
1539+ # second loop to detect a line
1540+ # remark : I have no success to use readline or readlines
1541+ # missing some characters
1542+
1543+ # mencoder display info on stdout,
1544+ # read one char to no be blocked until eof
1545+ char = sb_encoder.stdout.readline(1)
1546+ if not char : break
1547+ else :
1548+ if char == '\r':
1549+ aLine = string.join( buffer, '' )
1550+ buffer = []
1551+ break
1552+ else:
1553+ buffer.append(char)
1554+ #extact Frame num
1555+ #print aLine
1556+ #Exemple line :
1557+ #Pos: 19.0s 475f (99%) 30.68fps Trem: 0min 65mb A-V:0.000 [28800:0]
1558+ # regeexp for select percent value here : 99.
1559+ # value in parenthis ; with one or digits; select only the number
1560+ pattern = re.compile("\(\s*([0-9]+)\%\)")
1561+ # get Match_object
1562+ # cf tuto : http://docs.python.org/howto/regex.html#regex-howto
1563+ re_res = pattern.search(aLine)
1564+ if re_res :
1565+ # result is at first index of groups method
1566+ # divide by 100 to have a value in range 0 .. 100
1567+ res_value = re_res.groups()[0]
1568+ if res_value != res_value_old :
1569+ # if send message only when percentage value change
1570+ # avoid sending unuseful message
1571+ gobject.idle_add(self._on_progress, float(res_value)/100.0 )
1572+ res_value_old = res_value
1573+ # check if abort requested
1574+ if self.__abort == True :
1575+ pid = sb_encoder.pid
1576+ os.kill(pid, signal.SIGTERM)
1577+ time.sleep(1.0)
1578+ # check if is process really dead
1579+ if not isinstance(sb_encoder.poll(),int) :
1580+ os.kill(pid,signal.SIGKILL)
1581+ encode_not_finish = False
1582+
1583+
1584+
1585+class lcl_export_video(object) :
1586+ """ Class in charge of managing exports. The class use threads for conversions"""
1587+
1588+ _exportType = (EXPORT_DV,EXPORT_DVD,EXPORT_XVID)
1589+
1590+ _suffixList =(".dv",".mpeg2",".avi")
1591+
1592+ def _get_imageDir(self): return self.__imageDir
1593+ def _set_imageDir(self, value): self.__imageDir = value
1594+ def _del_imageDir(self): del self.__imageDir
1595+ imageDir = property(_get_imageDir, _set_imageDir, _del_imageDir, "Image's directory. ")
1596+
1597+ def _get_imageList(self): return self.__imageList
1598+ def _set_imageList(self, value): self.__imageList = value
1599+ def _del_imageList(self): del self.__imageList
1600+ imageList = property(_get_imageList, _set_imageList, _del_imageList, "Image's list to encode. This list should be sorted. images are encodede in list order. ")
1601+
1602+ def _get_imageInputType(self): return self.__imageInputType
1603+ def _set_imageInputType(self, value):
1604+ if value in (IMAGE_LIST,IMAGE_DIR) :
1605+ self.__imageInputType = value
1606+ else :
1607+ print "[",self.__class__.__name__,"] ",value," not in correct type"
1608+ def _del_imageInputType(self): del self.__imageInputType
1609+ imageInputType = property(_get_imageInputType, _set_imageInputType, _del_imageInputType, "Image's input type ")
1610+
1611+ def _get_VideoName(self): return self.__VideoName
1612+ def _set_VideoName(self, value): self.__VideoName = value
1613+ def _del_VideoName(self): del self.__VideoName
1614+ VideoName = property(_get_VideoName, _set_VideoName, _del_VideoName, "Image's directory. ")
1615+
1616+ def _get_videoDir(self): return self.__videoDir
1617+ def _set_videoDir(self, value): self.__videoDir = value
1618+ def _del_videoDir(self): del self.__videoDir
1619+ videoDir = property(_get_videoDir, _set_videoDir, _del_videoDir, "Image's directory. ")
1620+
1621+ def _get_framesPerImage(self): return self.__framesPerImage
1622+ def _set_framesPerImage(self, value): self.__framesPerImage = value
1623+ def _del_framesPerImage(self): del self.__framesPerImage
1624+ framesPerImage = property(_get_framesPerImage, _set_framesPerImage, _del_framesPerImage, "Number of frames per image. ")
1625+
1626+ def _get_outputFPS(self): return self.__outputFPS
1627+ def _set_outputFPS(self, value): self.__outputFPS = value
1628+ def _del_outputFPS(self): del self.__outputFPS
1629+ outputFPS = property(_get_outputFPS, _set_outputFPS, _del_outputFPS, "Image's directory. ")
1630+
1631+ def _get_videoFormat(self): return self.__videoFormat
1632+ def _set_videoFormat(self, value):
1633+ self.__videoFormat = value
1634+ #set values for PAL format
1635+ if self.__videoFormat == "PAL" :
1636+ self.__ppmtoy4m_opts['F'] = "-F 25:1" # framerate - see man ppmtoy4m
1637+ self.__ppmtoy4m_opts['A'] = "-A 59:54" # aspect ratio - see man ppmtoy4m
1638+ def _del_videoFormat(self): del self.__videoFormat
1639+ videoFormat = property(_get_videoFormat, _set_videoFormat, _del_videoFormat, "Image's directory. ")
1640+
1641+ def _get_videoType(self): return self.__videoType
1642+ def _set_videoType(self, value): self.__videoType = value
1643+ def _del_videoType(self): del self.__videoType
1644+ videoType = property(_get_videoType, _set_videoType, _del_videoType, "Image's directory. ")
1645+
1646+ def _get_videoAspect(self): return self.__videoAspect
1647+ def _set_videoAspect(self, value): self.__videoAspect = value
1648+ def _del_videoAspect(self): del self.__videoAspect
1649+ videoAspect = property(_get_videoAspect, _set_videoAspect, _del_videoAspect, "Image's directory. ")
1650+
1651+ def _get_withSound(self): return self.__withSound
1652+ def _set_withSound(self, value): self.__withSound = value
1653+ def _del_withSound(self): del self.__withSound
1654+ withSound = property(_get_withSound, _set_withSound, _del_withSound, "Image's directory. ")
1655+
1656+ def _get_export_on_progress(self):
1657+ """ export status is knowed by the status of the last expôpt thread """
1658+ if self._t_encoder and self._t_encoder.isAlive() == True :
1659+ self._export_on_progress = True
1660+ else :
1661+ self._export_on_progress = False
1662+ return self._export_on_progress
1663+ export_on_progress = property(_get_export_on_progress, None, None, "export status")
1664+
1665+
1666+
1667+ def __init__(self,tmp_dir, export_gui_obj):
1668+ """Init of class"""
1669+ self.__tmpDir = os.path.join(tmp_dir,"export")
1670+ self._cleanTmpDir()
1671+
1672+ self.__imageList = list() # input image list
1673+ self.__imageInputType = IMAGE_LIST # image type input is imageList by default
1674+
1675+ self.__VideoName = "export"
1676+ self.__outputFPS = "25"
1677+ self.__videoFormat = "PAL"
1678+ self.__videoType = "DV"
1679+ self.__videoAspect ="4/3"
1680+
1681+ # set ppmtoy4m options
1682+ self.__ppmtoy4m_opts=dict()
1683+ self.__ppmtoy4m_opts['-F'] = "25:1" # framerate - see man ppmtoy4m
1684+ self.__ppmtoy4m_opts['-A'] = "59:54" # aspectRatio - see man ppmtoy4m
1685+ self.__ppmtoy4m_opts['-S'] = "420mpeg2" # subsamplimg chroma mode - see man ppmtoy4m
1686+ self.__ppmtoy4m_opts['-I'] = "p" # interlacing mode,progressive, non-interlaced - see man ppmtoy4m
1687+ self.__ppmtoy4m_opts['-n'] = "0" # total output frames - see man ppmtoy4m
1688+ self.__ppmtoy4m_opts['-v'] = "2" # verbosity - see man ppmtoy4m
1689+
1690+ self.__framesPerImage=10 # default value for frame Per image
1691+
1692+ self.__withSound = False # no sound by default
1693+
1694+ self._export_on_progress = False # export progression
1695+ self._t_yuv = None
1696+ self._t_encoder = None
1697+ self._videopath = None
1698+ self.__imageDir = None
1699+ if self.__videoFormat == "PAL":
1700+ self.__videoRes=(720,576)
1701+ # export gui obj
1702+ self._export_gui_obj = export_gui_obj
1703+
1704+
1705+
1706+ def export(self, export_data ,forceExport=False) :
1707+ """ Export function
1708+ exportData is a dict with the following items :
1709+ 'image_input' = type of image put list or dir (IMAGE_LIST,IMAGE_DIR)
1710+ 'image_list' = List of image to convert, each element is an absolute path yo the image
1711+ or
1712+ 'image_dir' = dir with all the images to impot : Not implemented
1713+ 'export_dir' = the directory where the video will be exported
1714+ 'video_name' = the export video name wihout extension
1715+ 'export_type' = the type of export (EXPORT_DV,EXPORT_DVD,EXPORT_XVID)
1716+ 'fpi' = the frame rate
1717+ """
1718+
1719+
1720+ (VideoExsists,videopath) = self._IsVideoExists(export_data)
1721+ if ( ( VideoExsists == False) or (forceExport == True)
1722+ and
1723+ ( videopath != None ) ) :
1724+ # check tmp dir is clean
1725+ self._cleanTmpDir()
1726+ self._export_on_progress = True
1727+
1728+ # initiate queue for comunication between yuv converter and export converter
1729+ self._dataQueue=Queue.Queue()
1730+ self._ResQueue=Queue.Queue()
1731+
1732+ #
1733+ # Check input parameters
1734+ #
1735+ print " DEBUG : export - ",export_data
1736+ export_is_valid = False
1737+ if export_data.has_key('image_input') :
1738+ self.__imageInputType = export_data['image_input']
1739+ export_is_valid = True
1740+
1741+ # check type of input validity
1742+ if(
1743+ (export_is_valid == True )
1744+ and
1745+ (
1746+ (
1747+ export_data.has_key('image_list')
1748+ and
1749+ export_data['image_input'] == IMAGE_LIST
1750+ )
1751+ or
1752+ (
1753+ export_data.has_key('image_dir')
1754+ and
1755+ ( export_data['image_input'] == IMAGE_DIR )
1756+ ) )) :
1757+
1758+ if export_data.has_key('image_list') : self.__imageList = export_data['image_list']
1759+ if export_data.has_key('image_dir') : self.__imageList = export_data['image_dir']
1760+ export_is_valid = True
1761+ else :
1762+ export_is_valid = False
1763+
1764+ if (export_is_valid == True) and (export_data.has_key('fpi')) :
1765+ self.__framesPerImage = int(export_data['fpi'])
1766+ export_is_valid = True
1767+ else :
1768+ export_is_valid = False
1769+
1770+ if (export_is_valid == True) and (export_data.has_key('video_name')) :
1771+ self.__VideoName = export_data['video_name']
1772+ export_is_valid = True
1773+ else :
1774+ export_is_valid = False
1775+
1776+ if (export_is_valid == True) and os.path.exists(videopath) :
1777+ try :
1778+ os.remove(videopath)
1779+ except OSError,err :
1780+ print _('Unable to erase'),err.filename
1781+ print err.strerror
1782+ videopath = None
1783+ export_is_valid = False
1784+
1785+
1786+ if export_is_valid == True :
1787+ # start yuv converter sub process
1788+ self._t_yuv = MyThreadConvertToYuv(self.__imageInputType,self.__imageDir,self.__imageList,self.__ppmtoy4m_opts,self.__framesPerImage,self.__tmpDir,self.__VideoName,self._dataQueue, self._export_gui_obj)
1789+ self._t_yuv.start()
1790+ # start video export encoder subprocess
1791+ self._videopath = videopath
1792+ self._t_encoder = MyExportThread(export_data['export_type'], self._videopath, self._dataQueue, self._export_gui_obj)
1793+ self._t_encoder.start()
1794+ else :
1795+ return (0,videopath)
1796+
1797+ if ( VideoExsists == True ) :
1798+ return (ERR_FILE_EXIST,videopath)
1799+ else :
1800+ return (0,videopath)
1801+
1802+ def cancel_export(self):
1803+ """ Cancel export """
1804+ if self._export_on_progress == True :
1805+ # kill the eport Threads
1806+ if self._t_yuv.isAlive() : self._t_yuv.abort= True
1807+ if self._t_encoder.isAlive() : self._t_encoder.abort= True
1808+ #check if process remains
1809+ while ( self._t_yuv.isAlive() or self._t_encoder.isAlive() ) :
1810+ time.sleep(0.1)
1811+ # clean temporary files
1812+ self._cleanTmpDir()
1813+ try :
1814+ os.remove(self._videopath )
1815+ print self._videopath ," is removed"
1816+ except OSError,err :
1817+ print "Impossible d effacer ",err.filename
1818+ print err.strerror
1819+
1820+
1821+
1822+ ################################################################################
1823+ # private methods
1824+ ################################################################################
1825+ def _cleanTmpDir(self) :
1826+ """ Clean or create tmp dir if needed"""
1827+ if not os.path.exists(self.__tmpDir) :
1828+ #create it
1829+ try :
1830+ os.makedirs(self.__tmpDir)
1831+ except OSError,err :
1832+ print "Impossible de creer le repertoire export"
1833+ print err.strerror
1834+ self.__tmpDir = "/tmp"
1835+ else :
1836+ # directory exist, clean it
1837+ list = dircache.listdir( self.__tmpDir)
1838+ for file in list :
1839+ filePath = os.path.join(self.__tmpDir,file)
1840+ try :
1841+ os.remove(filePath)
1842+ print filePath ," is removed"
1843+ except OSError,err :
1844+ print "Impossible d effacer ",err.filename
1845+ print err.strerror
1846+
1847+ def _IsVideoExists(self,export_data):
1848+ """ test if a video path exists """
1849+ exists = False
1850+ video_path = None
1851+ # check export type
1852+ if export_data.has_key('export_type') and export_data['export_type'] in self._exportType :
1853+ suffix = self._suffixList[export_data['export_type']]
1854+ # check export video name
1855+ if export_data.has_key('video_name') :
1856+ video_name = export_data['video_name']+suffix
1857+
1858+ # check export dir
1859+ if not os.path.exists(export_data['export_dir']) :
1860+ #create it
1861+ try :
1862+ MT.mkdirs(export_data['export_dir'])
1863+ except LEXCEP.LucioException , err :
1864+ # to robustify : Error handling
1865+ print err
1866+ return (exists, video_path)
1867+ video_path = os.path.join(export_data['export_dir'], video_name)
1868+
1869+ # check if video path exists
1870+ if os.path.exists(video_path) :
1871+ exists = True
1872+ return (exists,video_path)
1873+
1874+
1875+
1876
1877=== removed directory 'lucioLib/lucioExport'
1878=== removed file 'lucioLib/lucioExport/__init__.py'
1879--- lucioLib/lucioExport/__init__.py 2009-12-03 08:11:32 +0000
1880+++ lucioLib/lucioExport/__init__.py 1970-01-01 00:00:00 +0000
1881@@ -1,26 +0,0 @@
1882-#!/usr/bin/env python
1883-# -*- coding: utf-8 -*-
1884-# -*- Mode: Python -*-
1885-# vi:si:ai:et:sw=4:sts=4:ts=4
1886-#
1887-#
1888-# Copyright Nicolas Bertrand (nico@inattendu.org), 2009
1889-#
1890-# This file is part of Luciole.
1891-#
1892-# Luciole is free software: you can redistribute it and/or modify
1893-# it under the terms of the GNU General Public License as published by
1894-# the Free Software Foundation, either version 3 of the License, or
1895-# (at your option) any later version.
1896-#
1897-# Luciole is distributed in the hope that it will be useful,
1898-# but WITHOUT ANY WARRANTY; without even the implied warranty of
1899-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1900-# GNU General Public License for more details.
1901-#
1902-# You should have received a copy of the GNU General Public License
1903-# along with Luciole. If not, see <http://www.gnu.org/licenses/>.
1904-#
1905-#
1906-from luciole_export import *
1907-from luciole_export_tool import *
1908
1909=== removed file 'lucioLib/lucioExport/lcl_export_pitivi.py'
1910--- lucioLib/lucioExport/lcl_export_pitivi.py 2010-01-04 17:42:19 +0000
1911+++ lucioLib/lucioExport/lcl_export_pitivi.py 1970-01-01 00:00:00 +0000
1912@@ -1,217 +0,0 @@
1913-#!/usr/bin/env python
1914-# -*- coding: utf-8 -*-
1915-# -*- Mode: Python -*-
1916-# vim:si:ai:et:sw=4:sts=4:ts=4
1917-#
1918-#
1919-# Copyright Nicolas Bertrand (nico@inattendu.org), 2009
1920-#
1921-# This file is part of Luciole.
1922-#
1923-# Luciole is free software: you can redistribute it and/or modify
1924-# it under the terms of the GNU General Public License as published by
1925-# the Free Software Foundation, either version 3 of the License, or
1926-# (at your option) any later version.
1927-#
1928-# Luciole is distributed in the hope that it will be useful,
1929-# but WITHOUT ANY WARRANTY; without even the implied warranty of
1930-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1931-# GNU General Public License for more details.
1932-#
1933-# You should have received a copy of the GNU General Public License
1934-# along with Luciole. If not, see <http://www.gnu.org/licenses/>.
1935-#
1936-#
1937-
1938-# for i18n
1939-from gettext import gettext as _
1940-
1941-import os.path
1942-
1943-import lcl_export_tool_base as LETBASE
1944-
1945-from .. import luciole_et as LE
1946-from .. import luciole_exceptions as LEXCEP
1947-from .. import luciole_project as LPROJECT
1948-
1949-class lcl_export_pitivi (LETBASE.luciole_export_tool):
1950-
1951- """
1952-
1953-
1954- :version:
1955- :author:
1956- """
1957-
1958- """ ATTRIBUTES
1959-
1960-
1961-
1962- _export_template (private)
1963-
1964- """
1965-
1966- """ CONSTANTS
1967- """
1968- # hard coded value in pitivi tamplate
1969- SOURCE_DURATION ="18446744073709551615"
1970- # pitivi type
1971- SOURCE_TYPE = "pitivi.factories.file.PictureFileSourceFactory"
1972-
1973- STREAM_CAPS = "video/x-raw-yuv, format=(fourcc)I420, width=(int)720, height=(int)576, framerate=(fraction)0/1"
1974- STREAM_NAME = "src0"
1975- STREAM_TYPE = "pitivi.stream.VideoStream"
1976-
1977- BASE_DURATION_NS = 1000000000
1978-
1979- TIMELINE_TYPE = "pitivi.timeline.track.SourceTrackObject"
1980- TIMELINE_IN_POINT = "(gint64)0"
1981- TIMELINE_PRIORITY = "(int)0"
1982-
1983-
1984-
1985- def __init__(self, lcl_project = "./luciole_project.xml", template="./pitivi_template.xptv", export_file = "./export.xptv"):
1986- """
1987-
1988-
1989- @param object lcl_project : luciole project or file
1990- @param string template : Path to template
1991- @param string export_file : Path to export_file
1992- @return :
1993- @author
1994- """
1995- super(lcl_export_pitivi, self).__init__(lcl_project,template,export_file )
1996-
1997- self._element_id = 0
1998- self._stream_id = 1
1999-
2000- def _new_element_id(self):
2001- """ generates an element id """
2002- element_id = self._element_id
2003- self._element_id += 1
2004- return str(element_id)
2005-
2006- def _new_stream_id(self):
2007- """ generates an element id """
2008- stream_id = self._stream_id
2009- self._stream_id += 1
2010- return str(stream_id)
2011-
2012-
2013- def _calc_duration(self,fpi) :
2014- """ compute duration in pitivi format.
2015- Input is the fpi : number of frame per image
2016- """
2017- duration = (1.0/fpi)*self.BASE_DURATION_NS
2018- # use of str and int : int for a rounded value and str for string in element tree
2019- return str(int(duration))
2020-
2021-
2022- def _load_template(self):
2023- """
2024- """
2025- self._template_xml = LE.lcl_et(self.template)
2026-
2027-
2028- def _gen_ressources(self):
2029- """
2030- ressources is called sources in pitivi
2031- """
2032- resources_tag = self._template_xml.find("//sources")
2033-
2034- for image in self._lcl_project_data['resources_images'] :
2035- # build image_path
2036- image_p = os.path.join(self._lcl_project_data['image_path'], image)
2037- image_p = "file://"+image_p
2038- # create a resouce element --> i.e a source element
2039- new_element = self._template_xml.SubElement(resources_tag,"source",
2040- {"filename":image_p,
2041- "type":self.SOURCE_TYPE,
2042- "duration":self.SOURCE_DURATION,
2043- "id":self._new_element_id(),
2044- "default_duration":self._calc_duration(int(self._lcl_project_data['fpi']))
2045- })
2046-
2047- # create <output-stream> and <stream> sub element
2048- new_output_stream = self._template_xml.SubElement(new_element,"output-streams")
2049- new_stream = self._template_xml.SubElement(new_output_stream,"stream",
2050- {"caps":self.STREAM_CAPS,
2051- "name":self.STREAM_NAME,
2052- "id":self._new_element_id(),
2053- "type":self.STREAM_TYPE
2054- })
2055-
2056-
2057- def __get_timeline_video_tags(self) :
2058- """
2059- in pitivi timeline tag is <timeline>
2060- video is on <track>/<stream> with typepitivi.stream.VideoStream, """
2061-
2062-
2063- video_track = None
2064- tracks = self._template_xml.findall("//track")
2065- for track in tracks:
2066- stream = track.find("stream")
2067- if stream.attrib["type"] == self.STREAM_TYPE :
2068- video_track = track
2069- break
2070- return video_track
2071-
2072- def __convert_to_gint64(self,value) :
2073- return "(gint64)"+str(value)
2074-
2075- def __find_track_id(self,image) :
2076- """ find image ids for pitivi avcorrding image name """
2077- sources = self._template_xml.findall("//source")
2078- (source_id, stream_id) = (None,None)
2079- for source in sources :
2080- filename = source.attrib["filename"]
2081- filename = filename.split('/')[-1]
2082- if filename == image :
2083- # get source id
2084- source_id = source.attrib['id']
2085- # find stream id
2086- stream_id = source.find('output-streams/stream').attrib['id']
2087- break
2088-
2089- return (source_id, stream_id)
2090-
2091-
2092-
2093- def _gen_timeline(self):
2094- """
2095- generate timeline :
2096- in pitivi each image is a track object with ref to <ourrce>
2097-
2098- """
2099- # get video track on timeline
2100- video_track = self.__get_timeline_video_tags()
2101-
2102- # create <track-objects> for each resource
2103- track_objects = video_track.find('track-objects')
2104-
2105- # loop on luciiole project timeline images
2106- for (index,image) in enumerate(self._lcl_project_data['timelines_images']) :
2107-
2108- # compute duration and start point
2109- track_duration = int( self._calc_duration(int(self._lcl_project_data['fpi'])))
2110- start_point = index * int(track_duration)
2111-
2112-
2113- # create a track_object
2114- new_track_object = self._template_xml.SubElement(track_objects,"track-object",
2115- { "id":self._new_element_id(),
2116- "type": self.TIMELINE_TYPE,
2117- "duration" :self.__convert_to_gint64(track_duration),
2118- "media_duration" : self.__convert_to_gint64(track_duration),
2119- "start": self.__convert_to_gint64(start_point),
2120- "in_point": self.TIMELINE_IN_POINT,
2121- "priority": self.TIMELINE_PRIORITY
2122- })
2123-
2124- # get and create ref tags with sources
2125- (factory_id, stream_id) = self.__find_track_id(image)
2126- self._template_xml.SubElement(new_track_object,"factory-ref", { "id":factory_id})
2127- self._template_xml.SubElement(new_track_object,"stream-ref", { "id":stream_id})
2128-
2129-
2130
2131=== removed file 'lucioLib/lucioExport/lcl_export_tool_base.py'
2132--- lucioLib/lucioExport/lcl_export_tool_base.py 2010-01-04 17:42:19 +0000
2133+++ lucioLib/lucioExport/lcl_export_tool_base.py 1970-01-01 00:00:00 +0000
2134@@ -1,188 +0,0 @@
2135-#!/usr/bin/env python
2136-# -*- coding: utf-8 -*-
2137-# -*- Mode: Python -*-
2138-# vim:si:ai:et:sw=4:sts=4:ts=4
2139-#
2140-#
2141-# Copyright Nicolas Bertrand (nico@inattendu.org), 2009
2142-#
2143-# This file is part of Luciole.
2144-#
2145-# Luciole is free software: you can redistribute it and/or modify
2146-# it under the terms of the GNU General Public License as published by
2147-# the Free Software Foundation, either version 3 of the License, or
2148-# (at your option) any later version.
2149-#
2150-# Luciole is distributed in the hope that it will be useful,
2151-# but WITHOUT ANY WARRANTY; without even the implied warranty of
2152-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2153-# GNU General Public License for more details.
2154-#
2155-# You should have received a copy of the GNU General Public License
2156-# along with Luciole. If not, see <http://www.gnu.org/licenses/>.
2157-#
2158-#
2159-
2160-# for i18n
2161-from gettext import gettext as _
2162-
2163-import os.path
2164-from .. import luciole_et as LE
2165-
2166-from .. import luciole_exceptions as LEXCEP
2167-from .. import luciole_project as LPROJECT
2168-class luciole_export_tool(object) :
2169- """
2170- :version:
2171- :author:
2172- """
2173-
2174-
2175- """
2176- ATTRIBUTES
2177- export_file : Path to file to export
2178- template : Path to template
2179- lcl_project : The luciole project a file or a dict
2180- _lcl_project_data :
2181- _export_template : File to update with export data
2182- _template_xml :
2183- """
2184-
2185-
2186-
2187- def __init__(self, lcl_project = "./luciole_project.xml", template="./template.xml", export_file = "./export.xml"):
2188- """
2189- @param lcl_project : Type de projet
2190- @param template : Path to template
2191- @param export_file : Path to export_file
2192- """
2193- #
2194- # Atributes init
2195- #
2196- self.lcl_project = lcl_project
2197- self.template = template
2198- self.export_file = export_file
2199- self._lcl_project_data = {}
2200- self._lcl_project_data['resources_images'] = []
2201- self._lcl_project_data['timelines_images'] = []
2202-
2203- self._export_template = None
2204- self._template_xml = None
2205-
2206- if (type(lcl_project) == str) :
2207- # test file type
2208- if os.path.exists(lcl_project) :
2209- #filetype exits load it
2210- self.__load_lcl_file()
2211- else :
2212- #Raise Error
2213- excep_message = "%s is ot a file",lcl_project
2214- raise LEXCEP.LucioException,excep_message
2215- elif (type(lcl_project) == LPROJECT.project_dico) :
2216- # input is a dictionary
2217- self.__load_lcl_dict()
2218- else :
2219- # template does not exists : raise an exception
2220- excep_message = "Invalid projetc type nor file nor luciole_project"
2221- raise LEXCEP.LucioException,excep_message
2222-
2223- #
2224- # Test template
2225- #
2226- if os.path.exists(self.template) :
2227- self._load_template()
2228- else :
2229- # template does not exists : raise an exception
2230- excep_message = " Template file %s does not exist"%self.template
2231- raise LEXCEP.LucioException,excep_message
2232-
2233-
2234-
2235-
2236- def generate(self):
2237- """
2238-
2239-
2240- @return :
2241- @author
2242- """
2243- self._gen_ressources()
2244- self._gen_timeline()
2245- self.__write_export_file()
2246-
2247- def _load_template(self):
2248- """
2249-
2250-
2251- @return :
2252- @author
2253- """
2254- pass
2255-
2256- def _gen_ressources(self):
2257- """
2258- generate date for resources
2259-
2260- @return :
2261- @author
2262- """
2263- pass
2264-
2265- def _gen_timeline(self):
2266- """
2267- Generate data for timeline
2268-
2269- @return :
2270- @author
2271- """
2272- pass
2273-
2274- def __load_lcl_file(self):
2275- """
2276- Load a Luciole project from a file dictionary
2277-
2278- @return :
2279- @author
2280- """
2281- try :
2282- _lcl_project_xml = LE.lcl_et(self.lcl_project)
2283- except :
2284- pass
2285- else :
2286- self._lcl_project_data['image_path'] = os.path.join(_lcl_project_xml.findtext("metas/projectPath").strip() ,
2287- _lcl_project_xml.findtext("metas/rush_dir").strip() )
2288- self._lcl_project_data['fpi'] = int(_lcl_project_xml.findtext("metas/fpi").strip())
2289-
2290- # get resources images from project capture data
2291- list_image = _lcl_project_xml.find("captureData").getiterator('image')
2292- [ self._lcl_project_data['resources_images'].append(image.text.strip()) for image in list_image]
2293-
2294- # get timelines images from project chrono data
2295- list_image = _lcl_project_xml.find("chronoData").getiterator('image')
2296- [self._lcl_project_data['timelines_images'].append(image.text.strip()) for image in list_image]
2297-
2298-
2299- def __load_lcl_dict(self):
2300- """
2301- Load a Luciole project from a dictionary
2302-
2303- @return :
2304- @author
2305- """
2306-
2307- self._lcl_project_data['image_path'] = os.path.join(self.lcl_project['project_dir'], self.lcl_project['rush_dir'])
2308- self._lcl_project_data['fpi'] = int(self.lcl_project['fpi'])
2309- self._lcl_project_data['resources_images'] = self.lcl_project['capture_images']
2310- self._lcl_project_data['timelines_images'] = self.lcl_project['chrono_images']
2311-
2312-
2313- def __write_export_file(self):
2314- """
2315-
2316-
2317- @return :
2318- @author
2319- """
2320- self._template_xml.write(self.export_file)
2321-
2322-
2323
2324=== removed file 'lucioLib/lucioExport/luciole_export.py'
2325--- lucioLib/lucioExport/luciole_export.py 2009-11-23 06:28:27 +0000
2326+++ lucioLib/lucioExport/luciole_export.py 1970-01-01 00:00:00 +0000
2327@@ -1,656 +0,0 @@
2328-#!/usr/bin/env python
2329-# -*- coding: utf-8 -*-
2330-# -*- Mode: Python -*-
2331-# vi:si:ai:et:sw=4:sts=4:ts=4
2332-#
2333-#
2334-# Copyright Nicolas Bertrand (nico@inattendu.org), 2009
2335-#
2336-# This file is part of Luciole.
2337-#
2338-# Luciole is free software: you can redistribute it and/or modify
2339-# it under the terms of the GNU General Public License as published by
2340-# the Free Software Foundation, either version 3 of the License, or
2341-# (at your option) any later version.
2342-#
2343-# Luciole is distributed in the hope that it will be useful,
2344-# but WITHOUT ANY WARRANTY; without even the implied warranty of
2345-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2346-# GNU General Public License for more details.
2347-#
2348-# You should have received a copy of the GNU General Public License
2349-# along with Luciole. If not, see <http://www.gnu.org/licenses/>.
2350-#
2351-#
2352-
2353-import subprocess as SP
2354-import os
2355-import os.path
2356-import dircache
2357-import fnmatch
2358-import string
2359-import tempfile
2360-import dircache
2361-import re
2362-
2363-from .. import luciole_tools as MT
2364-from .. import luciole_exceptions as LEXCEP
2365-
2366-import gobject
2367-import threading
2368-import Queue
2369-import time
2370-
2371-# for i18n
2372-from gettext import gettext as _
2373-
2374-
2375-import os
2376-import signal
2377-
2378-(IMAGE_LIST,IMAGE_DIR)=range(2)
2379-ERR_FILE_EXIST = 1
2380-(EXPORT_DV,EXPORT_DVD,EXPORT_XVID) = range(3)
2381-
2382-# Mencoder commands for export , according export type
2383-dictPal= {
2384- #EXPORT_DV : "-vf scale=720:576 -ofps 25 -ovc lavc -oac pcm",
2385- EXPORT_DV : "-target pal-dv -aspect 4:3",
2386- EXPORT_DVD : "-vf scale=720:576 -ofps 25 -oac pcm -ovc lavc -lavcopts vcodec=mpeg2video:vbitrate=9600:aspect=4/3",
2387- EXPORT_XVID : "-vf scale=720:576 -ofps 25 -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=2000:aspect=4/3 -oac mp3lame -lameopts cbr:br=128",
2388-}
2389-
2390-dictEncoder= {
2391- "PAL":dictPal,
2392- }
2393-
2394-
2395-class MyThreadConvertToYuv(threading.Thread):
2396- """ Thread class for convert a sequence of image to a YUV file """
2397-
2398- def _get_abort(self): return self.__abort
2399- def _set_abort(self, value):
2400- if value in (True,False) : self.__abort = value
2401- abort = property(_get_abort, _set_abort, None, "abort thread work")
2402-
2403-
2404- def __init__(self,imageInputType,imageDir,imageList,ppmtoy4m_opts,framesPerImage,tmpDir,VideoName,finish_queue,export_gui_obj):
2405- """ Thread class initialisation """
2406- super(MyThreadConvertToYuv, self).__init__()
2407- self.__imageInputType = imageInputType
2408- self.__imageDir = imageDir
2409- self.__imageList = imageList
2410- self.__ppmtoy4m_opts = ppmtoy4m_opts
2411- self.__framesPerImage = framesPerImage
2412- self.__tmpDir = tmpDir
2413- self.__VideoName = VideoName
2414- self.__queue= finish_queue
2415-
2416-
2417- self._export_gui_obj = export_gui_obj
2418- self._export_gui_obj.progress_bar_text = _("Pass 1/2")
2419- self._export_gui_obj.progress_bar_fraction = 0
2420-
2421- self.__abort = False # reset abort value
2422-
2423- def _on_finish(self, file_path,abort):
2424- """Callback function. Raised when conversions is finished"""
2425- #preparation of next pass if no abort
2426- if abort == False :
2427- self._export_gui_obj.progress_bar_text = _("Pass 2/2")
2428- self._export_gui_obj.progress_bar_fraction = 0.0
2429- # TBD : missing update of file path in export obj
2430- self.__abort = False # reset abort value
2431- return False
2432-
2433- def _on_progress(self, value):
2434- """Callback function. used to know the conversion progress """
2435- self._export_gui_obj.progress_bar_fraction = value
2436- return False
2437-
2438- def _convertToYuv(self) :
2439- """ This memthod is used to convert the sequence of image in a raw
2440- video in YUV4MPEG format. This method use external tools as
2441- imagemagick and tools (ppmtoy4m) from mjpegtools suite."""
2442- # Get first the list of image To co_nvert is depending of the type
2443-
2444- if self.__imageInputType == IMAGE_DIR :
2445- # input images are stored in a directory
2446- imagesToConv = MT.filesInDir(self.__imageDir,"*.jpeg")
2447- else :
2448- # input images are in a list
2449- imagesToConv = self.__imageList
2450- # copmute the number of frames to genarate = Number of images * nf of frame per image
2451- nbFrames = len(imagesToConv)*self.__framesPerImage
2452- self.__ppmtoy4m_opts['-n'] = str(nbFrames) #update the numner of frames for
2453-
2454- video_temp_base=os.path.join(self.__tmpDir,self.__VideoName)
2455- # build ppmtoy4m_cmd
2456- ppmtoy4m_cmd = ""
2457- for key,value in self.__ppmtoy4m_opts.items() :
2458- mystring = " "+ key
2459- if value : mystring = mystring + " "+value
2460- ppmtoy4m_cmd = mystring + ppmtoy4m_cmd
2461- ppmtoy4m_cmd = 'ppmtoy4m'+ppmtoy4m_cmd
2462-
2463- # launch ppmtoy4m, this process run during all the image convesrion
2464- # and receive in stdin images converted in ppm format
2465- # the ppm images are sent to this subprocess by the convert operation
2466- # see p2 below
2467- fd5 = os.open(video_temp_base+".yuv",os.O_WRONLY|os.O_CREAT)
2468- fd6 = os.open(video_temp_base+".log",os.O_WRONLY|os.O_CREAT)
2469-
2470- ppm_proc = SP.Popen(ppmtoy4m_cmd, shell=True, stdin=SP.PIPE,stdout=fd5,stderr=fd6)
2471- frame_cpt = 0
2472- for (count_image,image) in enumerate(imagesToConv) :
2473- # check abort
2474- if self.__abort == True : break
2475- #loop on images to convert
2476- #conversion and rezizing of cuurent image
2477- imagePath = os.path.join(self.__imageDir,image)
2478- montage_cmd = "montage -type TrueColor -quality 100 -geometry 720x576 "+imagePath+ " " + video_temp_base+".jpg"
2479- pMontage =SP.call(montage_cmd,shell=True)
2480- convert_cmd = "convert -type TrueColor -quality 100 "+video_temp_base+".jpg " +video_temp_base+".pnm "
2481- pconvert =SP.call(convert_cmd,shell=True)
2482-
2483- for i in range(self.__framesPerImage) :
2484- # check abort
2485- if self.__abort == True : break
2486- frame_cpt = frame_cpt +1
2487- convert_cmd = "convert -depth 8 ppm:"+video_temp_base+".pnm -"
2488- p1 = SP.Popen(convert_cmd,shell=True, stdout=ppm_proc.stdin,stderr=SP.PIPE)
2489- p1.wait()
2490- # progress bar update
2491- progression = ((count_image+1.0)/len(imagesToConv))
2492- gobject.idle_add(self._on_progress,progression)
2493- os.fsync(fd5)
2494- os.close(fd5)
2495- os.close(fd6)
2496- #return the path to yuv file
2497- return video_temp_base+".yuv"
2498-
2499- def run(self) :
2500- """ Thread start --> launch images to video conversion """
2501- yuvfile_path = self._convertToYuv()
2502- self.__queue.put(yuvfile_path)
2503- gobject.idle_add(self._on_finish,yuvfile_path,self.__abort)
2504-
2505-
2506-
2507-class MyExportThread(threading.Thread):
2508- """ Export Thread. Thread in charge to enconde video in Yuv format
2509- to DV, DVD or XVID format
2510- """
2511-
2512- def _get_abort(self): return self.__abort
2513- def _set_abort(self, value):
2514- if value in (True,False) : self.__abort = value
2515- abort = property(_get_abort, _set_abort, None, "abort thread work")
2516-
2517- def __init__(self, exportType, videoOutPath, yuv_queue, export_gui_obj) :
2518- """ Init export video Thread"""
2519- super(MyExportThread, self).__init__()
2520- self.__abort = False
2521- (self._exportType,self._videoInPath,self._videoOutPath) = (exportType,None,videoOutPath)
2522- self.__queue = yuv_queue
2523- self._export_gui_obj = export_gui_obj
2524-
2525- def _on_finish(self,abort):
2526- """Callback function. Raised when conversions is finished"""
2527- # update gui progress bar
2528- if abort == False :
2529- # Terminated normaly
2530- self._export_gui_obj.progress_bar_text = _("Export Done")
2531- self._export_gui_obj.progress_bar_fraction = 1.0
2532- else :
2533- self._export_gui_obj.progress_bar_text = _("Export Canceled")
2534- self._export_gui_obj.progress_bar_fraction = 0.0
2535- self.__abort = False # reset abort
2536- return False
2537-
2538- def _on_progress(self, value):
2539- """Callback function. used to know the conversion progress """
2540- self._export_gui_obj.progress_bar_fraction = value
2541- return False
2542-
2543- def run(self):
2544- """ Thread execution --> generate export """
2545- """ Build and launch ffmpeg command. """
2546- yuv_is_finished=False
2547- data = None
2548- while (yuv_is_finished == False) :
2549- time.sleep(0.1)
2550- try :
2551- data=self.__queue.get(block=False)
2552- yuv_is_finished=True
2553- except Queue.Empty :
2554- pass
2555- # check abort if true leave it
2556- if self.__abort == True :
2557- data = None
2558- break
2559- if data :
2560- self._videoInPath = data
2561- if self._exportType == EXPORT_DV :
2562- self._ffmpeg_launcher()
2563- else :
2564- self._mencoder_launcher()
2565- gobject.idle_add(self._on_finish,self.__abort)
2566-
2567- def _ffmpeg_launcher(self):
2568- """ Video conversion with ffmpeg : only used for DV conversion """
2569- self.__videoFormat = "PAL" # only PAL is supported
2570- self.__withSound = False # no sound by default
2571- if ( (self._exportType == EXPORT_DV) and ( self._videoInPath ) and ( self._videoOutPath ) ) :
2572- #
2573- # Build ffmpeg command
2574- #
2575- ffmpeg_cmd = []
2576- ffmpeg_cmd.append("ffmpeg")
2577- # add input video name
2578- ffmpeg_cmd.append('-i')
2579- ffmpeg_cmd.append(self._videoInPath)
2580- # add video conversion options
2581- coding_command = dictEncoder["PAL"][EXPORT_DV]
2582- for ffmpeg_w in coding_command.split() : ffmpeg_cmd.append(ffmpeg_w)
2583- # add output video name
2584- ffmpeg_cmd.append(self._videoOutPath)
2585-
2586- #
2587- # launch ffmpeg subprocess
2588- #
2589- print "[",self.__class__.__name__,"] ","command :", ffmpeg_cmd
2590- sb_encoder =SP.Popen(ffmpeg_cmd,stdout=SP.PIPE,stderr=SP.PIPE)
2591-
2592- #
2593- # check encoding process and finish
2594- #
2595- encode_not_finish = True
2596- pulse_counter = 0
2597- pulse_modulo = 20
2598- while encode_not_finish == True:
2599- ret_code = sb_encoder.poll()
2600- if type(ret_code) == int :
2601- # encoding is finished when a code is returned
2602- encode_not_finish = False
2603- if pulse_counter % pulse_modulo == 0 :
2604- # update progress bar only with pulses
2605- gobject.idle_add(self._on_progress, 2.0 )
2606- pulse_counter = pulse_counter +1
2607-
2608- def _mencoder_launcher(self) :
2609- """ Video conversion with mencoder : used for convestion in DVD and XVID format"""
2610- self.__videoFormat = "PAL" # only PAL is supported
2611- self.__withSound = False # no sound by default
2612- if ( (self._exportType in (EXPORT_DVD,EXPORT_XVID)) and ( self._videoInPath ) and ( self._videoOutPath ) ) :
2613- #
2614- # Build mencoder command
2615- #
2616- mencoder_cmd =[]
2617- mencoder_cmd.append("mencoder")
2618- # add export video options
2619- if self.__videoFormat == "PAL" :
2620- coding_command = dictEncoder["PAL"][self._exportType]
2621- for mencoder_w in coding_command.split() : mencoder_cmd.append(mencoder_w)
2622- #finally add the input and output :
2623- mencoder_cmd.append(self._videoInPath)
2624- mencoder_cmd.append('-o')
2625- mencoder_cmd.append(self._videoOutPath)
2626-
2627- #
2628- # launch mencoder command
2629- #
2630- print "[",self.__class__.__name__,"] ","command :", mencoder_cmd
2631- sb_encoder =SP.Popen(mencoder_cmd,stdout=SP.PIPE,stderr=SP.PIPE)
2632-
2633- #
2634- # check encoding process and finish
2635- #
2636- encode_not_finish = True
2637- res_value = 0
2638- res_value_old = 0
2639- while encode_not_finish == True:
2640-
2641- # First loop until child is finished
2642- buffer = []
2643- ret_code = sb_encoder.poll()
2644- if type(ret_code) == int :
2645- encode_not_finish = False
2646- while True:
2647- # second loop to detect a line
2648- # remark : I have no success to use readline or readlines
2649- # missing some characters
2650-
2651- # mencoder display info on stdout,
2652- # read one char to no be blocked until eof
2653- char = sb_encoder.stdout.readline(1)
2654- if not char : break
2655- else :
2656- if char == '\r':
2657- aLine = string.join( buffer, '' )
2658- buffer = []
2659- break
2660- else:
2661- buffer.append(char)
2662- #extact Frame num
2663- #print aLine
2664- #Exemple line :
2665- #Pos: 19.0s 475f (99%) 30.68fps Trem: 0min 65mb A-V:0.000 [28800:0]
2666- # regeexp for select percent value here : 99.
2667- # value in parenthis ; with one or digits; select only the number
2668- pattern = re.compile("\(\s*([0-9]+)\%\)")
2669- # get Match_object
2670- # cf tuto : http://docs.python.org/howto/regex.html#regex-howto
2671- re_res = pattern.search(aLine)
2672- if re_res :
2673- # result is at first index of groups method
2674- # divide by 100 to have a value in range 0 .. 100
2675- res_value = re_res.groups()[0]
2676- if res_value != res_value_old :
2677- # if send message only when percentage value change
2678- # avoid sending unuseful message
2679- gobject.idle_add(self._on_progress, float(res_value)/100.0 )
2680- res_value_old = res_value
2681- # check if abort requested
2682- if self.__abort == True :
2683- pid = sb_encoder.pid
2684- os.kill(pid, signal.SIGTERM)
2685- time.sleep(1.0)
2686- # check if is process really dead
2687- if not isinstance(sb_encoder.poll(),int) :
2688- os.kill(pid,signal.SIGKILL)
2689- encode_not_finish = False
2690-
2691-
2692-
2693-class luciole_export(object) :
2694- """ Class in charge of managing exports. The class use threads for conversions"""
2695-
2696- _exportType = (EXPORT_DV,EXPORT_DVD,EXPORT_XVID)
2697-
2698- _suffixList =(".dv",".mpeg2",".avi")
2699-
2700- def _get_imageDir(self): return self.__imageDir
2701- def _set_imageDir(self, value): self.__imageDir = value
2702- def _del_imageDir(self): del self.__imageDir
2703- imageDir = property(_get_imageDir, _set_imageDir, _del_imageDir, "Image's directory. ")
2704-
2705- def _get_imageList(self): return self.__imageList
2706- def _set_imageList(self, value): self.__imageList = value
2707- def _del_imageList(self): del self.__imageList
2708- imageList = property(_get_imageList, _set_imageList, _del_imageList, "Image's list to encode. This list should be sorted. images are encodede in list order. ")
2709-
2710- def _get_imageInputType(self): return self.__imageInputType
2711- def _set_imageInputType(self, value):
2712- if value in (IMAGE_LIST,IMAGE_DIR) :
2713- self.__imageInputType = value
2714- else :
2715- print "[",self.__class__.__name__,"] ",value," not in correct type"
2716- def _del_imageInputType(self): del self.__imageInputType
2717- imageInputType = property(_get_imageInputType, _set_imageInputType, _del_imageInputType, "Image's input type ")
2718-
2719- def _get_VideoName(self): return self.__VideoName
2720- def _set_VideoName(self, value): self.__VideoName = value
2721- def _del_VideoName(self): del self.__VideoName
2722- VideoName = property(_get_VideoName, _set_VideoName, _del_VideoName, "Image's directory. ")
2723-
2724- def _get_videoDir(self): return self.__videoDir
2725- def _set_videoDir(self, value): self.__videoDir = value
2726- def _del_videoDir(self): del self.__videoDir
2727- videoDir = property(_get_videoDir, _set_videoDir, _del_videoDir, "Image's directory. ")
2728-
2729- def _get_framesPerImage(self): return self.__framesPerImage
2730- def _set_framesPerImage(self, value): self.__framesPerImage = value
2731- def _del_framesPerImage(self): del self.__framesPerImage
2732- framesPerImage = property(_get_framesPerImage, _set_framesPerImage, _del_framesPerImage, "Number of frames per image. ")
2733-
2734- def _get_outputFPS(self): return self.__outputFPS
2735- def _set_outputFPS(self, value): self.__outputFPS = value
2736- def _del_outputFPS(self): del self.__outputFPS
2737- outputFPS = property(_get_outputFPS, _set_outputFPS, _del_outputFPS, "Image's directory. ")
2738-
2739- def _get_videoFormat(self): return self.__videoFormat
2740- def _set_videoFormat(self, value):
2741- self.__videoFormat = value
2742- #set values for PAL format
2743- if self.__videoFormat == "PAL" :
2744- self.__ppmtoy4m_opts['F'] = "-F 25:1" # framerate - see man ppmtoy4m
2745- self.__ppmtoy4m_opts['A'] = "-A 59:54" # aspect ratio - see man ppmtoy4m
2746- def _del_videoFormat(self): del self.__videoFormat
2747- videoFormat = property(_get_videoFormat, _set_videoFormat, _del_videoFormat, "Image's directory. ")
2748-
2749- def _get_videoType(self): return self.__videoType
2750- def _set_videoType(self, value): self.__videoType = value
2751- def _del_videoType(self): del self.__videoType
2752- videoType = property(_get_videoType, _set_videoType, _del_videoType, "Image's directory. ")
2753-
2754- def _get_videoAspect(self): return self.__videoAspect
2755- def _set_videoAspect(self, value): self.__videoAspect = value
2756- def _del_videoAspect(self): del self.__videoAspect
2757- videoAspect = property(_get_videoAspect, _set_videoAspect, _del_videoAspect, "Image's directory. ")
2758-
2759- def _get_withSound(self): return self.__withSound
2760- def _set_withSound(self, value): self.__withSound = value
2761- def _del_withSound(self): del self.__withSound
2762- withSound = property(_get_withSound, _set_withSound, _del_withSound, "Image's directory. ")
2763-
2764- def _get_export_on_progress(self):
2765- """ export status is knowed by the status of the last expôpt thread """
2766- if self._t_encoder and self._t_encoder.isAlive() == True :
2767- self._export_on_progress = True
2768- else :
2769- self._export_on_progress = False
2770- return self._export_on_progress
2771- export_on_progress = property(_get_export_on_progress, None, None, "export status")
2772-
2773-
2774-
2775- def __init__(self,tmp_dir, export_gui_obj):
2776- """Init of class"""
2777- self.__tmpDir = os.path.join(tmp_dir,"export")
2778- self._cleanTmpDir()
2779-
2780- self.__imageList = list() # input image list
2781- self.__imageInputType = IMAGE_LIST # image type input is imageList by default
2782-
2783- self.__VideoName = "export"
2784- self.__outputFPS = "25"
2785- self.__videoFormat = "PAL"
2786- self.__videoType = "DV"
2787- self.__videoAspect ="4/3"
2788-
2789- # set ppmtoy4m options
2790- self.__ppmtoy4m_opts=dict()
2791- self.__ppmtoy4m_opts['-F'] = "25:1" # framerate - see man ppmtoy4m
2792- self.__ppmtoy4m_opts['-A'] = "59:54" # aspectRatio - see man ppmtoy4m
2793- self.__ppmtoy4m_opts['-S'] = "420mpeg2" # subsamplimg chroma mode - see man ppmtoy4m
2794- self.__ppmtoy4m_opts['-I'] = "p" # interlacing mode,progressive, non-interlaced - see man ppmtoy4m
2795- self.__ppmtoy4m_opts['-n'] = "0" # total output frames - see man ppmtoy4m
2796- self.__ppmtoy4m_opts['-v'] = "2" # verbosity - see man ppmtoy4m
2797-
2798- self.__framesPerImage=10 # default value for frame Per image
2799-
2800- self.__withSound = False # no sound by default
2801-
2802- self._export_on_progress = False # export progression
2803- self._t_yuv = None
2804- self._t_encoder = None
2805- self._videopath = None
2806- self.__imageDir = None
2807- if self.__videoFormat == "PAL":
2808- self.__videoRes=(720,576)
2809- # export gui obj
2810- self._export_gui_obj = export_gui_obj
2811-
2812-
2813-
2814- def export(self, export_data ,forceExport=False) :
2815- """ Export function
2816- exportData is a dict with the following items :
2817- 'image_input' = type of image put list or dir (IMAGE_LIST,IMAGE_DIR)
2818- 'image_list' = List of image to convert, each element is an absolute path yo the image
2819- or
2820- 'image_dir' = dir with all the images to impot : Not implemented
2821- 'export_dir' = the directory where the video will be exported
2822- 'video_name' = the export video name wihout extension
2823- 'export_type' = the type of export (EXPORT_DV,EXPORT_DVD,EXPORT_XVID)
2824- 'fpi' = the frame rate
2825- """
2826-
2827-
2828- (VideoExsists,videopath) = self._IsVideoExists(export_data)
2829- if ( ( VideoExsists == False) or (forceExport == True)
2830- and
2831- ( videopath != None ) ) :
2832- # check tmp dir is clean
2833- self._cleanTmpDir()
2834- self._export_on_progress = True
2835-
2836- # initiate queue for comunication between yuv converter and export converter
2837- self._dataQueue=Queue.Queue()
2838- self._ResQueue=Queue.Queue()
2839-
2840- #
2841- # Check input parameters
2842- #
2843- print " DEBUG : export - ",export_data
2844- export_is_valid = False
2845- if export_data.has_key('image_input') :
2846- self.__imageInputType = export_data['image_input']
2847- export_is_valid = True
2848-
2849- # check type of input validity
2850- if(
2851- (export_is_valid == True )
2852- and
2853- (
2854- (
2855- export_data.has_key('image_list')
2856- and
2857- export_data['image_input'] == IMAGE_LIST
2858- )
2859- or
2860- (
2861- export_data.has_key('image_dir')
2862- and
2863- ( export_data['image_input'] == IMAGE_DIR )
2864- ) )) :
2865-
2866- if export_data.has_key('image_list') : self.__imageList = export_data['image_list']
2867- if export_data.has_key('image_dir') : self.__imageList = export_data['image_dir']
2868- export_is_valid = True
2869- else :
2870- export_is_valid = False
2871-
2872- if (export_is_valid == True) and (export_data.has_key('fpi')) :
2873- self.__framesPerImage = int(export_data['fpi'])
2874- export_is_valid = True
2875- else :
2876- export_is_valid = False
2877-
2878- if (export_is_valid == True) and (export_data.has_key('video_name')) :
2879- self.__VideoName = export_data['video_name']
2880- export_is_valid = True
2881- else :
2882- export_is_valid = False
2883-
2884- if (export_is_valid == True) and os.path.exists(videopath) :
2885- try :
2886- os.remove(videopath)
2887- except OSError,err :
2888- print _('Unable to erase'),err.filename
2889- print err.strerror
2890- videopath = None
2891- export_is_valid = False
2892-
2893-
2894- if export_is_valid == True :
2895- # start yuv converter sub process
2896- self._t_yuv = MyThreadConvertToYuv(self.__imageInputType,self.__imageDir,self.__imageList,self.__ppmtoy4m_opts,self.__framesPerImage,self.__tmpDir,self.__VideoName,self._dataQueue, self._export_gui_obj)
2897- self._t_yuv.start()
2898- # start video export encoder subprocess
2899- self._videopath = videopath
2900- self._t_encoder = MyExportThread(export_data['export_type'], self._videopath, self._dataQueue, self._export_gui_obj)
2901- self._t_encoder.start()
2902- else :
2903- return (0,videopath)
2904-
2905- if ( VideoExsists == True ) :
2906- return (ERR_FILE_EXIST,videopath)
2907- else :
2908- return (0,videopath)
2909-
2910- def cancel_export(self):
2911- """ Cancel export """
2912- if self._export_on_progress == True :
2913- # kill the eport Threads
2914- if self._t_yuv.isAlive() : self._t_yuv.abort= True
2915- if self._t_encoder.isAlive() : self._t_encoder.abort= True
2916- #check if process remains
2917- while ( self._t_yuv.isAlive() or self._t_encoder.isAlive() ) :
2918- time.sleep(0.1)
2919- # clean temporary files
2920- self._cleanTmpDir()
2921- try :
2922- os.remove(self._videopath )
2923- print self._videopath ," is removed"
2924- except OSError,err :
2925- print "Impossible d effacer ",err.filename
2926- print err.strerror
2927-
2928-
2929-
2930- ################################################################################
2931- # private methods
2932- ################################################################################
2933- def _cleanTmpDir(self) :
2934- """ Clean or create tmp dir if needed"""
2935- if not os.path.exists(self.__tmpDir) :
2936- #create it
2937- try :
2938- os.makedirs(self.__tmpDir)
2939- except OSError,err :
2940- print "Impossible de creer le repertoire export"
2941- print err.strerror
2942- self.__tmpDir = "/tmp"
2943- else :
2944- # directory exist, clean it
2945- list = dircache.listdir( self.__tmpDir)
2946- for file in list :
2947- filePath = os.path.join(self.__tmpDir,file)
2948- try :
2949- os.remove(filePath)
2950- print filePath ," is removed"
2951- except OSError,err :
2952- print "Impossible d effacer ",err.filename
2953- print err.strerror
2954-
2955- def _IsVideoExists(self,export_data):
2956- """ test if a video path exists """
2957- exists = False
2958- video_path = None
2959- # check export type
2960- if export_data.has_key('export_type') and export_data['export_type'] in self._exportType :
2961- suffix = self._suffixList[export_data['export_type']]
2962- # check export video name
2963- if export_data.has_key('video_name') :
2964- video_name = export_data['video_name']+suffix
2965-
2966- # check export dir
2967- if not os.path.exists(export_data['export_dir']) :
2968- #create it
2969- try :
2970- MT.mkdirs(export_data['export_dir'])
2971- except LEXCEP.LucioException , err :
2972- # to robustify : Error handling
2973- print err
2974- return (exists, video_path)
2975- video_path = os.path.join(export_data['export_dir'], video_name)
2976-
2977- # check if video path exists
2978- if os.path.exists(video_path) :
2979- exists = True
2980- return (exists,video_path)
2981-
2982-
2983-
2984
2985=== removed file 'lucioLib/lucioExport/luciole_export_tool.py'
2986--- lucioLib/lucioExport/luciole_export_tool.py 2009-12-03 08:11:32 +0000
2987+++ lucioLib/lucioExport/luciole_export_tool.py 1970-01-01 00:00:00 +0000
2988@@ -1,281 +0,0 @@
2989-#!/usr/bin/env python
2990-# -*- coding: utf-8 -*-
2991-# -*- Mode: Python -*-
2992-# vi:si:ai:et:sw=4:sts=4:ts=4
2993-#
2994-#
2995-# Copyright Nicolas Bertrand (nico@inattendu.org), 2009
2996-#
2997-# This file is part of Luciole.
2998-#
2999-# Luciole is free software: you can redistribute it and/or modify
3000-# it under the terms of the GNU General Public License as published by
3001-# the Free Software Foundation, either version 3 of the License, or
3002-# (at your option) any later version.
3003-#
3004-# Luciole is distributed in the hope that it will be useful,
3005-# but WITHOUT ANY WARRANTY; without even the implied warranty of
3006-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3007-# GNU General Public License for more details.
3008-#
3009-# You should have received a copy of the GNU General Public License
3010-# along with Luciole. If not, see <http://www.gnu.org/licenses/>.
3011-#
3012-#
3013-
3014-# for i18n
3015-from gettext import gettext as _
3016-
3017-import os.path
3018-from .. import luciole_et as LE
3019-
3020-from .. import luciole_exceptions as LEXCEP
3021-from .. import luciole_project as LPROJECT
3022-class luciole_export_tool(object) :
3023- """
3024- :version:
3025- :author:
3026- """
3027-
3028-
3029- """
3030- ATTRIBUTES
3031- export_file : Path to file to export
3032- template : Path to template
3033- lcl_project : The luciole project a file or a dict
3034- _lcl_project_data :
3035- _export_template : File to update with export data
3036- _template_xml :
3037- """
3038-
3039-
3040-
3041- def __init__(self, lcl_project = "./luciole_project.xml", template="./template.xml", export_file = "./export.xml"):
3042- """
3043- @param lcl_project : Type de projet
3044- @param template : Path to template
3045- @param export_file : Path to export_file
3046- """
3047- #
3048- # Atributes init
3049- #
3050- self.lcl_project = lcl_project
3051- self.template = template
3052- self.export_file = export_file
3053- self._lcl_project_data = {}
3054- self._lcl_project_data['resources_images'] = []
3055- self._lcl_project_data['timelines_images'] = []
3056-
3057- self._export_template = None
3058- self._template_xml = None
3059-
3060- if (type(lcl_project) == str) :
3061- # test file type
3062- if os.path.exists(lcl_project) :
3063- #filetype exits load it
3064- self.__load_lcl_file()
3065- else :
3066- #Raise Error
3067- excep_message = "%s is ot a file",lcl_project
3068- raise LEXCEP.LucioException,excep_message
3069- elif (type(lcl_project) == LPROJECT.project_dico) :
3070- # input is a dictionary
3071- self.__load_lcl_dict()
3072- else :
3073- # template does not exists : raise an exception
3074- excep_message = "Invalid projetc type nor file nor luciole_project"
3075- raise LEXCEP.LucioException,excep_message
3076-
3077- #
3078- # Test template
3079- #
3080- if os.path.exists(self.template) :
3081- self._load_template()
3082- else :
3083- # template does not exists : raise an exception
3084- excep_message = " Template file %s does not exist"%self.template
3085- raise LEXCEP.LucioException,excep_message
3086-
3087-
3088-
3089-
3090- def generate(self):
3091- """
3092-
3093-
3094- @return :
3095- @author
3096- """
3097- self._gen_ressources()
3098- self._gen_timeline()
3099- self.__write_export_file()
3100-
3101- def _load_template(self):
3102- """
3103-
3104-
3105- @return :
3106- @author
3107- """
3108- pass
3109-
3110- def _gen_ressources(self):
3111- """
3112- generate date for resources
3113-
3114- @return :
3115- @author
3116- """
3117- pass
3118-
3119- def _gen_timeline(self):
3120- """
3121- Generate data for timeline
3122-
3123- @return :
3124- @author
3125- """
3126- pass
3127-
3128- def __load_lcl_file(self):
3129- """
3130- Load a Luciole project from a file dictionary
3131-
3132- @return :
3133- @author
3134- """
3135- try :
3136- _lcl_project_xml = LE.lcl_et(self.lcl_project)
3137- except :
3138- pass
3139- else :
3140- self._lcl_project_data['image_path'] = os.path.join(_lcl_project_xml.findtext("metas/projectPath").strip() ,
3141- _lcl_project_xml.findtext("metas/rush_dir").strip() )
3142- self._lcl_project_data['fpi'] = int(_lcl_project_xml.findtext("metas/fpi").strip())
3143-
3144- # get resources images from project capture data
3145- list_image = _lcl_project_xml.find("captureData").getiterator('image')
3146- [ self._lcl_project_data['resources_images'].append(image.text.strip()) for image in list_image]
3147-
3148- # get timelines images from project chrono data
3149- list_image = _lcl_project_xml.find("chronoData").getiterator('image')
3150- [self._lcl_project_data['timelines_images'].append(image.text.strip()) for image in list_image]
3151-
3152-
3153- def __load_lcl_dict(self):
3154- """
3155- Load a Luciole project from a dictionary
3156-
3157- @return :
3158- @author
3159- """
3160-
3161- self._lcl_project_data['image_path'] = os.path.join(self.lcl_project['project_dir'], self.lcl_project['rush_dir'])
3162- self._lcl_project_data['fpi'] = int(self.lcl_project['fpi'])
3163- self._lcl_project_data['resources_images'] = self.lcl_project['capture_images']
3164- self._lcl_project_data['timelines_images'] = self.lcl_project['chrono_images']
3165-
3166-
3167- def __write_export_file(self):
3168- """
3169-
3170-
3171- @return :
3172- @author
3173- """
3174- self._template_xml.write(self.export_file)
3175-
3176-class luciole_export_tool_cinelerra (luciole_export_tool):
3177-
3178- """
3179-
3180-
3181- :version:
3182- :author:
3183- """
3184-
3185- """ ATTRIBUTES
3186-
3187-
3188-
3189- _export_template (private)
3190-
3191- """
3192-
3193- def __init__(self, lcl_project = "./luciole_project.xml", template="./template.xml", export_file = "./export.xml"):
3194- """
3195-
3196-
3197- @param luciole_export_tool lcl_project_type : type of luciole project : "file" or "project"
3198- @param object lcl_project : Type de projet
3199- @param string template : Path to template
3200- @param string export_file : Path to export_file
3201- @return :
3202- @author
3203- """
3204- super(luciole_export_tool_cinelerra, self).__init__(lcl_project,template,export_file )
3205-
3206- def _load_template(self):
3207- """
3208- """
3209- self._template_xml = LE.lcl_et(self.template)
3210-
3211-
3212- def _gen_ressources(self):
3213- """
3214- ressource is called asset in cinelerra template
3215- """
3216- resources_tag = self._template_xml.find("//ASSETS")
3217- resource_elem_tpl = self._template_xml.find("//ASSET")
3218-
3219- for image in self._lcl_project_data['resources_images'] :
3220- # build image_path
3221- image_p = os.path.join(self._lcl_project_data['image_path'], image)
3222- # create a resouce element --> i.e an ASSET element
3223- new_element = self._template_xml.SubElement(resources_tag,"ASSET",{"SRC":image_p})
3224- # copy subelement od ASSET template tag in new element
3225- for elem_tpl in resource_elem_tpl.getchildren() :
3226- new_element.append(elem_tpl)
3227-
3228- #remove template tag
3229- resources_tag.remove(resource_elem_tpl)
3230-
3231-
3232- def __get_timeline_tags(self) :
3233- """
3234- in cinelerra timeline tags is TRACK with type video, and EDITS/EDIT tag
3235- """
3236- video_track = None
3237- tracks = self._template_xml.findall("//TRACK")
3238- for track in tracks :
3239- if track.get("TYPE") == "VIDEO" :
3240- # video tak is found
3241- video_track = track
3242- break
3243- # get EDIT tag
3244- return ( video_track.find("EDITS"), video_track.find("EDITS/EDIT"))
3245-
3246-
3247- def _gen_timeline(self):
3248- """
3249- generate timeline
3250- """
3251- (timeline_tag, timeline_item_tpl) = self.__get_timeline_tags()
3252- for image in self._lcl_project_data['timelines_images'] :
3253- # build image_path
3254- image_p = os.path.join(self._lcl_project_data['image_path'], image)
3255-
3256- # create New EDIT element per image
3257- for frame in range(self._lcl_project_data['fpi']):
3258- framerate = str(self._lcl_project_data['fpi'])
3259- new_element = self._template_xml.SubElement(timeline_tag,"EDIT",{"STARTSOURCE":"0","CHANNEL":"0", "LENGTH":"1"})
3260- # create FILE subElement of EDIT
3261- self._template_xml.SubElement(new_element,"FILE",{"SRC":image_p})
3262-
3263- # remove EDIT template tag
3264- timeline_tag.remove(timeline_item_tpl)
3265-
3266-if __name__ == '__main__' :
3267- X = luciole_export_tool_cinelerra( lcl_project = "/home/nico/temp/testLuciole/luciole_project_isight/luciole_project_isight.xml",template="./cinelerra_template.xml",export_file = "./cine_export.xml" )
3268- X.generate()
3269-
3270
3271=== modified file 'lucioLib/luciole_et.py'
3272--- lucioLib/luciole_et.py 2010-01-04 17:42:18 +0000
3273+++ lucioLib/luciole_et.py 2010-01-04 17:42:19 +0000
3274@@ -64,12 +64,21 @@
3275 def findall(self,match) :
3276 return self.xml_obj.findall(match)
3277
3278+ def Element(self, tag) :
3279+ return ET.Element(tag)
3280+
3281 def SubElement(self,parent,tag,attrib={}) :
3282 return ET.SubElement(parent,tag,attrib)
3283
3284+ def insert(self,index,element) :
3285+ return self.xml_obj.insert(index,element)
3286+
3287 def tostring(self,element) :
3288 return ET.tostring(element)
3289
3290+ def getroot(self) :
3291+ return self.xml_obj.getroot()
3292+
3293 def write(self,file) :
3294 indent(self.xml_obj.getroot())
3295 return self.xml_obj.write(file,"UTF-8")
3296
3297=== added file 'templates/kdenlive_template.kdenlive'
3298--- templates/kdenlive_template.kdenlive 1970-01-01 00:00:00 +0000
3299+++ templates/kdenlive_template.kdenlive 2010-01-04 17:42:19 +0000
3300@@ -0,0 +1,46 @@
3301+<?xml version='1.0' encoding='utf-8'?>
3302+<mlt title="Anonymous Submission" root="" >
3303+ <producer in="0" out="14999" id="black" >
3304+ <property name="mlt_type" >producer</property>
3305+ <property name="aspect_ratio" >0.000000</property>
3306+ <property name="length" >15000</property>
3307+ <property name="eof" >pause</property>
3308+ <property name="resource" >black</property>
3309+ <property name="mlt_service" >colour</property>
3310+ </producer>
3311+ <!-- update out value with total duration -->
3312+ <playlist id="black_track" >
3313+ <entry in="0" out="" producer="black" />
3314+ </playlist>
3315+ <playlist id="playlist1" />
3316+ <playlist id="playlist2" />
3317+ <!-- generate producer tags here -->
3318+
3319+ <playlist id="lcl_track" >
3320+ <!-- generate entry tags here -->
3321+ </playlist>
3322+
3323+ <!-- modify "out" value with number of frame in project -->
3324+ <tractor title="Anonymous Submission" global_feed="1" in="0" out="2" id="maintractor" >
3325+ <track producer="black_track" />
3326+ <track hide="video" producer="playlist1" />
3327+ <track hide="video" producer="playlist2" />
3328+ <track producer="lcl_track" />
3329+ </tractor>
3330+ <!-- Update projectfolder tag with kdenlivepath -->
3331+ <kdenlivedoc profile="dv_pal" kdenliveversion="0.7.5" version="0.84" projectfolder="" >
3332+ <documentproperties zonein="0" zoneout="100" zoom="1" verticalzoom="1" position="0" />
3333+ <profileinfo width="720" display_aspect_den="3" frame_rate_den="1" description="DV/DVD PAL" height="576" frame_rate_num="25" display_aspect_num="4" progressive="0" sample_aspect_num="16" sample_aspect_den="15" />
3334+ <tracksinfo>
3335+ <trackinfo blind="1" mute="0" locked="0" type="audio" />
3336+ <trackinfo blind="1" mute="0" locked="0" type="audio" />
3337+ <trackinfo blind="0" mute="0" locked="0" />
3338+ </tracksinfo>
3339+ <!-- generate kdenlive_producer tag example :
3340+ <kdenlive_producer audio_max="0" channels="0" duration="10" default_audio="0" video_max="0" frame_size="720x576" frequency="0" in="0" file_size="46448" aspect_ratio="1.000000" out="10" file_hash="fc820ac310b557ad08df64a52a06e635" type="5" id="1" name="rush_00000.jpeg" default_video="0" resource="/home/nico/temp/testLuciole/luciole_project_isight/rush/rush_00000.jpeg" />
3341+ -->
3342+
3343+ <markers/>
3344+ <groups/>
3345+ </kdenlivedoc>
3346+</mlt>
3347
3348=== modified file 'ui/export_file.glade'
3349--- ui/export_file.glade 2009-12-03 08:11:32 +0000
3350+++ ui/export_file.glade 2010-01-04 17:42:19 +0000
3351@@ -4,11 +4,13 @@
3352 <!-- interface-naming-policy project-wide -->
3353 <object class="GtkDialog" id="dialog_export_file">
3354 <property name="border_width">5</property>
3355+ <property name="title" translatable="yes">Tool Exporter</property>
3356+ <property name="icon">../images/luciole.png</property>
3357+ <property name="icon_name">luciole</property>
3358 <property name="type_hint">normal</property>
3359 <property name="has_separator">False</property>
3360 <signal name="destroy" handler="on_dialog_export_file_destroy"/>
3361 <signal name="close" handler="on_dialog_export_file_close"/>
3362- <signal name="response" handler="on_dialog_export_file_response"/>
3363 <child internal-child="vbox">
3364 <object class="GtkVBox" id="dialog-vbox1">
3365 <property name="visible">True</property>
3366@@ -48,6 +50,7 @@
3367 <property name="visible">True</property>
3368 <property name="can_focus">True</property>
3369 <property name="receives_default">False</property>
3370+ <property name="tooltip_text" translatable="yes">Export to cinelerra</property>
3371 <property name="active">True</property>
3372 <property name="draw_indicator">True</property>
3373 <signal name="toggled" handler="on_radio_cine_toggled"/>
3374@@ -59,6 +62,7 @@
3375 <property name="visible">True</property>
3376 <property name="can_focus">True</property>
3377 <property name="receives_default">False</property>
3378+ <property name="tooltip_text" translatable="yes">Export to Kdenlive</property>
3379 <property name="draw_indicator">True</property>
3380 <property name="group">radio_cine</property>
3381 <signal name="toggled" handler="on_radio_kdenlive_toggled"/>
3382@@ -74,6 +78,7 @@
3383 <property name="visible">True</property>
3384 <property name="can_focus">True</property>
3385 <property name="receives_default">False</property>
3386+ <property name="tooltip_text" translatable="yes">Export to pitivi</property>
3387 <property name="draw_indicator">True</property>
3388 <property name="group">radio_cine</property>
3389 <signal name="toggled" handler="on_radio_pitivi_toggled"/>
3390@@ -87,8 +92,11 @@
3391 <object class="GtkRadioButton" id="radio_openshot">
3392 <property name="label" translatable="yes">Openshot</property>
3393 <property name="visible">True</property>
3394- <property name="can_focus">True</property>
3395+ <property name="sensitive">False</property>
3396+ <property name="can_focus">False</property>
3397 <property name="receives_default">False</property>
3398+ <property name="tooltip_text" translatable="yes">Coming soon</property>
3399+ <property name="inconsistent">True</property>
3400 <property name="draw_indicator">True</property>
3401 <property name="group">radio_cine</property>
3402 <signal name="toggled" handler="on_radio_openshot_toggled"/>

Subscribers

People subscribed via source and target branches

to all changes: