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