bfa65bfb56a752add588e59b337a3a5c67c303f7
[blender-addons-contrib.git] / sequencer_extra_actions / operators_extra_actions.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 '''
20 TODO
21 align strip to the left (shift-s + -lenght)
22
23 '''
24
25 import bpy
26
27 import random
28 import math
29 import os, sys
30
31 from bpy.props import IntProperty
32 from bpy.props import FloatProperty
33 from bpy.props import EnumProperty
34 from bpy.props import BoolProperty
35 from bpy.props import StringProperty
36
37 from . import functions
38 from . import exiftool
39
40
41 # Initialization
42
43 def initSceneProperties(context, scn):
44     try:
45         if context.scene.scene_initialized == True:
46             return False
47     except AttributeError:
48         pass
49
50     bpy.types.Scene.default_slide_offset = IntProperty(
51         name='Offset',
52         description='Number of frames to slide',
53         min=-250, max=250,
54         default=0)
55     scn.default_slide_offset = 0
56
57     bpy.types.Scene.default_fade_duration = IntProperty(
58         name='Duration',
59         description='Number of frames to fade',
60         min=1, max=250,
61         default=scn.render.fps)
62     scn.default_fade_duration = scn.render.fps
63
64     bpy.types.Scene.default_fade_amount = FloatProperty(
65         name='Amount',
66         description='Maximum value of fade',
67         min=0.0,
68         max=100.0,
69         default=1.0)
70     scn.default_fade_amount = 1.0
71
72     bpy.types.Scene.default_distribute_offset = IntProperty(
73         name='Offset',
74         description='Number of frames between strip start frames',
75         min=1,
76         max=250,
77         default=2)
78     scn.default_distribute_offset = 2
79
80     bpy.types.Scene.default_distribute_reverse = BoolProperty(
81         name='Reverse Order',
82         description='Reverse the order of selected strips',
83         default=False)
84     scn.default_distribute_reverse = False
85
86     bpy.types.Scene.default_proxy_suffix = StringProperty(
87         name='default proxy suffix',
88         description='default proxy filename suffix',
89         default="-25")
90     scn.default_proxy_suffix = "-25"
91
92     bpy.types.Scene.default_proxy_extension = StringProperty(
93         name='default proxy extension',
94         description='default proxy file extension',
95         default=".mkv")
96     scn.default_proxy_extension = ".mkv"
97
98     bpy.types.Scene.default_proxy_path = StringProperty(
99         name='default proxy path',
100         description='default proxy file path (relative to original file)',
101         default="")
102     scn.default_proxy_path = ""
103
104     bpy.types.Scene.default_build_25 = BoolProperty(
105         name='default_build_25',
106         description='default build_25',
107         default=True)
108     scn.default_build_25 = True
109
110     bpy.types.Scene.default_build_50 = BoolProperty(
111         name='default_build_50',
112         description='default build_50',
113         default=False)
114     scn.default_build_50 = False
115
116     bpy.types.Scene.default_build_75 = BoolProperty(
117         name='default_build_75',
118         description='default build_75',
119         default=False)
120     scn.default_build_75 = False
121
122     bpy.types.Scene.default_build_100 = BoolProperty(
123         name='default_build_100',
124         description='default build_100',
125         default=False)
126     scn.default_build_100 = False
127     
128     bpy.types.Scene.default_recursive = BoolProperty(
129         name='Recursive',
130         description='Load in recursive folders',
131         default=False)
132     scn.default_recursive = False
133     
134     bpy.types.Scene.default_recursive_ext = BoolProperty(
135         name='Recursive ext',
136         description='Load only clips with selected extension',
137         default=False)
138     scn.default_recursive_ext = False
139     
140     bpy.types.Scene.default_recursive_proxies = BoolProperty(
141         name='Recursive proxies',
142         description='Load in recursive folders + proxies',
143         default=False)
144     scn.default_recursive_proxies = False
145     
146     bpy.types.Scene.default_ext = EnumProperty(
147         items=functions.movieextdict,
148         name="ext enum",
149         default="3")
150     scn.default_ext = "3"
151     
152     bpy.types.Scene.scene_initialized = BoolProperty(
153         name='Init',
154         default=False)
155     scn.scene_initialized = True
156
157     return True
158
159    
160 # TRIM TIMELINE
161 class Sequencer_Extra_TrimTimeline(bpy.types.Operator):
162     bl_label = 'Trim to Timeline Content'
163     bl_idname = 'timeextra.trimtimeline'
164     bl_description = 'Automatically set start and end frames'
165     bl_options = {'REGISTER', 'UNDO'}
166
167     @classmethod
168     def poll(self, context):
169         scn = context.scene
170         if scn and scn.sequence_editor:
171             return scn.sequence_editor.sequences
172         else:
173             return False
174
175     def execute(self, context):
176         scn = context.scene
177         seq = scn.sequence_editor
178         meta_level = len(seq.meta_stack)
179         if meta_level > 0:
180             seq = seq.meta_stack[meta_level - 1]
181
182         frame_start = 300000
183         frame_end = -300000
184         for i in seq.sequences:
185             try:
186                 if i.frame_final_start < frame_start:
187                     frame_start = i.frame_final_start
188                 if i.frame_final_end > frame_end:
189                     frame_end = i.frame_final_end - 1
190             except AttributeError:
191                     pass
192
193         if frame_start != 300000:
194             scn.frame_start = frame_start
195         if frame_end != -300000:
196             scn.frame_end = frame_end
197         return {'FINISHED'}
198
199
200 # TRIM TIMELINE TO SELECTION
201 class Sequencer_Extra_TrimTimelineToSelection(bpy.types.Operator):
202     bl_label = 'Trim to Selection'
203     bl_idname = 'timeextra.trimtimelinetoselection'
204     bl_description = 'Set start and end frames to selection'
205     bl_options = {'REGISTER', 'UNDO'}
206
207     @classmethod
208     def poll(self, context):
209         scn = context.scene
210         if scn and scn.sequence_editor:
211             return scn.sequence_editor.sequences
212         else:
213             return False
214
215     def execute(self, context):
216         scn = context.scene
217         seq = scn.sequence_editor
218         meta_level = len(seq.meta_stack)
219         if meta_level > 0:
220             seq = seq.meta_stack[meta_level - 1]
221
222         frame_start = 300000
223         frame_end = -300000
224         for i in seq.sequences:
225             try:
226                 if i.frame_final_start < frame_start and i.select == True:
227                     frame_start = i.frame_final_start
228                 if i.frame_final_end > frame_end and i.select == True:
229                     frame_end = i.frame_final_end - 1
230             except AttributeError:
231                     pass
232
233         if frame_start != 300000:
234             scn.frame_start = frame_start
235         if frame_end != -300000:
236             scn.frame_end = frame_end
237         return {'FINISHED'}
238
239
240 # SLIDE STRIP
241 class Sequencer_Extra_SlideStrip(bpy.types.Operator):
242     bl_label = 'Slide'
243     bl_idname = 'sequencerextra.slide'
244     bl_description = 'Alter in and out points but not duration of a strip'
245     mode = EnumProperty(
246         name='Mode',
247         items=(
248         ('TOSTART', 'Current Frame to Strip Start', ''),
249         ('TOEND', 'Current Frame to Strip End', ''),
250         ('INPUT', 'Input...', '')),
251         default='INPUT',
252         options={'HIDDEN'})
253     bl_options = {'REGISTER', 'UNDO'}
254     
255     slide_offset = IntProperty(
256         name='Offset',
257         description='Number of frames to slide',
258         min=-250, max=250,
259         default=0)
260
261     @classmethod
262     def poll(self, context):
263         strip = functions.act_strip(context)
264         scn = context.scene
265         if scn and scn.sequence_editor and scn.sequence_editor.active_strip:
266             return strip.type in ('MOVIE', 'IMAGE', 'META', 'SCENE')
267         else:
268             return False
269
270     def execute(self, context):
271         strip = functions.act_strip(context)
272         scn = context.scene
273         cf = scn.frame_current
274
275         if self.mode == 'TOSTART':
276             sx = strip.frame_final_start - cf
277         elif self.mode == 'TOEND':
278             sx = strip.frame_final_end - cf
279         else:
280             sx = self.slide_offset
281
282         frame_end = strip.frame_start + strip.frame_duration
283         pad_left = strip.frame_final_start - strip.frame_start
284         pad_right = (frame_end - strip.frame_final_end) * -1
285
286         if sx > pad_left:
287             sx = pad_left
288         elif sx < pad_right:
289             sx = pad_right
290
291         strip.frame_start += sx
292         strip.frame_final_start -= sx
293         strip.frame_final_end -= sx
294
295         self.report({'INFO'}, 'Strip slid %d frame(s)' % (sx))
296         scn.default_slide_offset = sx
297         bpy.ops.sequencer.reload()
298         return {'FINISHED'}
299
300     def invoke(self, context, event):
301         scn = context.scene
302         initSceneProperties(context,scn)
303         self.slide_offset = scn.default_slide_offset
304         if self.mode == 'INPUT':
305             return context.window_manager.invoke_props_dialog(self)
306         else:
307             return self.execute(context)
308
309
310 # SLIDE GRAB
311 class Sequencer_Extra_SlideGrab(bpy.types.Operator):
312     bl_label = 'Slide Grab'
313     bl_idname = 'sequencerextra.slidegrab'
314     bl_description = 'Alter in and out points but not duration of a strip'
315     bl_options = {'REGISTER', 'UNDO'}
316
317     @classmethod
318     def poll(self, context):
319         strip = functions.act_strip(context)
320         scn = context.scene
321         if scn and scn.sequence_editor and scn.sequence_editor.active_strip:
322             return strip.type in ('MOVIE', 'IMAGE', 'META', 'SCENE')
323         else:
324             return False
325
326     def execute(self, context):
327         strip = functions.act_strip(context)
328         scn = context.scene
329
330         diff = self.prev_x - self.x
331         self.prev_x = self.x
332         sx = int(diff / 2)
333
334         frame_end = strip.frame_start + strip.frame_duration
335         pad_left = strip.frame_final_start - strip.frame_start
336         pad_right = (frame_end - strip.frame_final_end) * -1
337
338         if sx > pad_left:
339             sx = pad_left
340         elif sx < pad_right:
341             sx = pad_right
342
343         strip.frame_start += sx
344         strip.frame_final_start -= sx
345         strip.frame_final_end -= sx
346
347     def modal(self, context, event):
348         if event.type == 'MOUSEMOVE':
349             self.x = event.mouse_x
350             self.execute(context)
351         elif event.type == 'LEFTMOUSE':
352             return {'FINISHED'}
353         elif event.type in ('RIGHTMOUSE', 'ESC'):
354             return {'CANCELLED'}
355
356         return {'RUNNING_MODAL'}
357
358     def invoke(self, context, event):
359         scn = context.scene
360         self.x = event.mouse_x
361         self.prev_x = event.mouse_x
362         self.execute(context)
363         context.window_manager.modal_handler_add(self)
364         return {'RUNNING_MODAL'}
365
366
367 # FILE NAME TO STRIP NAME
368 class Sequencer_Extra_FileNameToStripName(bpy.types.Operator):
369     bl_label = 'File Name to Selected Strips Name'
370     bl_idname = 'sequencerextra.striprename'
371     bl_description = 'Set strip name to input file name'
372     bl_options = {'REGISTER', 'UNDO'}
373
374     @classmethod
375     def poll(self, context):
376         scn = context.scene
377         if scn and scn.sequence_editor:
378             return scn.sequence_editor.sequences
379         else:
380             return False
381
382     def execute(self, context):
383         scn = context.scene
384         seq = scn.sequence_editor
385         meta_level = len(seq.meta_stack)
386         if meta_level > 0:
387             seq = seq.meta_stack[meta_level - 1]
388         selection = False
389         for i in seq.sequences:
390             if i.select == True:
391                 if i.type == 'IMAGE' and not i.mute:
392                     selection = True
393                     i.name = i.elements[0].filename
394                 if (i.type == 'SOUND' or i.type == 'MOVIE') and not i.mute:
395                     selection = True
396                     i.name = bpy.path.display_name_from_filepath(i.filepath)
397         if selection == False:
398             self.report({'ERROR_INVALID_INPUT'},
399             'No image or movie strip selected')
400             return {'CANCELLED'}
401         return {'FINISHED'}
402
403
404 # NAVIGATE UP
405 class Sequencer_Extra_NavigateUp(bpy.types.Operator):
406     bl_label = 'Navigate Up'
407     bl_idname = 'sequencerextra.navigateup'
408     bl_description = 'Move to Parent Timeline'
409
410     @classmethod
411     def poll(self, context):
412         strip = functions.act_strip(context)
413         try:
414             if context.scene.sequence_editor.meta_stack:
415                 return True
416             else:
417                 return False
418         except:
419             return False
420
421     def execute(self, context):
422         if (functions.act_strip(context)):
423             strip = functions.act_strip(context)
424             seq_type = strip.type
425             if seq_type == 'META':
426                 context.scene.sequence_editor.active_strip = None
427
428         bpy.ops.sequencer.meta_toggle()
429         return {'FINISHED'}
430
431
432 # RIPPLE DELETE
433 class Sequencer_Extra_RippleDelete(bpy.types.Operator):
434     bl_label = 'Ripple Delete'
435     bl_idname = 'sequencerextra.rippledelete'
436     bl_description = 'Delete a strip and shift back following ones'
437     bl_options = {'REGISTER', 'UNDO'}
438
439     @classmethod
440     def poll(self, context):
441         strip = functions.act_strip(context)
442         scn = context.scene
443         if scn and scn.sequence_editor and scn.sequence_editor.active_strip:
444             return True
445         else:
446             return False
447
448     def execute(self, context):
449         scn = context.scene
450         seq = scn.sequence_editor
451         meta_level = len(seq.meta_stack)
452         if meta_level > 0:
453             seq = seq.meta_stack[meta_level - 1]
454         #strip = functions.act_strip(context)
455         for strip in context.selected_editable_sequences:
456             cut_frame = strip.frame_final_start
457             next_edit = 300000
458             bpy.ops.sequencer.select_all(action='DESELECT')
459             strip.select = True
460             bpy.ops.sequencer.delete()
461             striplist = []
462             for i in seq.sequences:
463                 try:
464                     if (i.frame_final_start > cut_frame
465                     and not i.mute):
466                         if i.frame_final_start < next_edit:
467                             next_edit = i.frame_final_start
468                     if not i.mute:
469                         striplist.append(i)
470                 except AttributeError:
471                         pass
472
473             if next_edit == 300000:
474                 return {'FINISHED'}
475             ripple_length = next_edit - cut_frame
476             for i in range(len(striplist)):
477                 str = striplist[i]
478                 try:
479                     if str.frame_final_start > cut_frame:
480                         str.frame_start = str.frame_start - ripple_length
481                 except AttributeError:
482                         pass
483             bpy.ops.sequencer.reload()
484         return {'FINISHED'}
485
486
487 # RIPPLE CUT
488 class Sequencer_Extra_RippleCut(bpy.types.Operator):
489     bl_label = 'Ripple Cut'
490     bl_idname = 'sequencerextra.ripplecut'
491     bl_description = 'Move a strip to buffer and shift back following ones'
492     bl_options = {'REGISTER', 'UNDO'}
493
494     @classmethod
495     def poll(self, context):
496         strip = functions.act_strip(context)
497         scn = context.scene
498         if scn and scn.sequence_editor and scn.sequence_editor.active_strip:
499             return True
500         else:
501             return False
502
503     def execute(self, context):
504         scn = context.scene
505         seq = scn.sequence_editor
506         meta_level = len(seq.meta_stack)
507         if meta_level > 0:
508             seq = seq.meta_stack[meta_level - 1]
509         strip = functions.act_strip(context)
510         bpy.ops.sequencer.select_all(action='DESELECT')
511         strip.select = True
512         temp_cf = scn.frame_current
513         scn.frame_current = strip.frame_final_start
514         bpy.ops.sequencer.copy()
515         scn.frame_current = temp_cf
516
517         bpy.ops.sequencerextra.rippledelete()
518         return {'FINISHED'}
519
520
521 # INSERT
522 class Sequencer_Extra_Insert(bpy.types.Operator):
523     bl_label = 'Insert'
524     bl_idname = 'sequencerextra.insert'
525     bl_description = 'Move active strip to current frame and shift '\
526     'forward following ones'
527     singlechannel = BoolProperty(
528     name='Single Channel',
529     default=False)
530     bl_options = {'REGISTER', 'UNDO'}
531
532     @classmethod
533     def poll(self, context):
534         strip = functions.act_strip(context)
535         scn = context.scene
536         if scn and scn.sequence_editor and scn.sequence_editor.active_strip:
537             return True
538         else:
539             return False
540
541     def execute(self, context):
542         scn = context.scene
543         seq = scn.sequence_editor
544         meta_level = len(seq.meta_stack)
545         if meta_level > 0:
546             seq = seq.meta_stack[meta_level - 1]
547         strip = functions.act_strip(context)
548         gap = strip.frame_final_duration
549         bpy.ops.sequencer.select_all(action='DESELECT')
550         current_frame = scn.frame_current
551
552         striplist = []
553         for i in seq.sequences:
554             try:
555                 if (i.frame_final_start >= current_frame
556                 and not i.mute):
557                     if self.singlechannel == True:
558                         if i.channel == strip.channel:
559                             striplist.append(i)
560                     else:
561                         striplist.append(i)
562             except AttributeError:
563                     pass
564         try:
565             bpy.ops.sequencerextra.selectcurrentframe('EXEC_DEFAULT',
566             mode='AFTER')
567         except:
568             self.report({'ERROR_INVALID_INPUT'}, 'Execution Error, '\
569             'check your Blender version')
570             return {'CANCELLED'}
571
572         for i in range(len(striplist)):
573             str = striplist[i]
574             try:
575                 if str.select == True:
576                     str.frame_start += gap
577             except AttributeError:
578                     pass
579         try:
580             diff = current_frame - strip.frame_final_start
581             strip.frame_start += diff
582         except AttributeError:
583                 pass
584
585         strip = functions.act_strip(context)
586         scn.frame_current += strip.frame_final_duration
587         bpy.ops.sequencer.reload()
588
589         return {'FINISHED'}
590
591
592 # PLACE FROM FILE BROWSER
593 class Sequencer_Extra_PlaceFromFileBrowser(bpy.types.Operator):
594     bl_label = 'Place'
595     bl_idname = 'sequencerextra.placefromfilebrowser'
596     bl_description = 'Place or insert active file from File Browser'
597     insert = BoolProperty(
598     name='Insert',
599     default=False)
600     bl_options = {'REGISTER', 'UNDO'}
601
602     def execute(self, context):
603         scn = context.scene
604         for a in context.window.screen.areas:
605             if a.type == 'FILE_BROWSER':
606                 params = a.spaces[0].params
607                 break
608         try:
609             params
610         except UnboundLocalError:
611             self.report({'ERROR_INVALID_INPUT'}, 'No visible File Browser')
612             return {'CANCELLED'}
613
614         if params.filename == '':
615             self.report({'ERROR_INVALID_INPUT'}, 'No file selected')
616             return {'CANCELLED'}
617
618         path = os.path.join(params.directory, params.filename)
619         frame = context.scene.frame_current
620         strip_type = functions.detect_strip_type(params.filename)
621
622         try:
623             if strip_type == 'IMAGE':
624                 image_file = []
625                 filename = {"name": params.filename}
626                 image_file.append(filename)
627                 f_in = scn.frame_current
628                 f_out = f_in + scn.render.fps - 1
629                 bpy.ops.sequencer.image_strip_add(files=image_file,
630                 directory=params.directory, frame_start=f_in,
631                 frame_end=f_out, relative_path=False)
632             elif strip_type == 'MOVIE':
633                 bpy.ops.sequencer.movie_strip_add(filepath=path,
634                 frame_start=frame, relative_path=False)
635             elif strip_type == 'SOUND':
636                 bpy.ops.sequencer.sound_strip_add(filepath=path,
637                 frame_start=frame, relative_path=False)
638             else:
639                 self.report({'ERROR_INVALID_INPUT'}, 'Invalid file format')
640                 return {'CANCELLED'}
641         except:
642             self.report({'ERROR_INVALID_INPUT'}, 'Error loading file')
643             return {'CANCELLED'}
644
645         if self.insert == True:
646             try:
647                 striplist = []
648                 for i in bpy.context.selected_editable_sequences:
649                     if (i.select == True and i.type == "SOUND"):
650                         striplist.append(i)
651                 bpy.ops.sequencerextra.insert()
652                 if striplist[0]:
653                     striplist[0].frame_start = frame
654             except:
655                 self.report({'ERROR_INVALID_INPUT'}, 'Execution Error, '\
656                 'check your Blender version')
657                 return {'CANCELLED'}
658         else:
659             strip = functions.act_strip(context)
660             scn.frame_current += strip.frame_final_duration
661             bpy.ops.sequencer.reload()
662
663         return {'FINISHED'}
664
665
666 # SELECT BY TYPE
667 class Sequencer_Extra_SelectAllByType(bpy.types.Operator):
668     bl_label = 'All by Type'
669     bl_idname = 'sequencerextra.select_all_by_type'
670     bl_description = 'Select all the strips of the same type'
671     type = EnumProperty(
672             name='Strip Type',
673             items=(
674             ('ACTIVE', 'Same as Active Strip', ''),
675             ('IMAGE', 'Image', ''),
676             ('META', 'Meta', ''),
677             ('SCENE', 'Scene', ''),
678             ('MOVIE', 'Movie', ''),
679             ('SOUND', 'Sound', ''),
680             ('TRANSFORM', 'Transform', ''),
681             ('COLOR', 'Color', '')),
682             default='ACTIVE',
683             )
684     bl_options = {'REGISTER', 'UNDO'}
685
686     @classmethod
687     def poll(self, context):
688         scn = context.scene
689         if scn and scn.sequence_editor:
690             return scn.sequence_editor.sequences
691         else:
692             return False
693
694     def execute(self, context):
695         strip_type = self.type
696         scn = context.scene
697         seq = scn.sequence_editor
698         meta_level = len(seq.meta_stack)
699         if meta_level > 0:
700             seq = seq.meta_stack[meta_level - 1]
701         active_strip = functions.act_strip(context)
702         if strip_type == 'ACTIVE':
703             if active_strip == None:
704                 self.report({'ERROR_INVALID_INPUT'},
705                 'No active strip')
706                 return {'CANCELLED'}
707             strip_type = active_strip.type
708
709         striplist = []
710         for i in seq.sequences:
711             try:
712                 if (i.type == strip_type
713                 and not i.mute):
714                     striplist.append(i)
715             except AttributeError:
716                     pass
717         for i in range(len(striplist)):
718             str = striplist[i]
719             try:
720                 str.select = True
721             except AttributeError:
722                     pass
723
724         return {'FINISHED'}
725
726
727 # CURRENT-FRAME-AWARE SELECT
728 class Sequencer_Extra_SelectCurrentFrame(bpy.types.Operator):
729     bl_label = 'Current-Frame-Aware Select'
730     bl_idname = 'sequencerextra.selectcurrentframe'
731     bl_description = 'Select strips according to current frame'
732     mode = EnumProperty(
733             name='Mode',
734             items=(
735             ('BEFORE', 'Before Current Frame', ''),
736             ('AFTER', 'After Current Frame', ''),
737             ('ON', 'On Current Frame', '')),
738             default='BEFORE',
739             )
740     bl_options = {'REGISTER', 'UNDO'}
741
742     @classmethod
743     def poll(self, context):
744         scn = context.scene
745         if scn and scn.sequence_editor:
746             return scn.sequence_editor.sequences
747         else:
748             return False
749
750     def execute(self, context):
751         mode = self.mode
752         scn = context.scene
753         seq = scn.sequence_editor
754         cf = scn.frame_current
755         meta_level = len(seq.meta_stack)
756         if meta_level > 0:
757             seq = seq.meta_stack[meta_level - 1]
758
759         if mode == 'AFTER':
760             for i in seq.sequences:
761                 try:
762                     if (i.frame_final_start >= cf
763                     and not i.mute):
764                         i.select = True
765                 except AttributeError:
766                         pass
767         elif mode == 'ON':
768             for i in seq.sequences:
769                 try:
770                     if (i.frame_final_start <= cf
771                     and i.frame_final_end > cf
772                     and not i.mute):
773                         i.select = True
774                 except AttributeError:
775                         pass
776         else:
777             for i in seq.sequences:
778                 try:
779                     if (i.frame_final_end < cf
780                     and not i.mute):
781                         i.select = True
782                 except AttributeError:
783                         pass
784
785         return {'FINISHED'}
786
787
788 # OPEN IMAGE WITH EXTERNAL EDITOR
789 class Sequencer_Extra_EditExternally(bpy.types.Operator):
790     bl_label = 'Open with External Editor'
791     bl_idname = 'sequencerextra.editexternally'
792     bl_description = 'Open with the default external image editor'
793
794     @classmethod
795     def poll(self, context):
796         strip = functions.act_strip(context)
797         scn = context.scene
798         if scn and scn.sequence_editor and scn.sequence_editor.active_strip:
799             return strip.type == 'IMAGE'
800         else:
801             return False
802
803     def execute(self, context):
804         strip = functions.act_strip(context)
805         scn = context.scene
806         base_dir = bpy.path.abspath(strip.directory)
807         strip_elem = strip.strip_elem_from_frame(scn.frame_current)
808         path = base_dir + strip_elem.filename
809
810         try:
811             bpy.ops.image.external_edit(filepath=path)
812         except:
813             self.report({'ERROR_INVALID_INPUT'},
814             'Please specify an Image Editor in Preferences > File')
815             return {'CANCELLED'}
816
817         return {'FINISHED'}
818
819
820 # OPEN IMAGE WITH EDITOR
821 class Sequencer_Extra_Edit(bpy.types.Operator):
822     bl_label = 'Open with Editor'
823     bl_idname = 'sequencerextra.edit'
824     bl_description = 'Open with Movie Clip or Image Editor'
825
826     @classmethod
827     def poll(self, context):
828         strip = functions.act_strip(context)
829         scn = context.scene
830         if scn and scn.sequence_editor and scn.sequence_editor.active_strip:
831             return strip.type in ('MOVIE', 'IMAGE')
832         else:
833             return False
834
835     def execute(self, context):
836         strip = functions.act_strip(context)
837         scn = context.scene
838         data_exists = False
839
840         if strip.type == 'MOVIE':
841             path = strip.filepath
842
843             for i in bpy.data.movieclips:
844                 if i.filepath == path:
845                     data_exists = True
846                     data = i
847
848             if data_exists == False:
849                 try:
850                     data = bpy.data.movieclips.load(filepath=path)
851                 except:
852                     self.report({'ERROR_INVALID_INPUT'}, 'Error loading file')
853                     return {'CANCELLED'}
854
855         elif strip.type == 'IMAGE':
856             base_dir = bpy.path.abspath(strip.directory)
857             strip_elem = strip.strip_elem_from_frame(scn.frame_current)
858             elem_name = strip_elem.filename
859             path = base_dir + elem_name
860
861             for i in bpy.data.images:
862                 if i.filepath == path:
863                     data_exists = True
864                     data = i
865
866             if data_exists == False:
867                 try:
868                     data = bpy.data.images.load(filepath=path)
869                 except:
870                     self.report({'ERROR_INVALID_INPUT'}, 'Error loading file')
871                     return {'CANCELLED'}
872
873         if strip.type == 'MOVIE':
874             for a in context.window.screen.areas:
875                 if a.type == 'CLIP_EDITOR':
876                     a.spaces[0].clip = data
877         elif strip.type == 'IMAGE':
878             for a in context.window.screen.areas:
879                 if a.type == 'IMAGE_EDITOR':
880                     a.spaces[0].image = data
881
882         return {'FINISHED'}
883
884
885 # COPY STRIP PROPERTIES
886 class Sequencer_Extra_CopyProperties(bpy.types.Operator):
887     bl_label = 'Copy Properties'
888     bl_idname = 'sequencerextra.copyproperties'
889     bl_description = 'Copy properties of active strip to selected strips'
890     bl_options = {'REGISTER', 'UNDO'}
891
892     prop = EnumProperty(
893     name='Property',
894     items=[
895     # COMMON
896     ('name', 'Name', ''),
897     ('blend_alpha', 'Opacity', ''),
898     ('blend_type', 'Blend Mode', ''),
899     ('animation_offset', 'Input - Trim Duration', ''),
900     # NON-SOUND
901     ('use_translation', 'Input - Image Offset', ''),
902     ('crop', 'Input - Image Crop', ''),
903     ('proxy', 'Proxy / Timecode', ''),
904     ('strobe', 'Filter - Strobe', ''),
905     ('color_multiply', 'Filter - Multiply', ''),
906     ('color_saturation', 'Filter - Saturation', ''),
907     ('deinterlace', 'Filter - De-Interlace', ''),
908     ('flip', 'Filter - Flip', ''),
909     ('float', 'Filter - Convert Float', ''),
910     ('alpha_mode', 'Filter - Alpha Mode', ''),
911     ('reverse', 'Filter - Backwards', ''),
912     # SOUND
913     ('pan', 'Sound - Pan', ''),
914     ('pitch', 'Sound - Pitch', ''),
915     ('volume', 'Sound - Volume', ''),
916     ('cache', 'Sound - Caching', ''),
917     # IMAGE
918     ('directory', 'Image - Directory', ''),
919     # MOVIE
920     ('mpeg_preseek', 'Movie - MPEG Preseek', ''),
921     ('stream_index', 'Movie - Stream Index', ''),
922     # WIPE
923     ('wipe', 'Effect - Wipe', ''),
924     # TRANSFORM
925     ('transform', 'Effect - Transform', ''),
926     # COLOR
927     ('color', 'Effect - Color', ''),
928     # SPEED
929     ('speed', 'Effect - Speed', ''),
930     # MULTICAM
931     ('multicam_source', 'Effect - Multicam Source', ''),
932     # EFFECT
933     ('effect_fader', 'Effect - Effect Fader', ''),
934     ],
935     default='blend_alpha')
936
937     @classmethod
938     def poll(self, context):
939         strip = functions.act_strip(context)
940         scn = context.scene
941         if scn and scn.sequence_editor and scn.sequence_editor.active_strip:
942             return True
943         else:
944             return False
945
946     def execute(self, context):
947         scn = context.scene
948         seq = scn.sequence_editor
949         meta_level = len(seq.meta_stack)
950         if meta_level > 0:
951             seq = seq.meta_stack[meta_level - 1]
952         strip = functions.act_strip(context)
953
954         for i in seq.sequences:
955             if (i.select == True and not i.mute):
956                 try:
957                     if self.prop == 'name':
958                         i.name = strip.name
959                     elif self.prop == 'blend_alpha':
960                         i.blend_alpha = strip.blend_alpha
961                     elif self.prop == 'blend_type':
962                         i.blend_type = strip.blend_type
963                     elif self.prop == 'animation_offset':
964                         i.animation_offset_start = strip.animation_offset_start
965                         i.animation_offset_end = strip.animation_offset_end
966                     elif self.prop == 'use_translation':
967                         i.use_translation = strip.use_translation
968                         i.transform.offset_x = strip.transform.offset_x
969                         i.transform.offset_y = strip.transform.offset_y
970                     elif self.prop == 'crop':
971                         i.use_crop = strip.use_crop
972                         i.crop.min_x = strip.crop.min_x
973                         i.crop.min_y = strip.crop.min_y
974                         i.crop.max_x = strip.crop.max_x
975                         i.crop.max_y = strip.crop.max_y
976                     elif self.prop == 'proxy':
977                         i.use_proxy = strip.use_proxy
978                         i.use_proxy_custom_file = strip.use_proxy_custom_file
979                         p = strip.use_proxy_custom_directory  # pep80
980                         i.use_proxy_custom_directory = p
981                         i.proxy.filepath = strip.proxy.filepath
982                         i.proxy.directory = strip.proxy.directory
983                         i.proxy.build_25 = strip.proxy.build_25
984                         i.proxy.build_50 = strip.proxy.build_50
985                         i.proxy.build_75 = strip.proxy.build_75
986                         i.proxy.build_100 = strip.proxy.build_100
987                         i.proxy.quality = strip.proxy.quality
988                         i.proxy.timecode = strip.proxy.timecode
989                     elif self.prop == 'strobe':
990                         i.strobe = strip.strobe
991                     elif self.prop == 'color_multiply':
992                         i.color_multiply = strip.color_multiply
993                     elif self.prop == 'color_saturation':
994                         i.color_saturation = strip.color_saturation
995                     elif self.prop == 'deinterlace':
996                         i.use_deinterlace = strip.use_deinterlace
997                     elif self.prop == 'flip':
998                         i.use_flip_x = strip.use_flip_x
999                         i.use_flip_y = strip.use_flip_y
1000                     elif self.prop == 'float':
1001                         i.use_float = strip.use_float
1002                     elif self.prop == 'alpha_mode':
1003                         i.alpha_mode = strip.alpha_mode
1004                     elif self.prop == 'reverse':
1005                         i.use_reverse_frames = strip.use_reverse_frames
1006                     elif self.prop == 'pan':
1007                         i.pan = strip.pan
1008                     elif self.prop == 'pitch':
1009                         i.pitch = strip.pitch
1010                     elif self.prop == 'volume':
1011                         i.volume = strip.volume
1012                     elif self.prop == 'cache':
1013                         i.use_memory_cache = strip.use_memory_cache
1014                     elif self.prop == 'directory':
1015                         i.directory = strip.directory
1016                     elif self.prop == 'mpeg_preseek':
1017                         i.mpeg_preseek = strip.mpeg_preseek
1018                     elif self.prop == 'stream_index':
1019                         i.stream_index = strip.stream_index
1020                     elif self.prop == 'wipe':
1021                         i.angle = strip.angle
1022                         i.blur_width = strip.blur_width
1023                         i.direction = strip.direction
1024                         i.transition_type = strip.transition_type
1025                     elif self.prop == 'transform':
1026                         i.interpolation = strip.interpolation
1027                         i.rotation_start = strip.rotation_start
1028                         i.use_uniform_scale = strip.use_uniform_scale
1029                         i.scale_start_x = strip.scale_start_x
1030                         i.scale_start_y = strip.scale_start_y
1031                         i.translation_unit = strip.translation_unit
1032                         i.translate_start_x = strip.translate_start_x
1033                         i.translate_start_y = strip.translate_start_y
1034                     elif self.prop == 'color':
1035                         i.color = strip.color
1036                     elif self.prop == 'speed':
1037                         i.use_default_fade = strip.use_default_fade
1038                         i.speed_factor = strip.speed_factor
1039                         i.use_as_speed = strip.use_as_speed
1040                         i.scale_to_length = strip.scale_to_length
1041                         i.multiply_speed = strip.multiply_speed
1042                         i.use_frame_blend = strip.use_frame_blend
1043                     elif self.prop == 'multicam_source':
1044                         i.multicam_source = strip.multicam_source
1045                     elif self.prop == 'effect_fader':
1046                         i.use_default_fade = strip.use_default_fade
1047                         i.effect_fader = strip.effect_fader
1048                 except:
1049                     pass
1050
1051         bpy.ops.sequencer.reload()
1052         return {'FINISHED'}
1053
1054
1055 # FADE IN AND OUT
1056 class Sequencer_Extra_FadeInOut(bpy.types.Operator):
1057     bl_idname = 'sequencerextra.fadeinout'
1058     bl_label = 'Fade...'
1059     bl_description = 'Fade volume or opacity of active strip'
1060     mode = EnumProperty(
1061             name='Direction',
1062             items=(
1063             ('IN', 'Fade In...', ''),
1064             ('OUT', 'Fade Out...', ''),
1065             ('INOUT', 'Fade In and Out...', '')),
1066             default='IN',
1067             )
1068     bl_options = {'REGISTER', 'UNDO'}
1069     
1070     fade_duration = IntProperty(
1071         name='Duration',
1072         description='Number of frames to fade',
1073         min=1, max=250,
1074         default=25)
1075     fade_amount = FloatProperty(
1076         name='Amount',
1077         description='Maximum value of fade',
1078         min=0.0,
1079         max=100.0,
1080         default=1.0)
1081
1082     @classmethod
1083     def poll(cls, context):
1084         scn = context.scene
1085         if scn and scn.sequence_editor and scn.sequence_editor.active_strip:
1086             return True
1087         else:
1088             return False
1089
1090     def execute(self, context):
1091         seq = context.scene.sequence_editor
1092         scn = context.scene
1093         strip = seq.active_strip
1094         tmp_current_frame = context.scene.frame_current
1095
1096         if strip.type == 'SOUND':
1097             if(self.mode) == 'OUT':
1098                 scn.frame_current = strip.frame_final_end - self.fade_duration
1099                 strip.volume = self.fade_amount
1100                 strip.keyframe_insert('volume')
1101                 scn.frame_current = strip.frame_final_end
1102                 strip.volume = 0
1103                 strip.keyframe_insert('volume')
1104             elif(self.mode) == 'INOUT':
1105                 scn.frame_current = strip.frame_final_start
1106                 strip.volume = 0
1107                 strip.keyframe_insert('volume')
1108                 scn.frame_current += self.fade_duration
1109                 strip.volume = self.fade_amount
1110                 strip.keyframe_insert('volume')
1111                 scn.frame_current = strip.frame_final_end - self.fade_duration
1112                 strip.volume = self.fade_amount
1113                 strip.keyframe_insert('volume')
1114                 scn.frame_current = strip.frame_final_end
1115                 strip.volume = 0
1116                 strip.keyframe_insert('volume')
1117             else:
1118                 scn.frame_current = strip.frame_final_start
1119                 strip.volume = 0
1120                 strip.keyframe_insert('volume')
1121                 scn.frame_current += self.fade_duration
1122                 strip.volume = self.fade_amount
1123                 strip.keyframe_insert('volume')
1124
1125         else:
1126             if(self.mode) == 'OUT':
1127                 scn.frame_current = strip.frame_final_end - self.fade_duration
1128                 strip.blend_alpha = self.fade_amount
1129                 strip.keyframe_insert('blend_alpha')
1130                 scn.frame_current = strip.frame_final_end
1131                 strip.blend_alpha = 0
1132                 strip.keyframe_insert('blend_alpha')
1133             elif(self.mode) == 'INOUT':
1134                 scn.frame_current = strip.frame_final_start
1135                 strip.blend_alpha = 0
1136                 strip.keyframe_insert('blend_alpha')
1137                 scn.frame_current += self.fade_duration
1138                 strip.blend_alpha = self.fade_amount
1139                 strip.keyframe_insert('blend_alpha')
1140                 scn.frame_current = strip.frame_final_end - self.fade_duration
1141                 strip.blend_alpha = self.fade_amount
1142                 strip.keyframe_insert('blend_alpha')
1143                 scn.frame_current = strip.frame_final_end
1144                 strip.blend_alpha = 0
1145                 strip.keyframe_insert('blend_alpha')
1146             else:
1147                 scn.frame_current = strip.frame_final_start
1148                 strip.blend_alpha = 0
1149                 strip.keyframe_insert('blend_alpha')
1150                 scn.frame_current += self.fade_duration
1151                 strip.blend_alpha = self.fade_amount
1152                 strip.keyframe_insert('blend_alpha')
1153
1154         scn.frame_current = tmp_current_frame
1155
1156         scn.default_fade_duration = self.fade_duration
1157         scn.default_fade_amount = self.fade_amount
1158         return{'FINISHED'}
1159
1160     def invoke(self, context, event):
1161         scn = context.scene
1162         initSceneProperties(context, scn)
1163         self.fade_duration = scn.default_fade_duration
1164         self.fade_amount = scn.default_fade_amount
1165         return context.window_manager.invoke_props_dialog(self)
1166
1167
1168 # EXTEND TO FILL
1169 class Sequencer_Extra_ExtendToFill(bpy.types.Operator):
1170     bl_idname = 'sequencerextra.extendtofill'
1171     bl_label = 'Extend to Fill'
1172     bl_description = 'Extend active strip forward to fill adjacent space'
1173     bl_options = {'REGISTER', 'UNDO'}
1174
1175     @classmethod
1176     def poll(cls, context):
1177         scn = context.scene
1178         if scn and scn.sequence_editor and scn.sequence_editor.active_strip:
1179             return True
1180         else:
1181             return False
1182
1183     def execute(self, context):
1184         scn = context.scene
1185         seq = scn.sequence_editor
1186         meta_level = len(seq.meta_stack)
1187         if meta_level > 0:
1188             seq = seq.meta_stack[meta_level - 1]
1189         strip = functions.act_strip(context)
1190         chn = strip.channel
1191         stf = strip.frame_final_end
1192         enf = 300000
1193
1194         for i in seq.sequences:
1195             ffs = i.frame_final_start
1196             if (i.channel == chn and ffs > stf):
1197                 if ffs < enf:
1198                     enf = ffs
1199         if enf == 300000 and stf < scn.frame_end:
1200             enf = scn.frame_end
1201
1202         if enf == 300000 or enf == stf:
1203             self.report({'ERROR_INVALID_INPUT'}, 'Unable to extend')
1204             return {'CANCELLED'}
1205         else:
1206             strip.frame_final_end = enf
1207
1208         bpy.ops.sequencer.reload()
1209         return {'FINISHED'}
1210
1211
1212 # DISTRIBUTE
1213 class Sequencer_Extra_Distribute(bpy.types.Operator):
1214     bl_idname = 'sequencerextra.distribute'
1215     bl_label = 'Distribute...'
1216     bl_description = 'Evenly distribute selected strips'
1217     bl_options = {'REGISTER', 'UNDO'}
1218
1219     @classmethod
1220     def poll(self, context):
1221         scn = context.scene
1222         if scn and scn.sequence_editor:
1223             return scn.sequence_editor.sequences
1224         else:
1225             return False
1226
1227     def execute(self, context):
1228         scn = context.scene
1229         seq = scn.sequence_editor
1230         seq_all = scn.sequence_editor
1231         meta_level = len(seq.meta_stack)
1232         if meta_level > 0:
1233             seq = seq.meta_stack[meta_level - 1]
1234         seq_list = {}
1235         first_start = 300000
1236         item_num = 0
1237         for i in seq.sequences:
1238             if i.select == True:
1239                 seq_list[i.frame_start] = i.name
1240                 item_num += 1
1241                 if i.frame_start < first_start:
1242                     first_start = i.frame_start
1243         n = item_num - 1
1244         if(self.distribute_reverse):
1245             for key in sorted(seq_list.keys()):
1246                 dest = first_start + (n * self.distribute_offset)
1247                 seq_all.sequences_all[str(seq_list[key])].frame_start = dest
1248                 n -= 1
1249         else:
1250             for key in sorted(seq_list.keys(), reverse=True):
1251                 dest = first_start + (n * self.distribute_offset)
1252                 seq_all.sequences_all[str(seq_list[key])].frame_start = dest
1253                 n -= 1
1254
1255         scn.default_distribute_offset = self.distribute_offset
1256         scn.default_distribute_reverse = self.distribute_reverse
1257         return{'FINISHED'}
1258
1259     def invoke(self, context, event):
1260         scn = context.scene
1261         initSceneProperties(context, scn)
1262         self.distribute_offset = scn.default_distribute_offset
1263         self.distribute_reverse = scn.default_distribute_reverse
1264         return context.window_manager.invoke_props_dialog(self)
1265
1266
1267 # SKIP ONE SECOND
1268 class Sequencer_Extra_FrameSkip(bpy.types.Operator):
1269     bl_label = 'Skip One Second'
1270     bl_idname = 'screenextra.frame_skip'
1271     bl_description = 'Skip through the Timeline by one-second increments'
1272     bl_options = {'REGISTER', 'UNDO'}
1273     back = BoolProperty(
1274         name='Back',
1275         default=False)
1276
1277     def execute(self, context):
1278         one_second = bpy.context.scene.render.fps
1279         if self.back == True:
1280             one_second *= -1
1281         bpy.ops.screen.frame_offset(delta=one_second)
1282         return {'FINISHED'}
1283
1284
1285 # JOG/SHUTTLE
1286 class Sequencer_Extra_JogShuttle(bpy.types.Operator):
1287     bl_label = 'Jog/Shuttle'
1288     bl_idname = 'sequencerextra.jogshuttle'
1289     bl_description = 'Jog through current sequence'
1290
1291     def execute(self, context):
1292         scn = context.scene
1293         start_frame = scn.frame_start
1294         end_frame = scn.frame_end
1295         duration = end_frame - start_frame
1296         diff = self.x - self.init_x
1297         diff /= 5
1298         diff = int(diff)
1299         extended_frame = diff + (self.init_current_frame - start_frame)
1300         looped_frame = extended_frame % (duration + 1)
1301         target_frame = start_frame + looped_frame
1302         context.scene.frame_current = target_frame
1303
1304     def modal(self, context, event):
1305         if event.type == 'MOUSEMOVE':
1306             self.x = event.mouse_x
1307             self.execute(context)
1308         elif event.type == 'LEFTMOUSE':
1309             return {'FINISHED'}
1310         elif event.type in ('RIGHTMOUSE', 'ESC'):
1311             return {'CANCELLED'}
1312
1313         return {'RUNNING_MODAL'}
1314
1315     def invoke(self, context, event):
1316         scn = context.scene
1317         self.x = event.mouse_x
1318         self.init_x = self.x
1319         self.init_current_frame = scn.frame_current
1320         self.execute(context)
1321         context.window_manager.modal_handler_add(self)
1322         return {'RUNNING_MODAL'}
1323
1324
1325 # OPEN IN MOVIE CLIP EDITOR FROM FILE BROWSER
1326 class Clip_Extra_OpenFromFileBrowser(bpy.types.Operator):
1327     bl_label = 'Open from File Browser'
1328     bl_idname = 'clipextra.openfromfilebrowser'
1329     bl_description = 'Load a Movie or Image Sequence from File Browser'
1330     bl_options = {'REGISTER', 'UNDO'}
1331
1332     def execute(self, context):
1333         for a in context.window.screen.areas:
1334             if a.type == 'FILE_BROWSER':
1335                 params = a.spaces[0].params
1336                 break
1337         try:
1338             params
1339         except:
1340             self.report({'ERROR_INVALID_INPUT'}, 'No visible File Browser')
1341             return {'CANCELLED'}
1342
1343         if params.filename == '':
1344             self.report({'ERROR_INVALID_INPUT'}, 'No file selected')
1345             return {'CANCELLED'}
1346
1347         strip = functions.act_strip(context)
1348         path = params.directory + params.filename
1349         strip_type = functions.detect_strip_type(params.filename)
1350         data_exists = False
1351
1352         if strip_type in ('MOVIE', 'IMAGE'):
1353             for i in bpy.data.movieclips:
1354                 if i.filepath == path:
1355                     data_exists = True
1356                     data = i
1357
1358             if data_exists == False:
1359                 try:
1360                     data = bpy.data.movieclips.load(filepath=path)
1361                 except:
1362                     self.report({'ERROR_INVALID_INPUT'}, 'Error loading file')
1363                     return {'CANCELLED'}
1364         else:
1365             self.report({'ERROR_INVALID_INPUT'}, 'Invalid file format')
1366             return {'CANCELLED'}
1367
1368         for a in context.window.screen.areas:
1369             if a.type == 'CLIP_EDITOR':
1370                 a.spaces[0].clip = data
1371
1372         return {'FINISHED'}
1373
1374
1375 # OPEN IN MOVIE CLIP EDITOR FROM SEQUENCER
1376 class Clip_Extra_OpenActiveStrip(bpy.types.Operator):
1377     bl_label = 'Open Active Strip'
1378     bl_idname = 'clipextra.openactivestrip'
1379     bl_description = 'Load a Movie or Image Sequence from Sequence Editor'
1380     bl_options = {'REGISTER', 'UNDO'}
1381
1382     @classmethod
1383     def poll(cls, context):
1384         scn = context.scene
1385         strip = functions.act_strip(context)
1386         if scn and scn.sequence_editor and scn.sequence_editor.active_strip:
1387             return strip.type in ('MOVIE', 'IMAGE')
1388         else:
1389             return False
1390
1391     def execute(self, context):
1392         strip = functions.act_strip(context)
1393         data_exists = False
1394
1395         if strip.type == 'MOVIE':
1396             path = strip.filepath
1397         elif strip.type == 'IMAGE':
1398             base_dir = bpy.path.relpath(strip.directory)
1399             filename = strip.elements[0].filename
1400             path = base_dir + '/' + filename
1401         else:
1402             self.report({'ERROR_INVALID_INPUT'}, 'Invalid file format')
1403             return {'CANCELLED'}
1404
1405         for i in bpy.data.movieclips:
1406             if i.filepath == path:
1407                 data_exists = True
1408                 data = i
1409         if data_exists == False:
1410             try:
1411                 data = bpy.data.movieclips.load(filepath=path)
1412             except:
1413                 self.report({'ERROR_INVALID_INPUT'}, 'Error loading file')
1414                 return {'CANCELLED'}
1415
1416         for a in context.window.screen.areas:
1417             if a.type == 'CLIP_EDITOR':
1418                 a.spaces[0].clip = data
1419
1420         return {'FINISHED'}
1421
1422
1423 # PLACE FROM FILE BROWSER WITH PROXY
1424 class Sequencer_Extra_PlaceFromFileBrowserProxy(bpy.types.Operator):
1425     bl_label = 'Place'
1426     bl_idname = 'sequencerextra.placefromfilebrowserproxy'
1427     bl_description = 'Place or insert active file from File Browser, '\
1428     'and add proxy file with according suffix and extension'
1429     insert = BoolProperty(name='Insert', default=False)
1430     build_25 = BoolProperty(name='default_build_25',
1431         description='default build_25',
1432         default=True)
1433     build_50 = BoolProperty(name='default_build_50',
1434         description='default build_50',
1435         default=True)
1436     build_75 = BoolProperty(name='default_build_75',
1437         description='default build_75',
1438         default=True)
1439     build_100 = BoolProperty(name='default_build_100',
1440         description='default build_100',
1441         default=True)
1442     proxy_suffix = StringProperty(
1443         name='default proxy suffix',
1444         description='default proxy filename suffix',
1445         default="-25")
1446     proxy_extension = StringProperty(
1447         name='default proxy extension',
1448         description='default proxy extension',
1449         default=".mkv")
1450     proxy_path = StringProperty(
1451         name='default proxy path',
1452         description='default proxy path',
1453         default="")
1454
1455     bl_options = {'REGISTER', 'UNDO'}
1456
1457     def invoke(self, context, event):
1458         scn = context.scene
1459         initSceneProperties(context, scn)
1460         self.build_25 = scn.default_build_25
1461         self.build_50 = scn.default_build_50
1462         self.build_75 = scn.default_build_75
1463         self.build_100 = scn.default_build_100
1464         self.proxy_suffix = scn.default_proxy_suffix
1465         self.proxy_extension = scn.default_proxy_extension
1466         self.proxy_path = scn.default_proxy_path
1467
1468         return context.window_manager.invoke_props_dialog(self)
1469
1470     def execute(self, context):
1471         scn = context.scene
1472         for a in context.window.screen.areas:
1473             if a.type == 'FILE_BROWSER':
1474                 params = a.spaces[0].params
1475                 break
1476         try:
1477             params
1478         except UnboundLocalError:
1479             self.report({'ERROR_INVALID_INPUT'}, 'No visible File Browser')
1480             return {'CANCELLED'}
1481
1482         if params.filename == '':
1483             self.report({'ERROR_INVALID_INPUT'}, 'No file selected (proxy)')
1484             return {'CANCELLED'}
1485         path = os.path.join(params.directory, params.filename)
1486         frame = context.scene.frame_current
1487         strip_type = functions.detect_strip_type(params.filename)
1488
1489         try:
1490             if strip_type == 'IMAGE':
1491                 image_file = []
1492                 filename = {"name": params.filename}
1493                 image_file.append(filename)
1494                 f_in = scn.frame_current
1495                 f_out = f_in + scn.render.fps - 1
1496                 bpy.ops.sequencer.image_strip_add(files=image_file,
1497                 directory=params.directory, frame_start=f_in,
1498                 frame_end=f_out, relative_path=False)
1499             elif strip_type == 'MOVIE':
1500                 bpy.ops.sequencer.movie_strip_add(filepath=path,
1501                 frame_start=frame, relative_path=False)
1502
1503                 strip = functions.act_strip(context)
1504                 strip.use_proxy = True
1505
1506                 # check if proxy exists, otherwise, uncheck proxy custom file
1507                 proxy_filepath = params.directory + self.proxy_path
1508                 proxy_filename = params.filename.rpartition(".")[0] + \
1509                 self.proxy_suffix + self.proxy_extension
1510                 proxypath = os.path.join(proxy_filepath, proxy_filename)
1511                 strip.proxy.build_25 = self.build_25
1512                 strip.proxy.build_50 = self.build_50
1513                 strip.proxy.build_75 = self.build_75
1514                 strip.proxy.build_100 = self.build_100
1515                 #print("----------------", proxypath)
1516                 if os.path.isfile(proxypath):
1517                     strip.use_proxy_custom_file = True
1518                     strip.proxy.filepath = proxypath
1519
1520             elif strip_type == 'SOUND':
1521                 bpy.ops.sequencer.sound_strip_add(filepath=path,
1522                 frame_start=frame, relative_path=False)
1523             else:
1524                 self.report({'ERROR_INVALID_INPUT'}, 'Invalid file format')
1525                 return {'CANCELLED'}
1526
1527         except:
1528             self.report({'ERROR_INVALID_INPUT'}, 'Error loading file (proxy)')
1529             return {'CANCELLED'}
1530
1531         scn.default_proxy_suffix = self.proxy_suffix
1532         scn.default_proxy_extension = self.proxy_extension
1533         scn.default_proxy_path = self.proxy_path
1534         scn.default_build_25 = self.build_25
1535         scn.default_build_50 = self.build_50
1536         scn.default_build_75 = self.build_75
1537         scn.default_build_100 = self.build_100
1538
1539         if self.insert == True:
1540             try:
1541                 bpy.ops.sequencerextra.insert()
1542             except:
1543                 self.report({'ERROR_INVALID_INPUT'}, 'Execution Error, '\
1544                 'check your Blender version')
1545                 return {'CANCELLED'}
1546         else:
1547             strip = functions.act_strip(context)
1548             scn.frame_current += strip.frame_final_duration
1549             bpy.ops.sequencer.reload()
1550
1551         return {'FINISHED'}
1552
1553
1554 # OPEN IMAGE WITH EDITOR AND create movie clip strip
1555 class Sequencer_Extra_CreateMovieclip(bpy.types.Operator):
1556     bl_label = 'Create a Movieclip from selected strip'
1557     bl_idname = 'sequencerextra.createmovieclip'
1558     bl_description = 'Create a Movieclip strip from a MOVIE or IMAGE strip'
1559
1560     """
1561     When a movie or image strip is selected, this operator creates a movieclip
1562     or find the correspondent movieclip that already exists for this footage,
1563     and add a VSE clip strip with same cuts the original strip has.
1564     It can convert movie strips and image sequences, both with hard cuts or
1565     soft cuts.
1566     """
1567
1568     @classmethod
1569     def poll(self, context):
1570         strip = functions.act_strip(context)
1571         scn = context.scene
1572         if scn and scn.sequence_editor and scn.sequence_editor.active_strip:
1573             return strip.type in ('MOVIE', 'IMAGE')
1574         else:
1575             return False
1576
1577     def execute(self, context):
1578         strip = functions.act_strip(context)
1579         scn = context.scene
1580
1581         if strip.type == 'MOVIE':
1582             #print("movie", strip.frame_start)
1583             path = strip.filepath
1584             #print(path)
1585             data_exists = False
1586             for i in bpy.data.movieclips:
1587                 if i.filepath == path:
1588                     data_exists = True
1589                     data = i
1590             newstrip = None
1591             if data_exists == False:
1592                 try:
1593                     data = bpy.data.movieclips.load(filepath=path)
1594                     newstrip = bpy.ops.sequencer.movieclip_strip_add(\
1595                         replace_sel=True, overlap=False, clip=data.name)
1596                     newstrip = functions.act_strip(context)
1597                     newstrip.frame_start = strip.frame_start\
1598                         - strip.animation_offset_start
1599                     tin = strip.frame_offset_start + strip.frame_start
1600                     tout = tin + strip.frame_final_duration
1601                     #print(newstrip.frame_start, strip.frame_start, tin, tout)
1602                     functions.triminout(newstrip, tin, tout)
1603                 except:
1604                     self.report({'ERROR_INVALID_INPUT'}, 'Error loading file')
1605                     return {'CANCELLED'}
1606
1607             else:
1608                 try:
1609                     newstrip = bpy.ops.sequencer.movieclip_strip_add(\
1610                         replace_sel=True, overlap=False, clip=data.name)
1611                     newstrip = functions.act_strip(context)
1612                     newstrip.frame_start = strip.frame_start\
1613                         - strip.animation_offset_start
1614                     # i need to declare the strip this way in order
1615                     # to get triminout() working
1616                     clip = bpy.context.scene.sequence_editor.sequences[\
1617                         newstrip.name]
1618                     # i cannot change these movie clip attributes via scripts
1619                     # but it works in the python console...
1620                     #clip.animation_offset_start = strip.animation.offset_start
1621                     #clip.animation_offset_end = strip.animation.offset_end
1622                     #clip.frame_final_duration = strip.frame_final_duration
1623                     tin = strip.frame_offset_start + strip.frame_start
1624                     tout = tin + strip.frame_final_duration
1625                     #print(newstrip.frame_start, strip.frame_start, tin, tout)
1626                     functions.triminout(clip, tin, tout)
1627                 except:
1628                     self.report({'ERROR_INVALID_INPUT'}, 'Error loading file')
1629                     return {'CANCELLED'}
1630
1631         elif strip.type == 'IMAGE':
1632             #print("image")
1633             base_dir = bpy.path.abspath(strip.directory)
1634             scn.frame_current = strip.frame_start -\
1635                 strip.animation_offset_start
1636             # searching for the first frame of the sequencer. This is mandatory
1637             # for hard cutted sequence strips to be correctly converted,
1638             # avoiding to create a new movie clip if not needed
1639             filename = sorted(os.listdir(base_dir))[0]
1640             path = os.path.join(base_dir, filename)
1641             #print(path)
1642             data_exists = False
1643             for i in bpy.data.movieclips:
1644                 #print(i.filepath, path)
1645                 if i.filepath == path:
1646                     data_exists = True
1647                     data = i
1648             #print(data_exists)
1649             if data_exists == False:
1650                 try:
1651                     data = bpy.data.movieclips.load(filepath=path)
1652                     newstrip = bpy.ops.sequencer.movieclip_strip_add(\
1653                         replace_sel=True, overlap=False,\
1654                         clip=data.name)
1655                     newstrip = functions.act_strip(context)
1656                     newstrip.frame_start = strip.frame_start\
1657                         - strip.animation_offset_start
1658                     clip = bpy.context.scene.sequence_editor.sequences[\
1659                     newstrip.name]
1660                     tin = strip.frame_offset_start + strip.frame_start
1661                     tout = tin + strip.frame_final_duration
1662                     #print(newstrip.frame_start, strip.frame_start, tin, tout)
1663                     functions.triminout(clip, tin, tout)
1664                 except:
1665                     self.report({'ERROR_INVALID_INPUT'}, 'Error loading file')
1666                     return {'CANCELLED'}
1667
1668             else:
1669                 try:
1670                     newstrip = bpy.ops.sequencer.movieclip_strip_add(\
1671                         replace_sel=True, overlap=False, clip=data.name)
1672                     newstrip = functions.act_strip(context)
1673                     newstrip.frame_start = strip.frame_start\
1674                         - strip.animation_offset_start
1675                     # need to declare the strip this way in order
1676                     # to get triminout() working
1677                     clip = bpy.context.scene.sequence_editor.sequences[\
1678                     newstrip.name]
1679                     # cannot change this atributes via scripts...
1680                     # but it works in the python console...
1681                     #clip.animation_offset_start = strip.animation.offset_start
1682                     #clip.animation_offset_end = strip.animation.offset_end
1683                     #clip.frame_final_duration = strip.frame_final_duration
1684                     tin = strip.frame_offset_start + strip.frame_start
1685                     tout = tin + strip.frame_final_duration
1686                     #print(newstrip.frame_start, strip.frame_start, tin, tout)
1687                     functions.triminout(clip, tin, tout)
1688                 except:
1689                     self.report({'ERROR_INVALID_INPUT'}, 'Error loading file')
1690                     return {'CANCELLED'}
1691
1692         # show the new clip in a movie clip editor, if available.
1693         if strip.type == 'MOVIE' or 'IMAGE':
1694             for a in context.window.screen.areas:
1695                 if a.type == 'CLIP_EDITOR':
1696                     a.spaces[0].clip = data
1697
1698         return {'FINISHED'}
1699         
1700         
1701 # RECURSIVE LOADER
1702
1703 class Sequencer_Extra_RecursiveLoader(bpy.types.Operator):
1704     bl_idname = "sequencerextra.recursiveload"
1705     bl_label = "recursive load"
1706     bl_options = {'REGISTER', 'UNDO'}
1707     
1708     default_recursive = BoolProperty(
1709         name='Recursive',
1710         description='Load in recursive folders',
1711         default=False)
1712         
1713     default_recursive_ext = BoolProperty(
1714         name='Recursive extension',
1715         description='Load in recursive folders',
1716         default=False)
1717         
1718     default_ext = EnumProperty(
1719         items=functions.movieextdict,
1720         name="ext enum",
1721         default="3")
1722         
1723     default_recursive_proxies = BoolProperty(
1724         name='Recursive proxies',
1725         description='Load in recursive folders',
1726         default=False)
1727     build_25 = BoolProperty(name='default_build_25',
1728         description='default build_25',
1729         default=True)
1730     build_50 = BoolProperty(name='default_build_50',
1731         description='default build_50',
1732         default=True)
1733     build_75 = BoolProperty(name='default_build_75',
1734         description='default build_75',
1735         default=True)
1736     build_100 = BoolProperty(name='default_build_100',
1737         description='default build_100',
1738         default=True)
1739     proxy_suffix = StringProperty(
1740         name='default proxy suffix',
1741         description='default proxy filename suffix',
1742         default="-25")
1743     proxy_extension = StringProperty(
1744         name='default proxy extension',
1745         description='default proxy extension',
1746         default=".mkv")
1747     proxy_path = StringProperty(
1748         name='default proxy path',
1749         description='default proxy path',
1750         default="")
1751     
1752        
1753     
1754     @classmethod
1755     def poll(self, context):
1756         scn = context.scene
1757         if scn and scn.sequence_editor:
1758             return (scn.sequence_editor)
1759         else:
1760             return False
1761         
1762     def invoke(self, context, event):
1763         scn = context.scene
1764         initSceneProperties(context, scn)
1765         self.build_25 = scn.default_build_25
1766         self.build_50 = scn.default_build_50
1767         self.build_75 = scn.default_build_75
1768         self.build_100 = scn.default_build_100
1769         self.proxy_suffix = scn.default_proxy_suffix
1770         self.proxy_extension = scn.default_proxy_extension
1771         self.proxy_path = scn.default_proxy_path
1772         self.default_recursive = scn.default_recursive
1773         self.default_recursive_ext = scn.default_recursive_ext
1774         self.default_recursive_proxies = scn.default_recursive_proxies
1775         self.default_ext = scn.default_ext 
1776         
1777         return context.window_manager.invoke_props_dialog(self)  
1778         
1779     def loader(self, context, filelist):
1780         scn = context.scene
1781         if filelist:
1782             for i in filelist:
1783                 functions.setpathinbrowser(i[0], i[1])
1784                 try:
1785                     if self.default_recursive_proxies:
1786                         bpy.ops.sequencerextra.placefromfilebrowserproxy(
1787                             proxy_suffix=self.default_proxy_suffix,
1788                             proxy_extension=self.default_proxy_extension,
1789                             proxy_path=self.default_proxy_path,
1790                             build_25=self.default_build_25,
1791                             build_50=self.default_build_50,
1792                             build_75=self.default_build_75,
1793                             build_100=self.default_build_100)
1794                     else:
1795                         bpy.ops.sequencerextra.placefromfilebrowser()
1796                 except:
1797                     print("Error loading file (recursive loader error): ", i[1])
1798                     functions.add_marker(i[1], context)
1799                     #self.report({'ERROR_INVALID_INPUT'}, 'Error loading file ')
1800                     pass
1801
1802
1803     def execute(self, context):
1804         scn = context.scene
1805         initSceneProperties(context, scn)
1806         if scn["default_recursive"] == True:
1807             self.loader(context, functions.sortlist(functions.recursive(context, self.default_recursive_ext)))
1808         else:
1809             self.loader(context, functions.sortlist(functions.onefolder(context, self.default_recursive_ext)))
1810         return {'FINISHED'}
1811
1812
1813 # READ EXIF DATA
1814 class Sequencer_Extra_ReadExifData(bpy.types.Operator):
1815     # load exifdata from strip to scene['metadata'] property
1816     bl_label = 'Read EXIF Data'
1817     bl_idname = 'sequencerextra.read_exif'
1818     bl_description = 'Load exifdata from strip to metadata property in scene'
1819     bl_options = {'REGISTER', 'UNDO'}
1820
1821     @classmethod
1822     def poll(self, context):
1823         strip = functions.act_strip(context)
1824         scn = context.scene
1825         if scn and scn.sequence_editor and scn.sequence_editor.active_strip:
1826             return (strip.type == 'IMAGE')
1827         else:
1828             return False
1829
1830     def execute(self, context):
1831         try:
1832             exiftool.ExifTool().start()
1833         except:
1834             self.report({'ERROR_INVALID_INPUT'},
1835             'exiftool not found in PATH')
1836             return {'CANCELLED'}
1837
1838         def getexifdata(strip):
1839             def getlist(lista):
1840                 for root, dirs, files in os.walk(path):
1841                     for f in files:
1842                         if "." + f.rpartition(".")[2].lower() \
1843                             in functions.imb_ext_image:
1844                             lista.append(f)
1845                         #if "."+f.rpartition(".")[2] in imb_ext_movie:
1846                         #    lista.append(f)
1847                 strip.elements
1848                 lista.sort()
1849                 return lista
1850
1851             def getexifvalues(lista):
1852                 metadata = []
1853                 with exiftool.ExifTool() as et:
1854                     try:
1855                         metadata = et.get_metadata_batch(lista)
1856                     except UnicodeDecodeError as Err:
1857                         print(Err)
1858                 return metadata
1859             if strip.type == "IMAGE":
1860                 path = bpy.path.abspath(strip.directory)
1861             if strip.type == "MOVIE":
1862                 path = bpy.path.abspath(strip.filepath.rpartition("/")[0])
1863             os.chdir(path)
1864             #get a list of files
1865             lista = []
1866             for i in strip.elements:
1867                 lista.append(i.filename)
1868             return getexifvalues(lista)
1869
1870         sce = bpy.context.scene
1871         frame = sce.frame_current
1872         text = bpy.context.active_object
1873         strip = context.scene.sequence_editor.active_strip
1874         sce['metadata'] = getexifdata(strip)
1875         return {'FINISHED'}