Merge lp:~nico-inattendu/luciole/bp-kdenlive-export into lp:luciole/0.8
- bp-kdenlive-export
- Merge into 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 |
Related bugs: | |
Related blueprints: |
Export to Kdenlive
(Medium)
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
NicoInattendu | Pending | ||
Review via email: mp+16795@code.launchpad.net |
Commit message
Description of the change
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"/> |