# ##### 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'}