diff --git a/sequencer_extra_actions/operators_extra_actions.py b/sequencer_extra_actions/operators_extra_actions.py new file mode 100644 index 0000000000000000000000000000000000000000..244cf8c898d529c65b3d8e2c88bcfefe56ac77ff --- /dev/null +++ b/sequencer_extra_actions/operators_extra_actions.py @@ -0,0 +1,1948 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +''' +TODO +align strip to the left (shift-s + -lenght) + +''' + +import bpy + +import random +import math +import os, sys + +from bpy.props import IntProperty +from bpy.props import FloatProperty +from bpy.props import EnumProperty +from bpy.props import BoolProperty +from bpy.props import StringProperty + +from . import functions +from . import exiftool + + +# Initialization + + +def initSceneProperties(context, scn): + try: + if context.scene.scene_initialized == True: + return False + except AttributeError: + pass + + bpy.types.Scene.default_slide_offset = IntProperty( + name='Offset', + description='Number of frames to slide', + min=-250, max=250, + default=0) + scn.default_slide_offset = 0 + + bpy.types.Scene.default_fade_duration = IntProperty( + name='Duration', + description='Number of frames to fade', + min=1, max=250, + default=scn.render.fps) + scn.default_fade_duration = scn.render.fps + + bpy.types.Scene.default_fade_amount = FloatProperty( + name='Amount', + description='Maximum value of fade', + min=0.0, + max=100.0, + default=1.0) + scn.default_fade_amount = 1.0 + + bpy.types.Scene.default_distribute_offset = IntProperty( + name='Offset', + description='Number of frames between strip start frames', + min=1, + max=250, + default=2) + scn.default_distribute_offset = 2 + + bpy.types.Scene.default_distribute_reverse = BoolProperty( + name='Reverse Order', + description='Reverse the order of selected strips', + default=False) + scn.default_distribute_reverse = False + + bpy.types.Scene.default_proxy_suffix = StringProperty( + name='default proxy suffix', + description='default proxy filename suffix', + default="-25") + scn.default_proxy_suffix = "-25" + + bpy.types.Scene.default_proxy_extension = StringProperty( + name='default proxy extension', + description='default proxy file extension', + default=".mkv") + scn.default_proxy_extension = ".mkv" + + bpy.types.Scene.default_proxy_path = StringProperty( + name='default proxy path', + description='default proxy file path (relative to original file)', + default="") + scn.default_proxy_path = "" + + bpy.types.Scene.default_build_25 = BoolProperty( + name='default_build_25', + description='default build_25', + default=True) + scn.default_build_25 = True + + bpy.types.Scene.default_build_50 = BoolProperty( + name='default_build_50', + description='default build_50', + default=False) + scn.default_build_50 = False + + bpy.types.Scene.default_build_75 = BoolProperty( + name='default_build_75', + description='default build_75', + default=False) + scn.default_build_75 = False + + bpy.types.Scene.default_build_100 = BoolProperty( + name='default_build_100', + description='default build_100', + default=False) + scn.default_build_100 = False + + bpy.types.Scene.default_recursive = BoolProperty( + name='Recursive', + description='Load in recursive folders', + default=False) + scn.default_recursive = False + + bpy.types.Scene.default_recursive_proxies = BoolProperty( + name='Recursive proxies', + description='Load in recursive folders + proxies', + default=False) + scn.default_recursive_proxies = False + + bpy.types.Scene.default_recursive_select_by_extension = BoolProperty( + name='Recursive ext', + description='Load only clips with selected extension', + default=False) + scn.default_recursive_select_by_extension = False + + bpy.types.Scene.default_ext = EnumProperty( + items=functions.movieextdict, + name="ext enum", + default="3") + scn.default_ext = "3" + + bpy.types.Scene.scene_initialized = BoolProperty( + name='Init', + default=False) + scn.scene_initialized = True + + return True + + +# TRIM TIMELINE +class Sequencer_Extra_TrimTimeline(bpy.types.Operator): + bl_label = 'Trim to Timeline Content' + bl_idname = 'timeextra.trimtimeline' + bl_description = 'Automatically set start and end frames' + bl_options = {'REGISTER', 'UNDO'} + + @classmethod + def poll(self, context): + scn = context.scene + if scn and scn.sequence_editor: + return scn.sequence_editor.sequences + else: + return False + + def execute(self, context): + scn = context.scene + seq = scn.sequence_editor + meta_level = len(seq.meta_stack) + if meta_level > 0: + seq = seq.meta_stack[meta_level - 1] + + frame_start = 300000 + frame_end = -300000 + for i in seq.sequences: + try: + if i.frame_final_start < frame_start: + frame_start = i.frame_final_start + if i.frame_final_end > frame_end: + frame_end = i.frame_final_end - 1 + except AttributeError: + pass + + if frame_start != 300000: + scn.frame_start = frame_start + if frame_end != -300000: + scn.frame_end = frame_end + return {'FINISHED'} + + +# TRIM TIMELINE TO SELECTION +class Sequencer_Extra_TrimTimelineToSelection(bpy.types.Operator): + bl_label = 'Trim to Selection' + bl_idname = 'timeextra.trimtimelinetoselection' + bl_description = 'Set start and end frames to selection' + bl_options = {'REGISTER', 'UNDO'} + + @classmethod + def poll(self, context): + scn = context.scene + if scn and scn.sequence_editor: + return scn.sequence_editor.sequences + else: + return False + + def execute(self, context): + scn = context.scene + seq = scn.sequence_editor + meta_level = len(seq.meta_stack) + if meta_level > 0: + seq = seq.meta_stack[meta_level - 1] + + frame_start = 300000 + frame_end = -300000 + for i in seq.sequences: + try: + if i.frame_final_start < frame_start and i.select == True: + frame_start = i.frame_final_start + if i.frame_final_end > frame_end and i.select == True: + frame_end = i.frame_final_end - 1 + except AttributeError: + pass + + if frame_start != 300000: + scn.frame_start = frame_start + if frame_end != -300000: + scn.frame_end = frame_end + return {'FINISHED'} + + +# SLIDE STRIP +class Sequencer_Extra_SlideStrip(bpy.types.Operator): + bl_label = 'Slide' + bl_idname = 'sequencerextra.slide' + bl_description = 'Alter in and out points but not duration of a strip' + mode = EnumProperty( + name='Mode', + items=( + ('TOSTART', 'Current Frame to Strip Start', ''), + ('TOEND', 'Current Frame to Strip End', ''), + ('INPUT', 'Input...', '')), + default='INPUT', + options={'HIDDEN'}) + bl_options = {'REGISTER', 'UNDO'} + + slide_offset = IntProperty( + name='Offset', + description='Number of frames to slide', + min=-250, max=250, + default=0) + + @classmethod + def poll(self, context): + strip = functions.act_strip(context) + scn = context.scene + if scn and scn.sequence_editor and scn.sequence_editor.active_strip: + return strip.type in ('MOVIE', 'IMAGE', 'META', 'SCENE') + else: + return False + + def execute(self, context): + strip = functions.act_strip(context) + scn = context.scene + cf = scn.frame_current + + if self.mode == 'TOSTART': + sx = strip.frame_final_start - cf + elif self.mode == 'TOEND': + sx = strip.frame_final_end - cf + else: + sx = self.slide_offset + + frame_end = strip.frame_start + strip.frame_duration + pad_left = strip.frame_final_start - strip.frame_start + pad_right = (frame_end - strip.frame_final_end) * -1 + + if sx > pad_left: + sx = pad_left + elif sx < pad_right: + sx = pad_right + + strip.frame_start += sx + strip.frame_final_start -= sx + strip.frame_final_end -= sx + + self.report({'INFO'}, 'Strip slid %d frame(s)' % (sx)) + scn.default_slide_offset = sx + bpy.ops.sequencer.reload() + return {'FINISHED'} + + def invoke(self, context, event): + scn = context.scene + initSceneProperties(context,scn) + self.slide_offset = scn.default_slide_offset + if self.mode == 'INPUT': + return context.window_manager.invoke_props_dialog(self) + else: + return self.execute(context) + + +# SLIDE GRAB +class Sequencer_Extra_SlideGrab(bpy.types.Operator): + bl_label = 'Slide Grab' + bl_idname = 'sequencerextra.slidegrab' + bl_description = 'Alter in and out points but not duration of a strip' + bl_options = {'REGISTER', 'UNDO'} + + @classmethod + def poll(self, context): + strip = functions.act_strip(context) + scn = context.scene + if scn and scn.sequence_editor and scn.sequence_editor.active_strip: + return strip.type in ('MOVIE', 'IMAGE', 'META', 'SCENE') + else: + return False + + def execute(self, context): + strip = functions.act_strip(context) + scn = context.scene + + diff = self.prev_x - self.x + self.prev_x = self.x + sx = int(diff / 2) + + frame_end = strip.frame_start + strip.frame_duration + pad_left = strip.frame_final_start - strip.frame_start + pad_right = (frame_end - strip.frame_final_end) * -1 + + if sx > pad_left: + sx = pad_left + elif sx < pad_right: + sx = pad_right + + strip.frame_start += sx + strip.frame_final_start -= sx + strip.frame_final_end -= sx + + def modal(self, context, event): + if event.type == 'MOUSEMOVE': + self.x = event.mouse_x + self.execute(context) + elif event.type == 'LEFTMOUSE': + return {'FINISHED'} + elif event.type in ('RIGHTMOUSE', 'ESC'): + return {'CANCELLED'} + + return {'RUNNING_MODAL'} + + def invoke(self, context, event): + scn = context.scene + self.x = event.mouse_x + self.prev_x = event.mouse_x + self.execute(context) + context.window_manager.modal_handler_add(self) + return {'RUNNING_MODAL'} + + +# FILE NAME TO STRIP NAME +class Sequencer_Extra_FileNameToStripName(bpy.types.Operator): + bl_label = 'File Name to Selected Strips Name' + bl_idname = 'sequencerextra.striprename' + bl_description = 'Set strip name to input file name' + bl_options = {'REGISTER', 'UNDO'} + + @classmethod + def poll(self, context): + scn = context.scene + if scn and scn.sequence_editor: + return scn.sequence_editor.sequences + else: + return False + + def execute(self, context): + scn = context.scene + seq = scn.sequence_editor + meta_level = len(seq.meta_stack) + if meta_level > 0: + seq = seq.meta_stack[meta_level - 1] + selection = False + for i in seq.sequences: + if i.select == True: + if i.type == 'IMAGE' and not i.mute: + selection = True + i.name = i.elements[0].filename + if (i.type == 'SOUND' or i.type == 'MOVIE') and not i.mute: + selection = True + i.name = bpy.path.display_name_from_filepath(i.filepath) + if selection == False: + self.report({'ERROR_INVALID_INPUT'}, + 'No image or movie strip selected') + return {'CANCELLED'} + return {'FINISHED'} + + +# NAVIGATE UP +class Sequencer_Extra_NavigateUp(bpy.types.Operator): + bl_label = 'Navigate Up' + bl_idname = 'sequencerextra.navigateup' + bl_description = 'Move to Parent Timeline' + + @classmethod + def poll(self, context): + strip = functions.act_strip(context) + try: + if context.scene.sequence_editor.meta_stack: + return True + else: + return False + except: + return False + + def execute(self, context): + if (functions.act_strip(context)): + strip = functions.act_strip(context) + seq_type = strip.type + if seq_type == 'META': + context.scene.sequence_editor.active_strip = None + + bpy.ops.sequencer.meta_toggle() + return {'FINISHED'} + + +# RIPPLE DELETE +class Sequencer_Extra_RippleDelete(bpy.types.Operator): + bl_label = 'Ripple Delete' + bl_idname = 'sequencerextra.rippledelete' + bl_description = 'Delete a strip and shift back following ones' + bl_options = {'REGISTER', 'UNDO'} + + @classmethod + def poll(self, context): + strip = functions.act_strip(context) + scn = context.scene + if scn and scn.sequence_editor and scn.sequence_editor.active_strip: + return True + else: + return False + + def execute(self, context): + scn = context.scene + seq = scn.sequence_editor + meta_level = len(seq.meta_stack) + if meta_level > 0: + seq = seq.meta_stack[meta_level - 1] + #strip = functions.act_strip(context) + for strip in context.selected_editable_sequences: + cut_frame = strip.frame_final_start + next_edit = 300000 + bpy.ops.sequencer.select_all(action='DESELECT') + strip.select = True + bpy.ops.sequencer.delete() + striplist = [] + for i in seq.sequences: + try: + if (i.frame_final_start > cut_frame + and not i.mute): + if i.frame_final_start < next_edit: + next_edit = i.frame_final_start + if not i.mute: + striplist.append(i) + except AttributeError: + pass + + if next_edit == 300000: + return {'FINISHED'} + ripple_length = next_edit - cut_frame + for i in range(len(striplist)): + str = striplist[i] + try: + if str.frame_final_start > cut_frame: + str.frame_start = str.frame_start - ripple_length + except AttributeError: + pass + bpy.ops.sequencer.reload() + return {'FINISHED'} + + +# RIPPLE CUT +class Sequencer_Extra_RippleCut(bpy.types.Operator): + bl_label = 'Ripple Cut' + bl_idname = 'sequencerextra.ripplecut' + bl_description = 'Move a strip to buffer and shift back following ones' + bl_options = {'REGISTER', 'UNDO'} + + @classmethod + def poll(self, context): + strip = functions.act_strip(context) + scn = context.scene + if scn and scn.sequence_editor and scn.sequence_editor.active_strip: + return True + else: + return False + + def execute(self, context): + scn = context.scene + seq = scn.sequence_editor + meta_level = len(seq.meta_stack) + if meta_level > 0: + seq = seq.meta_stack[meta_level - 1] + strip = functions.act_strip(context) + bpy.ops.sequencer.select_all(action='DESELECT') + strip.select = True + temp_cf = scn.frame_current + scn.frame_current = strip.frame_final_start + bpy.ops.sequencer.copy() + scn.frame_current = temp_cf + + bpy.ops.sequencerextra.rippledelete() + return {'FINISHED'} + + +# INSERT +class Sequencer_Extra_Insert(bpy.types.Operator): + bl_label = 'Insert' + bl_idname = 'sequencerextra.insert' + bl_description = 'Move active strip to current frame and shift '\ + 'forward following ones' + singlechannel = BoolProperty( + name='Single Channel', + default=False) + bl_options = {'REGISTER', 'UNDO'} + + @classmethod + def poll(self, context): + strip = functions.act_strip(context) + scn = context.scene + if scn and scn.sequence_editor and scn.sequence_editor.active_strip: + return True + else: + return False + + def execute(self, context): + scn = context.scene + seq = scn.sequence_editor + meta_level = len(seq.meta_stack) + if meta_level > 0: + seq = seq.meta_stack[meta_level - 1] + strip = functions.act_strip(context) + gap = strip.frame_final_duration + bpy.ops.sequencer.select_all(action='DESELECT') + current_frame = scn.frame_current + + striplist = [] + for i in seq.sequences: + try: + if (i.frame_final_start >= current_frame + and not i.mute): + if self.singlechannel == True: + if i.channel == strip.channel: + striplist.append(i) + else: + striplist.append(i) + except AttributeError: + pass + try: + bpy.ops.sequencerextra.selectcurrentframe('EXEC_DEFAULT', + mode='AFTER') + except: + self.report({'ERROR_INVALID_INPUT'}, 'Execution Error, '\ + 'check your Blender version') + return {'CANCELLED'} + + for i in range(len(striplist)): + str = striplist[i] + try: + if str.select == True: + str.frame_start += gap + except AttributeError: + pass + try: + diff = current_frame - strip.frame_final_start + strip.frame_start += diff + except AttributeError: + pass + + strip = functions.act_strip(context) + scn.frame_current += strip.frame_final_duration + bpy.ops.sequencer.reload() + + return {'FINISHED'} + + +# PLACE FROM FILE BROWSER +class Sequencer_Extra_PlaceFromFileBrowser(bpy.types.Operator): + bl_label = 'Place' + bl_idname = 'sequencerextra.placefromfilebrowser' + bl_description = 'Place or insert active file from File Browser' + insert = BoolProperty( + name='Insert', + default=False) + bl_options = {'REGISTER', 'UNDO'} + + def execute(self, context): + scn = context.scene + for a in context.window.screen.areas: + if a.type == 'FILE_BROWSER': + params = a.spaces[0].params + break + try: + params + except UnboundLocalError: + self.report({'ERROR_INVALID_INPUT'}, 'No visible File Browser') + return {'CANCELLED'} + + if params.filename == '': + self.report({'ERROR_INVALID_INPUT'}, 'No file selected') + return {'CANCELLED'} + + path = os.path.join(params.directory, params.filename) + frame = context.scene.frame_current + strip_type = functions.detect_strip_type(params.filename) + + try: + if strip_type == 'IMAGE': + image_file = [] + filename = {"name": params.filename} + image_file.append(filename) + f_in = scn.frame_current + f_out = f_in + scn.render.fps - 1 + bpy.ops.sequencer.image_strip_add(files=image_file, + directory=params.directory, frame_start=f_in, + frame_end=f_out, relative_path=False) + elif strip_type == 'MOVIE': + bpy.ops.sequencer.movie_strip_add(filepath=path, + frame_start=frame, relative_path=False) + elif strip_type == 'SOUND': + bpy.ops.sequencer.sound_strip_add(filepath=path, + frame_start=frame, relative_path=False) + else: + self.report({'ERROR_INVALID_INPUT'}, 'Invalid file format') + return {'CANCELLED'} + except: + self.report({'ERROR_INVALID_INPUT'}, 'Error loading file') + return {'CANCELLED'} + + if self.insert == True: + try: + striplist = [] + for i in bpy.context.selected_editable_sequences: + if (i.select == True and i.type == "SOUND"): + striplist.append(i) + bpy.ops.sequencerextra.insert() + if striplist[0]: + striplist[0].frame_start = frame + except: + self.report({'ERROR_INVALID_INPUT'}, 'Execution Error, '\ + 'check your Blender version') + return {'CANCELLED'} + else: + strip = functions.act_strip(context) + scn.frame_current += strip.frame_final_duration + bpy.ops.sequencer.reload() + + return {'FINISHED'} + + +# SELECT BY TYPE +class Sequencer_Extra_SelectAllByType(bpy.types.Operator): + bl_label = 'All by Type' + bl_idname = 'sequencerextra.select_all_by_type' + bl_description = 'Select all the strips of the same type' + type = EnumProperty( + name='Strip Type', + items=( + ('ACTIVE', 'Same as Active Strip', ''), + ('IMAGE', 'Image', ''), + ('META', 'Meta', ''), + ('SCENE', 'Scene', ''), + ('MOVIE', 'Movie', ''), + ('SOUND', 'Sound', ''), + ('TRANSFORM', 'Transform', ''), + ('COLOR', 'Color', '')), + default='ACTIVE', + ) + bl_options = {'REGISTER', 'UNDO'} + + @classmethod + def poll(self, context): + scn = context.scene + if scn and scn.sequence_editor: + return scn.sequence_editor.sequences + else: + return False + + def execute(self, context): + strip_type = self.type + scn = context.scene + seq = scn.sequence_editor + meta_level = len(seq.meta_stack) + if meta_level > 0: + seq = seq.meta_stack[meta_level - 1] + active_strip = functions.act_strip(context) + if strip_type == 'ACTIVE': + if active_strip == None: + self.report({'ERROR_INVALID_INPUT'}, + 'No active strip') + return {'CANCELLED'} + strip_type = active_strip.type + + striplist = [] + for i in seq.sequences: + try: + if (i.type == strip_type + and not i.mute): + striplist.append(i) + except AttributeError: + pass + for i in range(len(striplist)): + str = striplist[i] + try: + str.select = True + except AttributeError: + pass + + return {'FINISHED'} + + +# CURRENT-FRAME-AWARE SELECT +class Sequencer_Extra_SelectCurrentFrame(bpy.types.Operator): + bl_label = 'Current-Frame-Aware Select' + bl_idname = 'sequencerextra.selectcurrentframe' + bl_description = 'Select strips according to current frame' + mode = EnumProperty( + name='Mode', + items=( + ('BEFORE', 'Before Current Frame', ''), + ('AFTER', 'After Current Frame', ''), + ('ON', 'On Current Frame', '')), + default='BEFORE', + ) + bl_options = {'REGISTER', 'UNDO'} + + @classmethod + def poll(self, context): + scn = context.scene + if scn and scn.sequence_editor: + return scn.sequence_editor.sequences + else: + return False + + def execute(self, context): + mode = self.mode + scn = context.scene + seq = scn.sequence_editor + cf = scn.frame_current + meta_level = len(seq.meta_stack) + if meta_level > 0: + seq = seq.meta_stack[meta_level - 1] + + if mode == 'AFTER': + for i in seq.sequences: + try: + if (i.frame_final_start >= cf + and not i.mute): + i.select = True + except AttributeError: + pass + elif mode == 'ON': + for i in seq.sequences: + try: + if (i.frame_final_start <= cf + and i.frame_final_end > cf + and not i.mute): + i.select = True + except AttributeError: + pass + else: + for i in seq.sequences: + try: + if (i.frame_final_end < cf + and not i.mute): + i.select = True + except AttributeError: + pass + + return {'FINISHED'} + + +# SELECT STRIPS ON SAME CHANNEL +class Sequencer_Extra_SelectSameChannel(bpy.types.Operator): + bl_label = 'Select Strips on the Same Channel' + bl_idname = 'sequencerextra.selectsamechannel' + bl_description = 'Select strips on the same channel as active one' + bl_options = {'REGISTER', 'UNDO'} + + @classmethod + def poll(self, context): + strip = functions.act_strip(context) + scn = context.scene + if scn and scn.sequence_editor and scn.sequence_editor.active_strip: + return True + else: + return False + + def execute(self, context): + scn = context.scene + seq = scn.sequence_editor + meta_level = len(seq.meta_stack) + if meta_level > 0: + seq = seq.meta_stack[meta_level - 1] + bpy.ops.sequencer.select_active_side(side="LEFT") + bpy.ops.sequencer.select_active_side(side="RIGHT") + + return {'FINISHED'} + + +# OPEN IMAGE WITH EXTERNAL EDITOR +class Sequencer_Extra_EditExternally(bpy.types.Operator): + bl_label = 'Open with External Editor' + bl_idname = 'sequencerextra.editexternally' + bl_description = 'Open with the default external image editor' + + @classmethod + def poll(self, context): + strip = functions.act_strip(context) + scn = context.scene + if scn and scn.sequence_editor and scn.sequence_editor.active_strip: + return strip.type == 'IMAGE' + else: + return False + + def execute(self, context): + strip = functions.act_strip(context) + scn = context.scene + base_dir = bpy.path.abspath(strip.directory) + strip_elem = strip.strip_elem_from_frame(scn.frame_current) + path = base_dir + strip_elem.filename + + try: + bpy.ops.image.external_edit(filepath=path) + except: + self.report({'ERROR_INVALID_INPUT'}, + 'Please specify an Image Editor in Preferences > File') + return {'CANCELLED'} + + return {'FINISHED'} + + +# OPEN IMAGE WITH EDITOR +class Sequencer_Extra_Edit(bpy.types.Operator): + bl_label = 'Open with Editor' + bl_idname = 'sequencerextra.edit' + bl_description = 'Open with Movie Clip or Image Editor' + + @classmethod + def poll(self, context): + strip = functions.act_strip(context) + scn = context.scene + if scn and scn.sequence_editor and scn.sequence_editor.active_strip: + return strip.type in ('MOVIE', 'IMAGE') + else: + return False + + def execute(self, context): + strip = functions.act_strip(context) + scn = context.scene + data_exists = False + + if strip.type == 'MOVIE': + path = strip.filepath + + for i in bpy.data.movieclips: + if i.filepath == path: + data_exists = True + data = i + + if data_exists == False: + try: + data = bpy.data.movieclips.load(filepath=path) + except: + self.report({'ERROR_INVALID_INPUT'}, 'Error loading file') + return {'CANCELLED'} + + elif strip.type == 'IMAGE': + base_dir = bpy.path.abspath(strip.directory) + strip_elem = strip.strip_elem_from_frame(scn.frame_current) + elem_name = strip_elem.filename + path = base_dir + elem_name + + for i in bpy.data.images: + if i.filepath == path: + data_exists = True + data = i + + if data_exists == False: + try: + data = bpy.data.images.load(filepath=path) + except: + self.report({'ERROR_INVALID_INPUT'}, 'Error loading file') + return {'CANCELLED'} + + if strip.type == 'MOVIE': + for a in context.window.screen.areas: + if a.type == 'CLIP_EDITOR': + a.spaces[0].clip = data + elif strip.type == 'IMAGE': + for a in context.window.screen.areas: + if a.type == 'IMAGE_EDITOR': + a.spaces[0].image = data + + return {'FINISHED'} + + +# COPY STRIP PROPERTIES +class Sequencer_Extra_CopyProperties(bpy.types.Operator): + bl_label = 'Copy Properties' + bl_idname = 'sequencerextra.copyproperties' + bl_description = 'Copy properties of active strip to selected strips' + bl_options = {'REGISTER', 'UNDO'} + + prop = EnumProperty( + name='Property', + items=[ + # COMMON + ('name', 'Name', ''), + ('blend_alpha', 'Opacity', ''), + ('blend_type', 'Blend Mode', ''), + ('animation_offset', 'Input - Trim Duration', ''), + # NON-SOUND + ('use_translation', 'Input - Image Offset', ''), + ('crop', 'Input - Image Crop', ''), + ('proxy', 'Proxy / Timecode', ''), + ('strobe', 'Filter - Strobe', ''), + ('color_multiply', 'Filter - Multiply', ''), + ('color_saturation', 'Filter - Saturation', ''), + ('deinterlace', 'Filter - De-Interlace', ''), + ('flip', 'Filter - Flip', ''), + ('float', 'Filter - Convert Float', ''), + ('alpha_mode', 'Filter - Alpha Mode', ''), + ('reverse', 'Filter - Backwards', ''), + # SOUND + ('pan', 'Sound - Pan', ''), + ('pitch', 'Sound - Pitch', ''), + ('volume', 'Sound - Volume', ''), + ('cache', 'Sound - Caching', ''), + # IMAGE + ('directory', 'Image - Directory', ''), + # MOVIE + ('mpeg_preseek', 'Movie - MPEG Preseek', ''), + ('stream_index', 'Movie - Stream Index', ''), + # WIPE + ('wipe', 'Effect - Wipe', ''), + # TRANSFORM + ('transform', 'Effect - Transform', ''), + # COLOR + ('color', 'Effect - Color', ''), + # SPEED + ('speed', 'Effect - Speed', ''), + # MULTICAM + ('multicam_source', 'Effect - Multicam Source', ''), + # EFFECT + ('effect_fader', 'Effect - Effect Fader', ''), + ], + default='blend_alpha') + + @classmethod + def poll(self, context): + strip = functions.act_strip(context) + scn = context.scene + if scn and scn.sequence_editor and scn.sequence_editor.active_strip: + return True + else: + return False + + def execute(self, context): + strip = functions.act_strip(context) + selectedstrips = context.selected_editable_sequences + + for i in selectedstrips: + if (i.select == True and not i.mute) and i != strip: + try: + if self.prop == 'name': + i.name = strip.name + elif self.prop == 'blend_alpha': + i.blend_alpha = strip.blend_alpha + elif self.prop == 'blend_type': + i.blend_type = strip.blend_type + elif self.prop == 'animation_offset': + i.animation_offset_start = strip.animation_offset_start + i.animation_offset_end = strip.animation_offset_end + elif self.prop == 'use_translation': + i.use_translation = strip.use_translation + i.transform.offset_x = strip.transform.offset_x + i.transform.offset_y = strip.transform.offset_y + elif self.prop == 'crop': + i.use_crop = strip.use_crop + i.crop.min_x = strip.crop.min_x + i.crop.min_y = strip.crop.min_y + i.crop.max_x = strip.crop.max_x + i.crop.max_y = strip.crop.max_y + elif self.prop == 'proxy': + i.use_proxy = strip.use_proxy + i.use_proxy_custom_file = strip.use_proxy_custom_file + p = strip.use_proxy_custom_directory # pep80 + i.use_proxy_custom_directory = p + i.proxy.filepath = strip.proxy.filepath + i.proxy.directory = strip.proxy.directory + i.proxy.build_25 = strip.proxy.build_25 + i.proxy.build_50 = strip.proxy.build_50 + i.proxy.build_75 = strip.proxy.build_75 + i.proxy.build_100 = strip.proxy.build_100 + i.proxy.quality = strip.proxy.quality + i.proxy.timecode = strip.proxy.timecode + elif self.prop == 'strobe': + i.strobe = strip.strobe + elif self.prop == 'color_multiply': + i.color_multiply = strip.color_multiply + elif self.prop == 'color_saturation': + i.color_saturation = strip.color_saturation + elif self.prop == 'deinterlace': + i.use_deinterlace = strip.use_deinterlace + elif self.prop == 'flip': + i.use_flip_x = strip.use_flip_x + i.use_flip_y = strip.use_flip_y + elif self.prop == 'float': + i.use_float = strip.use_float + elif self.prop == 'alpha_mode': + i.alpha_mode = strip.alpha_mode + elif self.prop == 'reverse': + i.use_reverse_frames = strip.use_reverse_frames + elif self.prop == 'pan': + i.pan = strip.pan + elif self.prop == 'pitch': + i.pitch = strip.pitch + elif self.prop == 'volume': + i.volume = strip.volume + elif self.prop == 'cache': + i.use_memory_cache = strip.use_memory_cache + elif self.prop == 'directory': + i.directory = strip.directory + elif self.prop == 'mpeg_preseek': + i.mpeg_preseek = strip.mpeg_preseek + elif self.prop == 'stream_index': + i.stream_index = strip.stream_index + elif self.prop == 'wipe': + i.angle = strip.angle + i.blur_width = strip.blur_width + i.direction = strip.direction + i.transition_type = strip.transition_type + elif self.prop == 'transform': + i.interpolation = strip.interpolation + i.rotation_start = strip.rotation_start + i.use_uniform_scale = strip.use_uniform_scale + i.scale_start_x = strip.scale_start_x + i.scale_start_y = strip.scale_start_y + i.translation_unit = strip.translation_unit + i.translate_start_x = strip.translate_start_x + i.translate_start_y = strip.translate_start_y + elif self.prop == 'color': + i.color = strip.color + elif self.prop == 'speed': + i.use_default_fade = strip.use_default_fade + i.speed_factor = strip.speed_factor + i.use_as_speed = strip.use_as_speed + i.scale_to_length = strip.scale_to_length + i.multiply_speed = strip.multiply_speed + i.use_frame_blend = strip.use_frame_blend + elif self.prop == 'multicam_source': + i.multicam_source = strip.multicam_source + elif self.prop == 'effect_fader': + i.use_default_fade = strip.use_default_fade + i.effect_fader = strip.effect_fader + except: + pass + + bpy.ops.sequencer.reload() + return {'FINISHED'} + + +# FADE IN AND OUT +class Sequencer_Extra_FadeInOut(bpy.types.Operator): + bl_idname = 'sequencerextra.fadeinout' + bl_label = 'Fade...' + bl_description = 'Fade volume or opacity of active strip' + mode = EnumProperty( + name='Direction', + items=( + ('IN', 'Fade In...', ''), + ('OUT', 'Fade Out...', ''), + ('INOUT', 'Fade In and Out...', '')), + default='IN', + ) + bl_options = {'REGISTER', 'UNDO'} + + fade_duration = IntProperty( + name='Duration', + description='Number of frames to fade', + min=1, max=250, + default=25) + fade_amount = FloatProperty( + name='Amount', + description='Maximum value of fade', + min=0.0, + max=100.0, + default=1.0) + + @classmethod + def poll(cls, context): + scn = context.scene + if scn and scn.sequence_editor and scn.sequence_editor.active_strip: + return True + else: + return False + + def execute(self, context): + seq = context.scene.sequence_editor + scn = context.scene + strip = seq.active_strip + tmp_current_frame = context.scene.frame_current + + if strip.type == 'SOUND': + if(self.mode) == 'OUT': + scn.frame_current = strip.frame_final_end - self.fade_duration + strip.volume = self.fade_amount + strip.keyframe_insert('volume') + scn.frame_current = strip.frame_final_end + strip.volume = 0 + strip.keyframe_insert('volume') + elif(self.mode) == 'INOUT': + scn.frame_current = strip.frame_final_start + strip.volume = 0 + strip.keyframe_insert('volume') + scn.frame_current += self.fade_duration + strip.volume = self.fade_amount + strip.keyframe_insert('volume') + scn.frame_current = strip.frame_final_end - self.fade_duration + strip.volume = self.fade_amount + strip.keyframe_insert('volume') + scn.frame_current = strip.frame_final_end + strip.volume = 0 + strip.keyframe_insert('volume') + else: + scn.frame_current = strip.frame_final_start + strip.volume = 0 + strip.keyframe_insert('volume') + scn.frame_current += self.fade_duration + strip.volume = self.fade_amount + strip.keyframe_insert('volume') + + else: + if(self.mode) == 'OUT': + scn.frame_current = strip.frame_final_end - self.fade_duration + strip.blend_alpha = self.fade_amount + strip.keyframe_insert('blend_alpha') + scn.frame_current = strip.frame_final_end + strip.blend_alpha = 0 + strip.keyframe_insert('blend_alpha') + elif(self.mode) == 'INOUT': + scn.frame_current = strip.frame_final_start + strip.blend_alpha = 0 + strip.keyframe_insert('blend_alpha') + scn.frame_current += self.fade_duration + strip.blend_alpha = self.fade_amount + strip.keyframe_insert('blend_alpha') + scn.frame_current = strip.frame_final_end - self.fade_duration + strip.blend_alpha = self.fade_amount + strip.keyframe_insert('blend_alpha') + scn.frame_current = strip.frame_final_end + strip.blend_alpha = 0 + strip.keyframe_insert('blend_alpha') + else: + scn.frame_current = strip.frame_final_start + strip.blend_alpha = 0 + strip.keyframe_insert('blend_alpha') + scn.frame_current += self.fade_duration + strip.blend_alpha = self.fade_amount + strip.keyframe_insert('blend_alpha') + + scn.frame_current = tmp_current_frame + + scn.default_fade_duration = self.fade_duration + scn.default_fade_amount = self.fade_amount + return{'FINISHED'} + + def invoke(self, context, event): + scn = context.scene + initSceneProperties(context, scn) + self.fade_duration = scn.default_fade_duration + self.fade_amount = scn.default_fade_amount + return context.window_manager.invoke_props_dialog(self) + + +# EXTEND TO FILL +class Sequencer_Extra_ExtendToFill(bpy.types.Operator): + bl_idname = 'sequencerextra.extendtofill' + bl_label = 'Extend to Fill' + bl_description = 'Extend active strip forward to fill adjacent space' + bl_options = {'REGISTER', 'UNDO'} + + @classmethod + def poll(cls, context): + scn = context.scene + if scn and scn.sequence_editor and scn.sequence_editor.active_strip: + return True + else: + return False + + def execute(self, context): + scn = context.scene + seq = scn.sequence_editor + meta_level = len(seq.meta_stack) + if meta_level > 0: + seq = seq.meta_stack[meta_level - 1] + strip = functions.act_strip(context) + chn = strip.channel + stf = strip.frame_final_end + enf = 300000 + + for i in seq.sequences: + ffs = i.frame_final_start + if (i.channel == chn and ffs > stf): + if ffs < enf: + enf = ffs + if enf == 300000 and stf < scn.frame_end: + enf = scn.frame_end + + if enf == 300000 or enf == stf: + self.report({'ERROR_INVALID_INPUT'}, 'Unable to extend') + return {'CANCELLED'} + else: + strip.frame_final_end = enf + + bpy.ops.sequencer.reload() + return {'FINISHED'} + + +# DISTRIBUTE +class Sequencer_Extra_Distribute(bpy.types.Operator): + bl_idname = 'sequencerextra.distribute' + bl_label = 'Distribute...' + bl_description = 'Evenly distribute selected strips' + bl_options = {'REGISTER', 'UNDO'} + + @classmethod + def poll(self, context): + scn = context.scene + if scn and scn.sequence_editor: + return scn.sequence_editor.sequences + else: + return False + + def execute(self, context): + scn = context.scene + seq = scn.sequence_editor + seq_all = scn.sequence_editor + meta_level = len(seq.meta_stack) + if meta_level > 0: + seq = seq.meta_stack[meta_level - 1] + seq_list = {} + first_start = 300000 + item_num = 0 + for i in seq.sequences: + if i.select == True: + seq_list[i.frame_start] = i.name + item_num += 1 + if i.frame_start < first_start: + first_start = i.frame_start + n = item_num - 1 + if(self.distribute_reverse): + for key in sorted(seq_list.keys()): + dest = first_start + (n * self.distribute_offset) + seq_all.sequences_all[str(seq_list[key])].frame_start = dest + n -= 1 + else: + for key in sorted(seq_list.keys(), reverse=True): + dest = first_start + (n * self.distribute_offset) + seq_all.sequences_all[str(seq_list[key])].frame_start = dest + n -= 1 + + scn.default_distribute_offset = self.distribute_offset + scn.default_distribute_reverse = self.distribute_reverse + return{'FINISHED'} + + def invoke(self, context, event): + scn = context.scene + initSceneProperties(context, scn) + self.distribute_offset = scn.default_distribute_offset + self.distribute_reverse = scn.default_distribute_reverse + return context.window_manager.invoke_props_dialog(self) + + +# SKIP ONE SECOND +class Sequencer_Extra_FrameSkip(bpy.types.Operator): + bl_label = 'Skip One Second' + bl_idname = 'screenextra.frame_skip' + bl_description = 'Skip through the Timeline by one-second increments' + bl_options = {'REGISTER', 'UNDO'} + back = BoolProperty( + name='Back', + default=False) + + def execute(self, context): + one_second = bpy.context.scene.render.fps + if self.back == True: + one_second *= -1 + bpy.ops.screen.frame_offset(delta=one_second) + return {'FINISHED'} + + +# JOG/SHUTTLE +class Sequencer_Extra_JogShuttle(bpy.types.Operator): + bl_label = 'Jog/Shuttle' + bl_idname = 'sequencerextra.jogshuttle' + bl_description = 'Jog through current sequence' + + def execute(self, context): + scn = context.scene + start_frame = scn.frame_start + end_frame = scn.frame_end + duration = end_frame - start_frame + diff = self.x - self.init_x + diff /= 5 + diff = int(diff) + extended_frame = diff + (self.init_current_frame - start_frame) + looped_frame = extended_frame % (duration + 1) + target_frame = start_frame + looped_frame + context.scene.frame_current = target_frame + + def modal(self, context, event): + if event.type == 'MOUSEMOVE': + self.x = event.mouse_x + self.execute(context) + elif event.type == 'LEFTMOUSE': + return {'FINISHED'} + elif event.type in ('RIGHTMOUSE', 'ESC'): + return {'CANCELLED'} + + return {'RUNNING_MODAL'} + + def invoke(self, context, event): + scn = context.scene + self.x = event.mouse_x + self.init_x = self.x + self.init_current_frame = scn.frame_current + self.execute(context) + context.window_manager.modal_handler_add(self) + return {'RUNNING_MODAL'} + + +# OPEN IN MOVIE CLIP EDITOR FROM FILE BROWSER +class Clip_Extra_OpenFromFileBrowser(bpy.types.Operator): + bl_label = 'Open from File Browser' + bl_idname = 'clipextra.openfromfilebrowser' + bl_description = 'Load a Movie or Image Sequence from File Browser' + bl_options = {'REGISTER', 'UNDO'} + + def execute(self, context): + for a in context.window.screen.areas: + if a.type == 'FILE_BROWSER': + params = a.spaces[0].params + break + try: + params + except: + self.report({'ERROR_INVALID_INPUT'}, 'No visible File Browser') + return {'CANCELLED'} + + if params.filename == '': + self.report({'ERROR_INVALID_INPUT'}, 'No file selected') + return {'CANCELLED'} + + strip = functions.act_strip(context) + path = params.directory + params.filename + strip_type = functions.detect_strip_type(params.filename) + data_exists = False + + if strip_type in ('MOVIE', 'IMAGE'): + for i in bpy.data.movieclips: + if i.filepath == path: + data_exists = True + data = i + + if data_exists == False: + try: + data = bpy.data.movieclips.load(filepath=path) + except: + self.report({'ERROR_INVALID_INPUT'}, 'Error loading file') + return {'CANCELLED'} + else: + self.report({'ERROR_INVALID_INPUT'}, 'Invalid file format') + return {'CANCELLED'} + + for a in context.window.screen.areas: + if a.type == 'CLIP_EDITOR': + a.spaces[0].clip = data + + return {'FINISHED'} + + +# OPEN IN MOVIE CLIP EDITOR FROM SEQUENCER +class Clip_Extra_OpenActiveStrip(bpy.types.Operator): + bl_label = 'Open Active Strip' + bl_idname = 'clipextra.openactivestrip' + bl_description = 'Load a Movie or Image Sequence from Sequence Editor' + bl_options = {'REGISTER', 'UNDO'} + + @classmethod + def poll(cls, context): + scn = context.scene + strip = functions.act_strip(context) + if scn and scn.sequence_editor and scn.sequence_editor.active_strip: + return strip.type in ('MOVIE', 'IMAGE') + else: + return False + + def execute(self, context): + strip = functions.act_strip(context) + data_exists = False + + if strip.type == 'MOVIE': + path = strip.filepath + elif strip.type == 'IMAGE': + base_dir = bpy.path.relpath(strip.directory) + filename = strip.elements[0].filename + path = base_dir + '/' + filename + else: + self.report({'ERROR_INVALID_INPUT'}, 'Invalid file format') + return {'CANCELLED'} + + for i in bpy.data.movieclips: + if i.filepath == path: + data_exists = True + data = i + if data_exists == False: + try: + data = bpy.data.movieclips.load(filepath=path) + except: + self.report({'ERROR_INVALID_INPUT'}, 'Error loading file') + return {'CANCELLED'} + + for a in context.window.screen.areas: + if a.type == 'CLIP_EDITOR': + a.spaces[0].clip = data + + return {'FINISHED'} + + +# PLACE FROM FILE BROWSER WITH PROXY +class Sequencer_Extra_PlaceFromFileBrowserProxy(bpy.types.Operator): + bl_label = 'Place' + bl_idname = 'sequencerextra.placefromfilebrowserproxy' + bl_description = 'Place or insert active file from File Browser, '\ + 'and add proxy file with according suffix and extension' + insert = BoolProperty(name='Insert', default=False) + build_25 = BoolProperty(name='default_build_25', + description='default build_25', + default=True) + build_50 = BoolProperty(name='default_build_50', + description='default build_50', + default=True) + build_75 = BoolProperty(name='default_build_75', + description='default build_75', + default=True) + build_100 = BoolProperty(name='default_build_100', + description='default build_100', + default=True) + proxy_suffix = StringProperty( + name='default proxy suffix', + description='default proxy filename suffix', + default="-25") + proxy_extension = StringProperty( + name='default proxy extension', + description='default proxy extension', + default=".mkv") + proxy_path = StringProperty( + name='default proxy path', + description='default proxy path', + default="") + + bl_options = {'REGISTER', 'UNDO'} + + def invoke(self, context, event): + scn = context.scene + initSceneProperties(context, scn) + self.build_25 = scn.default_build_25 + self.build_50 = scn.default_build_50 + self.build_75 = scn.default_build_75 + self.build_100 = scn.default_build_100 + self.proxy_suffix = scn.default_proxy_suffix + self.proxy_extension = scn.default_proxy_extension + self.proxy_path = scn.default_proxy_path + + return context.window_manager.invoke_props_dialog(self) + + def execute(self, context): + scn = context.scene + for a in context.window.screen.areas: + if a.type == 'FILE_BROWSER': + params = a.spaces[0].params + break + try: + params + except UnboundLocalError: + self.report({'ERROR_INVALID_INPUT'}, 'No visible File Browser') + return {'CANCELLED'} + + if params.filename == '': + self.report({'ERROR_INVALID_INPUT'}, 'No file selected (proxy)') + return {'CANCELLED'} + path = os.path.join(params.directory, params.filename) + frame = context.scene.frame_current + strip_type = functions.detect_strip_type(params.filename) + + try: + if strip_type == 'IMAGE': + image_file = [] + filename = {"name": params.filename} + image_file.append(filename) + f_in = scn.frame_current + f_out = f_in + scn.render.fps - 1 + bpy.ops.sequencer.image_strip_add(files=image_file, + directory=params.directory, frame_start=f_in, + frame_end=f_out, relative_path=False) + elif strip_type == 'MOVIE': + bpy.ops.sequencer.movie_strip_add(filepath=path, + frame_start=frame, relative_path=False) + + strip = functions.act_strip(context) + strip.use_proxy = True + + # check if proxy exists, otherwise, uncheck proxy custom file + proxy_filepath = params.directory + self.proxy_path + proxy_filename = params.filename.rpartition(".")[0] + \ + self.proxy_suffix + self.proxy_extension + proxypath = os.path.join(proxy_filepath, proxy_filename) + strip.proxy.build_25 = self.build_25 + strip.proxy.build_50 = self.build_50 + strip.proxy.build_75 = self.build_75 + strip.proxy.build_100 = self.build_100 + #print("----------------", proxypath) + if os.path.isfile(proxypath): + strip.use_proxy_custom_file = True + strip.proxy.filepath = proxypath + + elif strip_type == 'SOUND': + bpy.ops.sequencer.sound_strip_add(filepath=path, + frame_start=frame, relative_path=False) + else: + self.report({'ERROR_INVALID_INPUT'}, 'Invalid file format') + return {'CANCELLED'} + + except: + self.report({'ERROR_INVALID_INPUT'}, 'Error loading file (proxy)') + return {'CANCELLED'} + + scn.default_proxy_suffix = self.proxy_suffix + scn.default_proxy_extension = self.proxy_extension + scn.default_proxy_path = self.proxy_path + scn.default_build_25 = self.build_25 + scn.default_build_50 = self.build_50 + scn.default_build_75 = self.build_75 + scn.default_build_100 = self.build_100 + + if self.insert == True: + try: + bpy.ops.sequencerextra.insert() + except: + self.report({'ERROR_INVALID_INPUT'}, 'Execution Error, '\ + 'check your Blender version') + return {'CANCELLED'} + else: + strip = functions.act_strip(context) + scn.frame_current += strip.frame_final_duration + bpy.ops.sequencer.reload() + + return {'FINISHED'} + + +# OPEN IMAGE WITH EDITOR AND create movie clip strip +class Sequencer_Extra_CreateMovieclip(bpy.types.Operator): + bl_label = 'Create a Movieclip from selected strip' + bl_idname = 'sequencerextra.createmovieclip' + bl_description = 'Create a Movieclip strip from a MOVIE or IMAGE strip' + + """ + When a movie or image strip is selected, this operator creates a movieclip + or find the correspondent movieclip that already exists for this footage, + and add a VSE clip strip with same cuts the original strip has. + It can convert movie strips and image sequences, both with hard cuts or + soft cuts. + """ + + @classmethod + def poll(self, context): + strip = functions.act_strip(context) + scn = context.scene + if scn and scn.sequence_editor and scn.sequence_editor.active_strip: + return strip.type in ('MOVIE', 'IMAGE') + else: + return False + + def execute(self, context): + strip = functions.act_strip(context) + scn = context.scene + + if strip.type == 'MOVIE': + #print("movie", strip.frame_start) + path = strip.filepath + #print(path) + data_exists = False + for i in bpy.data.movieclips: + if i.filepath == path: + data_exists = True + data = i + newstrip = None + if data_exists == False: + try: + data = bpy.data.movieclips.load(filepath=path) + newstrip = bpy.ops.sequencer.movieclip_strip_add(\ + replace_sel=True, overlap=False, clip=data.name) + newstrip = functions.act_strip(context) + newstrip.frame_start = strip.frame_start\ + - strip.animation_offset_start + tin = strip.frame_offset_start + strip.frame_start + tout = tin + strip.frame_final_duration + #print(newstrip.frame_start, strip.frame_start, tin, tout) + functions.triminout(newstrip, tin, tout) + except: + self.report({'ERROR_INVALID_INPUT'}, 'Error loading file') + return {'CANCELLED'} + + else: + try: + newstrip = bpy.ops.sequencer.movieclip_strip_add(\ + replace_sel=True, overlap=False, clip=data.name) + newstrip = functions.act_strip(context) + newstrip.frame_start = strip.frame_start\ + - strip.animation_offset_start + # i need to declare the strip this way in order + # to get triminout() working + clip = bpy.context.scene.sequence_editor.sequences[\ + newstrip.name] + # i cannot change these movie clip attributes via scripts + # but it works in the python console... + #clip.animation_offset_start = strip.animation.offset_start + #clip.animation_offset_end = strip.animation.offset_end + #clip.frame_final_duration = strip.frame_final_duration + tin = strip.frame_offset_start + strip.frame_start + tout = tin + strip.frame_final_duration + #print(newstrip.frame_start, strip.frame_start, tin, tout) + functions.triminout(clip, tin, tout) + except: + self.report({'ERROR_INVALID_INPUT'}, 'Error loading file') + return {'CANCELLED'} + + elif strip.type == 'IMAGE': + #print("image") + base_dir = bpy.path.abspath(strip.directory) + scn.frame_current = strip.frame_start -\ + strip.animation_offset_start + # searching for the first frame of the sequencer. This is mandatory + # for hard cutted sequence strips to be correctly converted, + # avoiding to create a new movie clip if not needed + filename = sorted(os.listdir(base_dir))[0] + path = os.path.join(base_dir, filename) + #print(path) + data_exists = False + for i in bpy.data.movieclips: + #print(i.filepath, path) + if i.filepath == path: + data_exists = True + data = i + #print(data_exists) + if data_exists == False: + try: + data = bpy.data.movieclips.load(filepath=path) + newstrip = bpy.ops.sequencer.movieclip_strip_add(\ + replace_sel=True, overlap=False,\ + clip=data.name) + newstrip = functions.act_strip(context) + newstrip.frame_start = strip.frame_start\ + - strip.animation_offset_start + clip = bpy.context.scene.sequence_editor.sequences[\ + newstrip.name] + tin = strip.frame_offset_start + strip.frame_start + tout = tin + strip.frame_final_duration + #print(newstrip.frame_start, strip.frame_start, tin, tout) + functions.triminout(clip, tin, tout) + except: + self.report({'ERROR_INVALID_INPUT'}, 'Error loading file') + return {'CANCELLED'} + + else: + try: + newstrip = bpy.ops.sequencer.movieclip_strip_add(\ + replace_sel=True, overlap=False, clip=data.name) + newstrip = functions.act_strip(context) + newstrip.frame_start = strip.frame_start\ + - strip.animation_offset_start + # need to declare the strip this way in order + # to get triminout() working + clip = bpy.context.scene.sequence_editor.sequences[\ + newstrip.name] + # cannot change this atributes via scripts... + # but it works in the python console... + #clip.animation_offset_start = strip.animation.offset_start + #clip.animation_offset_end = strip.animation.offset_end + #clip.frame_final_duration = strip.frame_final_duration + tin = strip.frame_offset_start + strip.frame_start + tout = tin + strip.frame_final_duration + #print(newstrip.frame_start, strip.frame_start, tin, tout) + functions.triminout(clip, tin, tout) + except: + self.report({'ERROR_INVALID_INPUT'}, 'Error loading file') + return {'CANCELLED'} + + # show the new clip in a movie clip editor, if available. + if strip.type == 'MOVIE' or 'IMAGE': + for a in context.window.screen.areas: + if a.type == 'CLIP_EDITOR': + a.spaces[0].clip = data + + return {'FINISHED'} + + +# RECURSIVE LOADER + +class Sequencer_Extra_RecursiveLoader(bpy.types.Operator): + bl_idname = "sequencerextra.recursiveload" + bl_label = "recursive load" + bl_options = {'REGISTER', 'UNDO'} + + recursive = BoolProperty( + name='recursive', + description='Load in recursive folders', + default=False) + + recursive_select_by_extension = BoolProperty( + name='select by extension', + description='Load only clips with selected extension', + default=False) + + ext = EnumProperty( + items=functions.movieextdict, + name="extension", + default="3") + + recursive_proxies = BoolProperty( + name='use proxies', + description='Load in recursive folders', + default=False) + build_25 = BoolProperty(name='default_build_25', + description='build_25', + default=True) + build_50 = BoolProperty(name='default_build_50', + description='build_50', + default=False) + build_75 = BoolProperty(name='default_build_75', + description='build_75', + default=False) + build_100 = BoolProperty(name='default_build_100', + description='build_100', + default=False) + proxy_suffix = StringProperty( + name='proxy suffix', + description='proxy filename suffix', + default="-25") + proxy_extension = StringProperty( + name='proxy extension', + description='proxy extension', + default=".mkv") + proxy_path = StringProperty( + name='proxy path', + description='proxy path', + default="") + + + + @classmethod + def poll(self, context): + scn = context.scene + if scn and scn.sequence_editor: + return (scn.sequence_editor) + else: + return False + + def invoke(self, context, event): + scn = context.scene + try: + self.build_25 = scn.default_build_25 + self.build_50 = scn.default_build_50 + self.build_75 = scn.default_build_75 + self.build_100 = scn.default_build_100 + self.proxy_suffix = scn.default_proxy_suffix + self.proxy_extension = scn.default_proxy_extension + self.proxy_path = scn.default_proxy_path + self.recursive = scn.default_recursive + self.recursive_select_by_extension = scn.default_recursive_select_by_extension + self.recursive_proxies = scn.default_recursive_proxies + self.ext = scn.default_ext + except AttributeError: + initSceneProperties(context, scn) + self.build_25 = scn.default_build_25 + self.build_50 = scn.default_build_50 + self.build_75 = scn.default_build_75 + self.build_100 = scn.default_build_100 + self.proxy_suffix = scn.default_proxy_suffix + self.proxy_extension = scn.default_proxy_extension + self.proxy_path = scn.default_proxy_path + self.recursive = scn.default_recursive + self.recursive_select_by_extension = scn.default_recursive_select_by_extension + self.recursive_proxies = scn.default_recursive_proxies + self.ext = scn.default_ext + + return context.window_manager.invoke_props_dialog(self) + + def loader(self, context, filelist): + scn = context.scene + if filelist: + for i in filelist: + functions.setpathinbrowser(i[0], i[1]) + try: + if self.recursive_proxies: + bpy.ops.sequencerextra.placefromfilebrowserproxy( + proxy_suffix=self.proxy_suffix, + proxy_extension=self.proxy_extension, + proxy_path=self.proxy_path, + build_25=self.build_25, + build_50=self.build_50, + build_75=self.build_75, + build_100=self.build_100) + else: + bpy.ops.sequencerextra.placefromfilebrowser() + except: + print("Error loading file (recursive loader error): ", i[1]) + functions.add_marker(context, i[1]) + self.report({'ERROR_INVALID_INPUT'}, 'Error loading file ') + pass + + + def execute(self, context): + scn = context.scene + #initSceneProperties(context, scn) + if self.recursive == True: + #recursive + #print(functions.sortlist(functions.recursive(\ + #context, self.recursive_select_by_extension, self.ext))) + self.loader(context, functions.sortlist(\ + functions.recursive(context, self.recursive_select_by_extension,\ + self.ext))) + else: + #non recursive + #print(functions.sortlist(functions.onefolder(\ + #context, self.recursive_select_by_extension, self.ext))) + self.loader(context, functions.sortlist(functions.onefolder(\ + context, self.recursive_select_by_extension, self.ext))) + try: + scn.default_build_25 = self.build_25 + scn.default_build_50 = self.build_50 + scn.default_build_75 = self.build_75 + scn.default_build_100 = self.build_100 + scn.default_proxy_suffix = self.proxy_suffix + scn.default_proxy_extension = self.proxy_extension + scn.default_proxy_path = self.proxy_path + scn.default_recursive = self.recursive + scn.default_recursive_select_by_extension = self.recursive_select_by_extension + scn.default_recursive_proxies = self.recursive_proxies + scn.default_ext = self.ext + except AttributeError: + initSceneProperties(context, scn) + self.build_25 = scn.default_build_25 + self.build_50 = scn.default_build_50 + self.build_75 = scn.default_build_75 + self.build_100 = scn.default_build_100 + self.proxy_suffix = scn.default_proxy_suffix + self.proxy_extension = scn.default_proxy_extension + self.proxy_path = scn.default_proxy_path + self.recursive = scn.default_recursive + self.recursive_select_by_extension = scn.default_recursive_select_by_extension + self.recursive_proxies = scn.default_recursive_proxies + self.ext = scn.default_ext + + return {'FINISHED'} + + +# READ EXIF DATA +class Sequencer_Extra_ReadExifData(bpy.types.Operator): + # load exifdata from strip to scene['metadata'] property + bl_label = 'Read EXIF Data' + bl_idname = 'sequencerextra.read_exif' + bl_description = 'Load exifdata from strip to metadata property in scene' + bl_options = {'REGISTER', 'UNDO'} + + @classmethod + def poll(self, context): + strip = functions.act_strip(context) + scn = context.scene + if scn and scn.sequence_editor and scn.sequence_editor.active_strip: + return (strip.type == 'IMAGE' or 'MOVIE') + else: + return False + + def execute(self, context): + try: + exiftool.ExifTool().start() + except: + self.report({'ERROR_INVALID_INPUT'}, + 'exiftool not found in PATH') + return {'CANCELLED'} + + def getexifdata(strip): + def getlist(lista): + for root, dirs, files in os.walk(path): + for f in files: + if "." + f.rpartition(".")[2].lower() \ + in functions.imb_ext_image: + lista.append(f) + #if "."+f.rpartition(".")[2] in imb_ext_movie: + # lista.append(f) + strip.elements + lista.sort() + return lista + + def getexifvalues(lista): + metadata = [] + with exiftool.ExifTool() as et: + try: + metadata = et.get_metadata_batch(lista) + except UnicodeDecodeError as Err: + print(Err) + return metadata + if strip.type == "IMAGE": + path = bpy.path.abspath(strip.directory) + if strip.type == "MOVIE": + path = bpy.path.abspath(strip.filepath.rpartition("/")[0]) + os.chdir(path) + #get a list of files + lista = [] + for i in strip.elements: + lista.append(i.filename) + return getexifvalues(lista) + + sce = bpy.context.scene + frame = sce.frame_current + text = bpy.context.active_object + strip = context.scene.sequence_editor.active_strip + sce['metadata'] = getexifdata(strip) + return {'FINISHED'}