diff options
Diffstat (limited to 'kinoraw_tools/operators_extra_actions.py')
-rw-r--r-- | kinoraw_tools/operators_extra_actions.py | 1316 |
1 files changed, 1316 insertions, 0 deletions
diff --git a/kinoraw_tools/operators_extra_actions.py b/kinoraw_tools/operators_extra_actions.py new file mode 100644 index 00000000..cae4a5d4 --- /dev/null +++ b/kinoraw_tools/operators_extra_actions.py @@ -0,0 +1,1316 @@ +# ##### 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 ##### + +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 + +# ------------------------------ + +# 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'} + + +# 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 + + bpy.ops.sequencer.view_all() + + 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 + + bpy.ops.sequencer.view_selected() + 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'} + + +# 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'} + + +# 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'} + + +# 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'} + + +# 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 + + scn = context.scene + seq = scn.sequence_editor + meta_level = len(seq.meta_stack) + if meta_level > 0: + seq = seq.meta_stack[meta_level - 1] + + for i in seq.sequences: + if (i.select == True and not i.mute): + 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 + p = strip.proxy.use_proxy_custom_directory # pep80 + i.proxy.use_proxy_custom_directory = p + i.proxy.use_proxy_custom_file = strip.proxy.use_proxy_custom_file + i.proxy.build_100 = strip.proxy.build_100 + 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.directory = strip.proxy.directory + i.proxy.filepath = strip.proxy.filepath + i.proxy.quality = strip.proxy.quality + i.proxy.timecode = strip.proxy.timecode + i.proxy.use_overwrite = strip.proxy.use_overwrite + 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.kr_default_fade_duration = self.fade_duration + scn.kr_default_fade_amount = self.fade_amount + return{'FINISHED'} + + def invoke(self, context, event): + scn = context.scene + functions.initSceneProperties(context) + self.fade_duration = scn.kr_default_fade_duration + self.fade_amount = scn.kr_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'} + + +# ------------------------------ + + +# 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 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'} + + +# 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 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'} + + +# ------------------------------ + +# 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'} + + +# ------------------------------ + + +# 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'} |