Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormeta-androcto <meta.androcto1@gmail.com>2017-04-15 07:25:27 +0300
committermeta-androcto <meta.androcto1@gmail.com>2017-04-15 07:25:27 +0300
commite9f96e1d93a50e8b1a0faa360af63738956737cb (patch)
tree40ae98cc0c342f7a5777dc9ba0263de9d57d263c
parent725dbe4d410d5afbd903b1d6464854fd25166913 (diff)
Initial commit kinoraw_tools by Carlos Padial T51109 T50357
-rw-r--r--kinoraw_tools/__init__.py335
-rw-r--r--kinoraw_tools/audio_tools.py360
-rw-r--r--kinoraw_tools/datamosh.py194
-rw-r--r--kinoraw_tools/eco.py140
-rw-r--r--kinoraw_tools/exiftool.py330
-rw-r--r--kinoraw_tools/functions.py439
-rw-r--r--kinoraw_tools/jumptocut.py645
-rw-r--r--kinoraw_tools/operators_extra_actions.py1316
-rw-r--r--kinoraw_tools/proxy_tools.py333
-rw-r--r--kinoraw_tools/random_editor.py218
-rw-r--r--kinoraw_tools/recursive_loader.py261
-rw-r--r--kinoraw_tools/ui.py757
12 files changed, 5328 insertions, 0 deletions
diff --git a/kinoraw_tools/__init__.py b/kinoraw_tools/__init__.py
new file mode 100644
index 00000000..58bcccac
--- /dev/null
+++ b/kinoraw_tools/__init__.py
@@ -0,0 +1,335 @@
+# ##### 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 #####
+
+bl_info = {
+ "name": "Kinoraw Tools",
+ "author": "Carlos Padial, Turi Scandurra",
+ "version": (0, 5),
+ "blender": (2, 74, 0),
+ "location": "Sequencer",
+ "description": "compilation of tools to improve video editing with blender's VSE",
+ "wiki_url": "https://github.com/kinoraw/kinoraw_tools/blob/master/README.md",
+ "tracker_url": "https://github.com/kinoraw/kinoraw_tools",
+ "support": "COMMUNITY",
+ "category": "Sequencer"
+ }
+
+
+if "bpy" in locals():
+ import imp
+ imp.reload(jumptocut)
+ imp.reload(operators_extra_actions)
+ imp.reload(audio_tools)
+ imp.reload(proxy_tools)
+ imp.reload(recursive_loader)
+ imp.reload(eco)
+ imp.reload(random_editor)
+ imp.reload(ui)
+ imp.reload(datamosh)
+else:
+ from . import jumptocut
+ from . import operators_extra_actions
+ from . import audio_tools
+ from . import proxy_tools
+ from . import recursive_loader
+ from . import eco
+ from . import random_editor
+ from . import ui
+ from . import datamosh
+
+import bpy
+import os.path
+from bpy.types import Menu
+from bpy.types import Header
+
+from bpy.props import IntProperty, StringProperty, BoolProperty, EnumProperty
+
+
+class KinorawToolsAddon(bpy.types.AddonPreferences):
+ # this must match the addon name, use '__package__'
+ # when defining this in a submodule of a python package.
+ bl_idname = __package__
+ bl_option = {'REGISTER'}
+
+ # extra_actions
+ kr_show_tools = BoolProperty(
+ name = "show tools" ,
+ description = 'show extra tools in the panel',
+ default = False)
+ kr_mini_ui = BoolProperty(
+ name = "mini ui" ,
+ description = 'enable mini UI',
+ default = True)
+ kr_show_info = BoolProperty(
+ name = 'show info',
+ description = 'show basic info from selected strip',
+ default = False)
+ kr_show_trim = BoolProperty(
+ name = 'show trim',
+ default = False)
+ kr_show_modifiers = BoolProperty(
+ name = 'show modifiers',
+ default = False)
+ kr_extra_info = BoolProperty(
+ name = 'show extra info',
+ default = False)
+
+ # exif
+ use_exif_panel = BoolProperty(
+ name = 'exif info panel | depends on external programs, see documentation',
+ default = False)
+
+ # glitch
+ use_glitch_panel = BoolProperty(
+ name = 'glitch panel | depends on external programs, see documentation',
+ default = False)
+ all_keyframes = BoolProperty(
+ name = 'remove all keyframes',
+ default = True)
+ load_glitch = BoolProperty(
+ name = 'load glitch after conversion > UNSTABLE!!!',
+ default = True)
+
+ # jump to cut
+ use_jumptocut = BoolProperty(
+ name = 'jumptocut panel',
+ default = True)
+ use_io_tools = BoolProperty(
+ name = 'enable in and out tools in jumptocut panel',
+ default = False)
+
+ # Proxy Tools
+ use_proxy_tools = BoolProperty(
+ name = 'proxy tools panel | depends on external programs, see documentation',
+ default = False)
+ proxy_dir = StringProperty(
+ name = 'Proxy Custom Directory',
+ default = "//proxies/")
+ proxy_scripts_path = StringProperty(
+ name = 'directory to store proxy scripts',
+ default = "//proxy_scripts/")
+ proxy_scripts = BoolProperty(
+ name = 'generate ffmpeg scritps',
+ default = False)
+ ffmpeg_command = StringProperty(
+ name = 'command to generate proxy',
+ default = '''ffmpeg -i {} -vcodec mjpeg -q:v 10 -s {}x{} -an -y {}''')
+ use_internal_proxy = BoolProperty(
+ name = 'use internal blender proxy system',
+ default = True)
+ use_bi_custom_directory = BoolProperty(
+ name = 'Proxy Custom Directory',
+ default = True)
+ quality = IntProperty(
+ name = 'Quality',
+ default = 90,
+ min = 0, max = 32767)
+ tc_list = [ ( "NONE", "No TC in use","" ), ( "RECORD_RUN", "Record Run", "" ),
+ ( "FREE_RUN", "Free Run", "" ), ("FREE_RUN_REC_DATE", "Free Run (rec date)", "" ),
+ ( "RECORD_RUN_NO_GAPS", "Record Run No Gaps", "" )]
+ timecode = EnumProperty(
+ name = "Settings Type",
+ items = tc_list,
+ default="NONE",
+ description = "timecode" )
+
+ # Audio Tools
+ use_audio_tools = BoolProperty(
+ name='audio tools panel | depends on external programs, see documentation',
+ default=False)
+ audio_dir = StringProperty(
+ name='path to store extracted audio',
+ default="//audio/")
+ audio_scripts_path = StringProperty(
+ name='path to store audio scripts',
+ default="//audio_scripts/")
+ audio_scripts = BoolProperty(
+ name='generate ffmpeg scritps',
+ default=False)
+
+ # Audio Tools - external links
+ audio_use_external_links = BoolProperty(
+ name='use external audio linked to movie strips',
+ default=False)
+ audio_external_filename = StringProperty(
+ name='file to store info about linked audio',
+ default="//external_audio_sync_info.txt")
+
+ # audio tools vu-meter
+
+ meterbridge = [ ( "VU", "classic moving needle VU meter","" ), ( "PPM", "PPM meter", "" ),
+ ( "DPM", "Digital peak meter", "" ), ("JF", "'Jellyfish' phase meter", "" ),
+ ( "SCO", "Oscilloscope meter", "" )]
+
+ metertype = EnumProperty(
+ name = "meter type",
+ items = meterbridge,
+ default="DPM",
+ description = "meterbridge meter type" )
+
+ # eco
+ use_eco_tools = BoolProperty(
+ name='eco tools panel',
+ default=True)
+ eco_value = IntProperty(
+ name = 'number of echoes',
+ default = 5,
+ min = 1, max = 25)
+ eco_offset = IntProperty(
+ name = 'Echo Offset',
+ default = 1,
+ min = -25000, max = 25000)
+ eco_use_add_blend_mode = BoolProperty(
+ name = 'use_add_blend_mode',
+ default = False)
+
+ # random editor
+ use_random_editor = BoolProperty(
+ name='random editor panel | Experimental',
+ default=False)
+ random_frames = IntProperty(
+ name='frames',
+ default=1,
+ min = 1, max = 1000)
+ random_selected_scene = StringProperty(
+ name = 'selected_scene',
+ default = '')
+ random_use_marker_subsets = BoolProperty(
+ name = 'use_marker_subsets',
+ default = True)
+ random_number_of_subsets = IntProperty(
+ name = 'number_of_subsets',
+ default = 3,
+ min = 1, max = 5)
+
+ def draw(self, context):
+
+ layout = self.layout
+ layout.prop(self, "use_jumptocut")
+ layout = self.layout
+ layout.prop(self, "use_proxy_tools")
+ layout = self.layout
+ layout.prop(self, "use_audio_tools")
+ layout = self.layout
+ layout.prop(self, "use_exif_panel")
+ layout = self.layout
+ layout.prop(self, "use_eco_tools")
+ layout = self.layout
+ layout.prop(self, "use_random_editor")
+ layout = self.layout
+ layout.prop(self, "use_glitch_panel")
+
+
+# Registration
+def register():
+ bpy.utils.register_class(KinorawToolsAddon)
+
+ bpy.utils.register_module(__name__)
+
+ # Append menu entries
+ bpy.types.SEQUENCER_MT_add.prepend(ui.sequencer_add_menu_func)
+ bpy.types.SEQUENCER_MT_select.prepend(ui.sequencer_select_menu_func)
+ bpy.types.SEQUENCER_MT_strip.prepend(ui.sequencer_strip_menu_func)
+ bpy.types.SEQUENCER_HT_header.append(ui.sequencer_header_func)
+ bpy.types.CLIP_HT_header.append(ui.clip_header_func)
+ bpy.types.CLIP_MT_clip.prepend(ui.clip_clip_menu_func)
+ bpy.types.TIME_MT_frame.prepend(ui.time_frame_menu_func)
+ bpy.types.TIME_HT_header.append(ui.time_header_func)
+
+ # Add keyboard shortcut configuration
+ kc = bpy.context.window_manager.keyconfigs.addon
+ km = kc.keymaps.new(name='Frames')
+
+ # jump 1 second
+ kmi = km.keymap_items.new('screenextra.frame_skip',
+ 'RIGHT_ARROW', 'PRESS', ctrl=True, shift=True)
+ kmi.properties.back = False
+ kmi = km.keymap_items.new('screenextra.frame_skip',
+ 'LEFT_ARROW', 'PRESS', ctrl=True, shift=True)
+ kmi.properties.back = True
+
+ # jump to cut
+ kmi = km.keymap_items.new("sequencer.strip_jump",
+ 'Q', 'PRESS', ctrl=False, shift=False)
+ kmi.properties.next=False
+ kmi.properties.center=False
+ kmi = km.keymap_items.new("sequencer.strip_jump",
+ 'W', 'PRESS', ctrl=False, shift=False)
+ kmi.properties.next=True
+ kmi.properties.center=False
+
+ # in and out
+ kmi = km.keymap_items.new("sequencerextra.sourcein",
+ 'I', 'PRESS', ctrl=True, shift=True)
+ kmi = km.keymap_items.new("sequencerextra.sourceout",
+ 'O', 'PRESS', ctrl=True, shift=True)
+
+ #markers
+ kc = bpy.context.window_manager.keyconfigs.active
+ km = kc.keymaps.new(name='Screen')
+ kmi = km.keymap_items.new("screen.marker_jump",
+ 'Q', 'PRESS', ctrl=False, shift=True)
+ kmi.properties.next=False
+ kmi = km.keymap_items.new("screen.marker_jump",
+ 'W', 'PRESS', ctrl=False, shift=True)
+ kmi.properties.next=True
+
+
+def unregister():
+ bpy.utils.unregister_module(__name__)
+
+
+ try:
+ bpy.utils.unregister_class(KinorawToolsAddon)
+ except RuntimeError:
+ pass
+
+
+
+ # Remove menu entries
+ bpy.types.SEQUENCER_MT_add.remove(ui.sequencer_add_menu_func)
+ bpy.types.SEQUENCER_MT_select.remove(ui.sequencer_select_menu_func)
+ bpy.types.SEQUENCER_MT_strip.remove(ui.sequencer_strip_menu_func)
+ bpy.types.SEQUENCER_HT_header.remove(ui.sequencer_header_func)
+ bpy.types.CLIP_HT_header.remove(ui.clip_header_func)
+ bpy.types.CLIP_MT_clip.remove(ui.clip_clip_menu_func)
+ bpy.types.TIME_MT_frame.remove(ui.time_frame_menu_func)
+ bpy.types.TIME_HT_header.remove(ui.time_header_func)
+
+
+ # Remove keyboard shortcut configuration
+ kc = bpy.context.window_manager.keyconfigs.addon
+ km = kc.keymaps['Frames']
+ km.keymap_items.remove(km.keymap_items['screenextra.frame_skip'])
+ km.keymap_items.remove(km.keymap_items['screenextra.frame_skip'])
+
+ km.keymap_items.remove(km.keymap_items['sequencer.strip_jump'])
+ km.keymap_items.remove(km.keymap_items['sequencer.strip_jump'])
+
+ km.keymap_items.remove(km.keymap_items['sequencerextra.sourcein'])
+ km.keymap_items.remove(km.keymap_items['sequencerextra.sourceout'])
+
+
+ kc = bpy.context.window_manager.keyconfigs.active
+ km = kc.keymaps['Screen']
+ km.keymap_items.remove(km.keymap_items['screen.marker_jump'])
+ km.keymap_items.remove(km.keymap_items['screen.marker_jump'])
+
+
+if __name__ == '__main__':
+ register()
diff --git a/kinoraw_tools/audio_tools.py b/kinoraw_tools/audio_tools.py
new file mode 100644
index 00000000..d0278de0
--- /dev/null
+++ b/kinoraw_tools/audio_tools.py
@@ -0,0 +1,360 @@
+import bpy, os
+from bpy.props import IntProperty, StringProperty, BoolProperty
+import subprocess
+
+from . import functions
+
+
+proxy_qualities = [ ( "1", "25%", "" ), ( "2", "50%", "" ),
+ ( "3", "75%", "" ), ( "4", "100%", "" )]
+
+#
+# ls *.sh | parallel -j 8 sh {}
+#
+
+# functions
+
+
+
+
+def createsyncfile(filename):
+ if not os.path.isfile(bpy.path.abspath(filename)):
+ f = open(bpy.path.abspath(filename), "w")
+ data = []
+
+ try:
+ f.writelines(data) # Write a sequence of strings to a file
+ finally:
+ f.close()
+
+
+def readsyncfile(filename):
+ try:
+ file = open(bpy.path.abspath(filename))
+ data = file.readlines()
+ file.close()
+
+ return data
+
+ except IOError:
+ pass
+
+
+def writesyncfile(filename, data):
+ try:
+ f = open(bpy.path.abspath(filename), "w")
+ try:
+ for line in data:
+ f.writelines(line) # Write a sequence of strings to a file
+ finally:
+ f.close()
+
+ except IOError:
+ pass
+
+
+# classes
+
+
+class ExtractWavOperator(bpy.types.Operator):
+ """ Use ffmpeg to extract audio from video and import it synced"""
+ bl_idname = "sequencer.extract_wav_operator"
+ bl_label = "Extract Wav from movie strip Operator"
+
+ @staticmethod
+ def has_sequencer(context):
+ return (context.space_data.view_type\
+ in {'SEQUENCER', 'SEQUENCER_PREVIEW'})
+
+ @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')
+ else:
+ return False
+
+
+
+ def execute(self, context):
+
+ preferences = context.user_preferences
+ audio_dir = preferences.addons[__package__].preferences.audio_dir
+
+ functions.create_folder(bpy.path.abspath(audio_dir))
+
+ for strip in context.selected_editable_sequences:
+
+ # get filename
+ if strip.type == "MOVIE":
+ filename = bpy.path.abspath(strip.filepath)
+ newfilename = bpy.path.abspath(strip.filepath).rpartition(
+ "/")[2]
+ fileoutput = os.path.join(bpy.path.abspath(audio_dir),
+ newfilename)+".wav"
+
+ # check for wav existing file
+ if not os.path.isfile(fileoutput):
+ #if not, extract the file
+ extract_audio = "ffmpeg -i '{}' -acodec pcm_s16le -ac 2 {}".\
+ format(filename, fileoutput)
+ print(extract_audio)
+ os.system(extract_audio)
+ else:
+ print("ya existe")
+
+ if strip.type == "MOVIE":
+ # import the file and trim in the same way the original
+ bpy.ops.sequencer.sound_strip_add(filepath=fileoutput,\
+ frame_start=strip.frame_start, channel=strip.channel+1,\
+ replace_sel=True, overlap=False, cache=False)
+
+ # Update scene
+ context.scene.update()
+
+ newstrip = context.scene.sequence_editor.active_strip
+
+ # deselect all other strips
+ for i in context.selected_editable_sequences:
+ if i.name != newstrip.name:
+ i.select=False
+
+ # Update scene
+ context.scene.update()
+
+ #Match the original clip's length
+
+ newstrip.frame_start = strip.frame_start - strip.animation_offset_start
+
+ #print(newstrip.name)
+ functions.triminout(newstrip,
+ strip.frame_start + strip.frame_offset_start,
+ strip.frame_start + strip.frame_offset_start + \
+ strip.frame_final_duration)
+
+ return {'FINISHED'}
+
+
+
+class ExternalAudioSetSyncOperator(bpy.types.Operator):
+ """get sync info from selected audio and video strip and store it into a text file """
+ bl_idname = "sequencer.external_audio_set_sync"
+ bl_label = "set sync info"
+
+ @staticmethod
+ def has_sequencer(context):
+ return (context.space_data.view_type\
+ in {'SEQUENCER', 'SEQUENCER_PREVIEW'})
+
+ @classmethod
+ def poll(cls, context):
+ if cls.has_sequencer(context):
+ if len(context.selected_editable_sequences) == 2:
+ types = []
+ for i in context.selected_editable_sequences:
+ types.append(i.type)
+ if 'MOVIE' and 'SOUND' in types:
+ return True
+ else:
+ return False
+
+
+ def execute(self, context):
+
+ preferences = context.user_preferences
+ filename = preferences.addons[__package__].preferences.audio_external_filename
+
+ for strip in context.selected_editable_sequences:
+ if strip.type == "MOVIE":
+ moviestrip = strip
+ elif strip.type == "SOUND":
+ soundstrip = strip
+
+ offset = str(moviestrip.frame_start - soundstrip.frame_start)
+
+ data1 = readsyncfile(filename)
+ data2 = []
+ newline = moviestrip.filepath + " " + soundstrip.filepath + " " + offset + "\n"
+
+ if data1 != None:
+ repeated = False
+ for line in data1:
+ if line.split()[0] == moviestrip.filepath and line.split()[1] == soundstrip.filepath:
+ data2.append(newline)
+ repeated = True
+ else:
+ data2.append(line)
+ if not repeated:
+ data2.append(newline)
+ else:
+ data2.append(newline)
+
+ createsyncfile(filename)
+ writesyncfile(filename, data2)
+
+ return {'FINISHED'}
+
+
+class ExternalAudioReloadOperator(bpy.types.Operator):
+ """ reload external audio synced to selected movie strip acording to info from a text file """
+ bl_idname = "sequencer.external_audio_reload"
+ bl_label = "reload external audio"
+
+ @staticmethod
+ def has_sequencer(context):
+ return (context.space_data.view_type\
+ in {'SEQUENCER', 'SEQUENCER_PREVIEW'})
+
+ @classmethod
+ def poll(cls, context):
+ if cls.has_sequencer(context):
+ if len(context.selected_editable_sequences) == 1:
+ if context.selected_editable_sequences[0].type == 'MOVIE':
+ return True
+ else:
+ return False
+
+
+ def execute(self, context):
+
+ preferences = context.user_preferences
+ filename = preferences.addons[__package__].preferences.audio_external_filename
+
+ data = readsyncfile(filename)
+
+ for strip in context.selected_editable_sequences:
+
+ sounds = []
+ for line in data:
+ if line.split()[0] == strip.filepath:
+ moviefile = bpy.path.abspath(line.split()[0])
+ soundfile = bpy.path.abspath(line.split()[1])
+ offset = int(line.split()[2])
+ sounds.append((soundfile, offset))
+
+
+ for soundfile, offset in sounds:
+ print(soundfile,offset)
+ print(strip.filepath)
+ # find start frame for sound strip (using offset from file)
+ sound_frame_start = strip.frame_start - strip.animation_offset_start - offset
+
+ # import the file and trim in the same way the original
+ bpy.ops.sequencer.sound_strip_add(filepath=soundfile,\
+ frame_start=sound_frame_start, channel=strip.channel+1,\
+ replace_sel=True, overlap=False, cache=False)
+
+ # Update scene
+ context.scene.update()
+
+ newstrip = context.scene.sequence_editor.active_strip
+
+ # deselect all other strips
+ for i in context.selected_editable_sequences:
+ if i.name != newstrip.name:
+ i.select=False
+
+ # Update scene
+ context.scene.update()
+
+ # trim sound strip like original one
+ functions.triminout(newstrip,
+ strip.frame_start + strip.frame_offset_start,
+ strip.frame_start + strip.frame_offset_start + \
+ strip.frame_final_duration)
+
+ return {'FINISHED'}
+
+
+class AudioToolPanel(bpy.types.Panel):
+ """ """
+ bl_label = "Audio Tools"
+ bl_idname = "OBJECT_PT_AudioTool"
+ bl_space_type = 'SEQUENCE_EDITOR'
+ bl_region_type = 'UI'
+
+ @classmethod
+ def poll(self, context):
+ if context.space_data.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}:
+ strip = functions.act_strip(context)
+ scn = context.scene
+ preferences = context.user_preferences
+ prefs = preferences.addons[__package__].preferences
+ if scn and scn.sequence_editor and scn.sequence_editor.active_strip:
+ if prefs.use_audio_tools:
+ return True
+ else:
+ return False
+
+ def draw_header(self, context):
+ layout = self.layout
+ layout.label(text="", icon="PLAY_AUDIO")
+
+ def draw(self, context):
+
+ preferences = context.user_preferences
+ prefs = preferences.addons[__package__].preferences
+
+ strip = functions.act_strip(context)
+
+ if strip.type == "MOVIE":
+ layout = self.layout
+ layout.prop(prefs, "audio_dir", text="path for audio files")
+
+
+ layout.operator("sequencer.extract_wav_operator", text="Extract Wav")
+
+
+ layout = self.layout
+ layout.prop(prefs, "audio_scripts")
+
+ if prefs.audio_scripts:
+ layout = self.layout
+ layout.prop(prefs, "audio_scripts_path", text="path for scripts")
+
+ layout = self.layout
+ layout.prop(prefs, "audio_use_external_links", text="external audio sync")
+
+
+ if prefs.audio_use_external_links:
+ layout = self.layout
+ layout.prop(prefs, "audio_external_filename", text="sync data")
+
+ row = layout.row(align=True)
+ row.operator("sequencer.external_audio_set_sync", text="set sync")
+ row.operator("sequencer.external_audio_reload", text="reload audio")
+
+ layout = self.layout
+
+ row = layout.row()
+ row.prop(prefs, "metertype", text="")
+ row.operator("sequencer.openmeterbridge", text="Launch Audio Meter", icon = "SOUND")
+
+
+class OpenMeterbridgeOperator(bpy.types.Operator):
+ """ """
+ bl_idname = "sequencer.openmeterbridge"
+ bl_label = "open external vu meter to work with jack"
+
+ @staticmethod
+ def has_sequencer(context):
+ return (context.space_data.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'})
+
+ @classmethod
+ def poll(cls, context):
+ if cls.has_sequencer(context):
+ if len(context.selected_editable_sequences) == 1:
+ return True
+
+
+ def execute(self, context):
+ preferences = context.user_preferences
+ prefs = preferences.addons[__package__].preferences
+
+ command = "meterbridge -t {} 'PulseAudio JACK Sink:front-left' 'PulseAudio JACK Sink:front-right' &".format(prefs.metertype.lower())
+ p = subprocess.Popen(command,stdout=subprocess.PIPE, shell = True)
+
+ return {'FINISHED'}
+
+
diff --git a/kinoraw_tools/datamosh.py b/kinoraw_tools/datamosh.py
new file mode 100644
index 00000000..0dc9d015
--- /dev/null
+++ b/kinoraw_tools/datamosh.py
@@ -0,0 +1,194 @@
+import bpy, os
+from bpy.props import IntProperty, StringProperty, BoolProperty
+import subprocess
+
+from . import functions
+
+
+proxy_qualities = [ ( "1", "25%", "" ), ( "2", "50%", "" ),
+ ( "3", "75%", "" ), ( "4", "100%", "" ),
+ ( "5", "none", "" )]
+
+
+# functions
+
+def createdatamosh(context, strip):
+ preferences = context.user_preferences
+ prefs = preferences.addons[__package__].preferences
+
+ fileinput = bpy.path.abspath(strip.filepath)
+ fileoutput = fileinput.rpartition(".")[0]+"_datamosh.avi"
+
+ if prefs.all_keyframes:
+ command = "datamosh '{}' -a -o '{}'".format(fileinput, fileoutput)
+ else:
+ command = "datamosh '{}' -o '{}'".format(fileinput, fileoutput)
+ print(command)
+ os.system(command)
+ return fileoutput
+
+def createavi(context, strip):
+ preferences = context.user_preferences
+ prefs = preferences.addons[__package__].preferences
+
+ fileinput = bpy.path.abspath(strip.filepath)
+ fileoutput = fileinput.rpartition(".")[0]+"_.avi"
+
+ command = "ffmpeg -i '{}' -vcodec copy '{}'".format(fileinput, fileoutput)
+
+ print(command)
+ os.system(command)
+ return fileoutput
+
+def createavimjpeg(context, strip):
+ preferences = context.user_preferences
+ prefs = preferences.addons[__package__].preferences
+
+ fileinput = bpy.path.abspath(strip.filepath)
+ fileoutput = fileinput.rpartition(".")[0]+"_mjpeg.avi"
+
+ command = "ffmpeg -i '{}' -vcodec mjpeg -q:v 1 '{}'".format(fileinput, fileoutput)
+
+ print(command)
+ os.system(command)
+ return fileoutput
+
+
+
+
+# classes
+
+class CreateAvi(bpy.types.Operator):
+ """ """
+ bl_idname = "sequencer.createavi"
+ bl_label = "create avi file"
+
+ @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')
+ else:
+ return False
+
+ size = IntProperty(
+ name='proxysize',
+ default=1)
+ bl_options = {'REGISTER', 'UNDO'}
+
+ def execute(self, context):
+
+ preferences = context.user_preferences
+ strips = functions.get_selected_strips(context)
+
+ for strip in strips:
+ #deselect all other strips
+ for i in strips: i.select = False
+ #select current strip
+ strip.select = True
+ if strip.type == "MOVIE":
+ if self.size == 1:
+ fileoutput = createavi(context, strip)
+ elif self.size == 2:
+ fileoutput = createavimjpeg(context, strip)
+ strip.filepath = fileoutput
+ #select all strips again
+ for strip in strips:
+ try:
+ strip.select=True
+ except ReferenceError:
+ pass
+ bpy.ops.sequencer.reload()
+ return {'FINISHED'}
+
+
+class CreateDatamosh(bpy.types.Operator):
+ """ """
+ bl_idname = "sequencer.createdatamosh"
+ bl_label = "create datamosh"
+
+ @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')
+ else:
+ return False
+
+ def execute(self, context):
+
+ preferences = context.user_preferences
+ prefs = preferences.addons[__package__].preferences
+ strips = functions.get_selected_strips(context)
+
+ for strip in strips:
+ #deselect all other strips
+ for i in strips: i.select = False
+ #select current strip
+ strip.select = True
+ if strip.type == "MOVIE":
+ fileoutput = createdatamosh(context, strip)
+ if prefs.load_glitch:
+ strip.filepath = fileoutput
+ #select all strips again
+ for strip in strips:
+ try:
+ strip.select=True
+ except ReferenceError:
+ pass
+ bpy.ops.sequencer.reload()
+ return {'FINISHED'}
+
+
+class CreateGlitchToolPanel(bpy.types.Panel):
+ """ """
+ bl_label = "Glitch Tools"
+ bl_idname = "OBJECT_PT_GlitchTool"
+ bl_space_type = 'SEQUENCE_EDITOR'
+ bl_region_type = 'UI'
+
+
+ @classmethod
+ def poll(self, context):
+ if context.space_data.view_type in {'SEQUENCER',
+ 'SEQUENCER_PREVIEW'}:
+ strip = functions.act_strip(context)
+ scn = context.scene
+ preferences = context.user_preferences
+ prefs = preferences.addons[__package__].preferences
+ if scn and scn.sequence_editor and scn.sequence_editor.active_strip:
+ if prefs.use_glitch_panel:
+ return strip.type in ('MOVIE')
+ else:
+ return False
+
+ def draw_header(self, context):
+ layout = self.layout
+ layout.label(text="", icon="GAME")
+
+ def draw(self, context):
+
+ preferences = context.user_preferences
+ prefs = preferences.addons[__package__].preferences
+
+ layout = self.layout
+
+ layout.operator("sequencer.createavi", text="create avi (same codec)")
+ layout.operator("sequencer.createavi", text="create avi (mjpeg)").size=2
+
+ strip = functions.act_strip(context)
+
+ layout.prop(prefs,"all_keyframes")
+ layout.prop(prefs,"load_glitch")
+
+ layout.operator("sequencer.createdatamosh")
+
+
+
+
+
+
+
+
diff --git a/kinoraw_tools/eco.py b/kinoraw_tools/eco.py
new file mode 100644
index 00000000..01d13f71
--- /dev/null
+++ b/kinoraw_tools/eco.py
@@ -0,0 +1,140 @@
+#----------------------------------------------------------
+# File sequencer_slide_strip.py
+#----------------------------------------------------------
+
+# ##### 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
+from bpy.props import IntProperty, BoolProperty
+
+from . import functions
+
+
+class EcoPanel(bpy.types.Panel):
+ bl_label = "Eco Tool"
+ bl_idname = "OBJECT_PT_EcoTool"
+ bl_space_type = "SEQUENCE_EDITOR"
+ bl_region_type = "UI"
+
+ @staticmethod
+ def has_sequencer(context):
+ return (context.space_data.view_type\
+ in {'SEQUENCER', 'SEQUENCER_PREVIEW'})
+
+ @classmethod
+ def poll(self, context):
+ if context.space_data.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}:
+ strip = functions.act_strip(context)
+ scn = context.scene
+ preferences = context.user_preferences
+ prefs = preferences.addons[__package__].preferences
+ if scn and scn.sequence_editor and scn.sequence_editor.active_strip:
+ if prefs.use_eco_tools:
+ return strip.type in ('META')
+ else:
+ return False
+
+ def draw_header(self, context):
+ layout = self.layout
+ layout.label(text="", icon="FORCE_HARMONIC")
+
+ def draw(self, context):
+ scn = bpy.context.scene
+ strip = functions.act_strip(context)
+ seq_type = strip.type
+
+ preferences = context.user_preferences
+ prefs = preferences.addons[__package__].preferences
+
+ if seq_type in ( 'MOVIE', 'IMAGE', 'META', 'MOVIECLIP', 'SCENE') :
+ active_strip = functions.act_strip(context)
+ layout = self.layout
+ row=layout.row()
+ row.prop(prefs, 'eco_value', text="Ecos")
+ row=layout.row()
+ row.prop(prefs, 'eco_offset', text="Offset")
+ row=layout.row()
+ row.prop(prefs, 'eco_use_add_blend_mode', text="use_add_blend_mode")
+ row=layout.row()
+ row.operator('sequencer.eco')
+
+
+
+
+class OBJECT_OT_EcoOperator(bpy.types.Operator):
+
+ bl_idname = "sequencer.eco"
+ bl_label = "Eco operator"
+ bl_description = 'generate an echo effect by duplicating the selected strip'
+ bl_options = {'REGISTER', 'UNDO'}
+
+ @staticmethod
+ def has_sequencer(context):
+ return (context.space_data.view_type\
+ in {'SEQUENCER', 'SEQUENCER_PREVIEW'})
+
+ @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 ('META')
+ else:
+ return False
+
+ def execute(self, context):
+
+ active_strip=functions.act_strip(context)
+
+ preferences = context.user_preferences
+ prefs = preferences.addons[__package__].preferences
+
+ scn = bpy.context.scene
+ sel_strips = bpy.context.selected_sequences
+ cur_camera = scn.camera
+ eco = prefs.eco_value
+ offset = prefs.eco_offset
+
+ active_strip.blend_type = 'REPLACE'
+ active_strip.blend_alpha = 1
+ for i in range(eco):
+ bpy.ops.sequencer.duplicate(mode='TRANSLATION')
+ bpy.ops.transform.seq_slide(value=(offset, 1), snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0), snap_align=False, snap_normal=(0, 0, 0), release_confirm=False)
+
+ active_strip = functions.act_strip(context)
+
+ if prefs.eco_use_add_blend_mode:
+ active_strip.blend_type = 'ADD'
+ active_strip.blend_alpha = 1-1/eco
+ else:
+ active_strip.blend_type = 'ALPHA_OVER'
+ active_strip.blend_alpha = 1/eco
+
+
+ bpy.ops.sequencer.select_all(action='TOGGLE')
+ bpy.ops.sequencer.select_all(action='TOGGLE')
+ bpy.ops.sequencer.meta_make()
+
+
+ return {'FINISHED'}
+
+
+
diff --git a/kinoraw_tools/exiftool.py b/kinoraw_tools/exiftool.py
new file mode 100644
index 00000000..f42e3f34
--- /dev/null
+++ b/kinoraw_tools/exiftool.py
@@ -0,0 +1,330 @@
+# -*- coding: utf-8 -*-
+# PyExifTool <http://github.com/smarnach/pyexiftool>
+# Copyright 2012 Sven Marnach
+
+# This file is part of PyExifTool.
+#
+# PyExifTool 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 3 of the License, or
+# (at your option) any later version.
+#
+# PyExifTool 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 PyExifTool. If not, see <http://www.gnu.org/licenses/>.
+
+"""
+PyExifTool is a Python library to communicate with an instance of Phil
+Harvey's excellent ExifTool_ command-line application. The library
+provides the class :py:class:`ExifTool` that runs the command-line
+tool in batch mode and features methods to send commands to that
+program, including methods to extract meta-information from one or
+more image files. Since ``exiftool`` is run in batch mode, only a
+single instance needs to be launched and can be reused for many
+queries. This is much more efficient than launching a separate
+process for every single query.
+
+.. _ExifTool: http://www.sno.phy.queensu.ca/~phil/exiftool/
+
+The source code can be checked out from the github repository with
+
+::
+
+ git clone git://github.com/smarnach/pyexiftool.git
+
+Alternatively, you can download a tarball_. There haven't been any
+releases yet.
+
+.. _tarball: https://github.com/smarnach/pyexiftool/tarball/master
+
+PyExifTool is licenced under GNU GPL version 3 or later.
+
+Example usage::
+
+ import exiftool
+
+ files = ["a.jpg", "b.png", "c.tif"]
+ with exiftool.ExifTool() as et:
+ metadata = et.get_metadata_batch(files)
+ for d in metadata:
+ print("{:20.20} {:20.20}".format(d["SourceFile"],
+ d["EXIF:DateTimeOriginal"]))
+"""
+
+from __future__ import unicode_literals
+
+import sys
+import subprocess
+import os
+import json
+import warnings
+import codecs
+
+try: # Py3k compatibility
+ basestring
+except NameError:
+ basestring = (bytes, str)
+
+executable = "exiftool"
+"""The name of the executable to run.
+
+If the executable is not located in one of the paths listed in the
+``PATH`` environment variable, the full path should be given here.
+"""
+
+# Sentinel indicating the end of the output of a sequence of commands.
+# The standard value should be fine.
+sentinel = b"{ready}"
+
+# The block size when reading from exiftool. The standard value
+# should be fine, though other values might give better performance in
+# some cases.
+block_size = 4096
+
+# This code has been adapted from Lib/os.py in the Python source tree
+# (sha1 265e36e277f3)
+
+
+def _fscodec():
+ encoding = sys.getfilesystemencoding()
+ errors = "strict"
+ if encoding != "mbcs":
+ try:
+ codecs.lookup_error("surrogateescape")
+ except LookupError:
+ pass
+ else:
+ errors = "surrogateescape"
+
+ def fsencode(filename):
+ """
+ Encode filename to the filesystem encoding with 'surrogateescape' error
+ handler, return bytes unchanged. On Windows, use 'strict' error handler
+ if the file system encoding is 'mbcs' (which is the default encoding).
+ """
+ if isinstance(filename, bytes):
+ return filename
+ else:
+ return filename.encode(encoding, errors)
+
+ return fsencode
+
+fsencode = _fscodec()
+del _fscodec
+
+
+class ExifTool(object):
+ """Run the `exiftool` command-line tool and communicate to it.
+
+ You can pass the file name of the ``exiftool`` executable as an
+ argument to the constructor. The default value ``exiftool`` will
+ only work if the executable is in your ``PATH``.
+
+ Most methods of this class are only available after calling
+ :py:meth:`start()`, which will actually launch the subprocess. To
+ avoid leaving the subprocess running, make sure to call
+ :py:meth:`terminate()` method when finished using the instance.
+ This method will also be implicitly called when the instance is
+ garbage collected, but there are circumstance when this won't ever
+ happen, so you should not rely on the implicit process
+ termination. Subprocesses won't be automatically terminated if
+ the parent process exits, so a leaked subprocess will stay around
+ until manually killed.
+
+ A convenient way to make sure that the subprocess is terminated is
+ to use the :py:class:`ExifTool` instance as a context manager::
+
+ with ExifTool() as et:
+ ...
+
+ .. warning:: Note that there is no error handling. Nonsensical
+ options will be silently ignored by exiftool, so there's not
+ much that can be done in that regard. You should avoid passing
+ non-existent files to any of the methods, since this will lead
+ to undefied behaviour.
+
+ .. py:attribute:: running
+
+ A Boolean value indicating whether this instance is currently
+ associated with a running subprocess.
+ """
+
+ def __init__(self, executable_=None):
+ if executable_ is None:
+ self.executable = executable
+ else:
+ self.executable = executable_
+ self.running = False
+
+ def start(self):
+ """Start an ``exiftool`` process in batch mode for this instance.
+
+ This method will issue a ``UserWarning`` if the subprocess is
+ already running. The process is started with the ``-G`` and
+ ``-n`` as common arguments, which are automatically included
+ in every command you run with :py:meth:`execute()`.
+ """
+ if self.running:
+ warnings.warn("ExifTool already running; doing nothing.")
+ return
+ with open(os.devnull, "w") as devnull:
+ self._process = subprocess.Popen(
+ [self.executable, "-stay_open", "True", "-@", "-",
+ "-common_args", "-G", "-u", "-a", "-n"],
+ stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+ stderr=devnull)
+ self.running = True
+
+ def terminate(self):
+ """Terminate the ``exiftool`` process of this instance.
+
+ If the subprocess isn't running, this method will do nothing.
+ """
+ if not self.running:
+ return
+ self._process.stdin.write(b"-stay_open\nFalse\n")
+ self._process.stdin.flush()
+ self._process.communicate()
+ del self._process
+ self.running = False
+
+ def __enter__(self):
+ self.start()
+ return self
+
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ self.terminate()
+
+ def __del__(self):
+ self.terminate()
+
+ def execute(self, *params):
+ """Execute the given batch of parameters with ``exiftool``.
+
+ This method accepts any number of parameters and sends them to
+ the attached ``exiftool`` process. The process must be
+ running, otherwise ``ValueError`` is raised. The final
+ ``-execute`` necessary to actually run the batch is appended
+ automatically; see the documentation of :py:meth:`start()` for
+ the common options. The ``exiftool`` output is read up to the
+ end-of-output sentinel and returned as a raw ``bytes`` object,
+ excluding the sentinel.
+
+ The parameters must also be raw ``bytes``, in whatever
+ encoding exiftool accepts. For filenames, this should be the
+ system's filesystem encoding.
+
+ .. note:: This is considered a low-level method, and should
+ rarely be needed by application developers.
+ """
+ if not self.running:
+ raise ValueError("ExifTool instance not running.")
+ self._process.stdin.write(b"\n".join(params + (b"-execute\n",)))
+ self._process.stdin.flush()
+ output = b""
+ fd = self._process.stdout.fileno()
+ while not output[-32:].strip().endswith(sentinel):
+ output += os.read(fd, block_size)
+ return output.strip()[:-len(sentinel)]
+
+ def execute_json(self, *params):
+ """Execute the given batch of parameters and parse the JSON output.
+
+ This method is similar to :py:meth:`execute()`. It
+ automatically adds the parameter ``-j`` to request JSON output
+ from ``exiftool`` and parses the output. The return value is
+ a list of dictionaries, mapping tag names to the corresponding
+ values. All keys are Unicode strings with the tag names
+ including the ExifTool group name in the format <group>:<tag>.
+ The values can have multiple types. All strings occurring as
+ values will be Unicode strings. Each dictionary contains the
+ name of the file it corresponds to in the key ``"SourceFile"``.
+
+ The parameters to this function must be either raw strings
+ (type ``str`` in Python 2.x, type ``bytes`` in Python 3.x) or
+ Unicode strings (type ``unicode`` in Python 2.x, type ``str``
+ in Python 3.x). Unicode strings will be encoded using
+ system's filesystem encoding. This behaviour means you can
+ pass in filenames according to the convention of the
+ respective Python version – as raw strings in Python 2.x and
+ as Unicode strings in Python 3.x.
+ """
+ params = map(fsencode, params)
+ return json.loads(self.execute(b"-j", *params).decode("utf-8"))
+
+ def get_metadata_batch(self, filenames):
+ """Return all meta-data for the given files.
+
+ The return value will have the format described in the
+ documentation of :py:meth:`execute_json()`.
+ """
+ return self.execute_json(*filenames)
+
+ def get_metadata(self, filename):
+ """Return meta-data for a single file.
+
+ The returned dictionary has the format described in the
+ documentation of :py:meth:`execute_json()`.
+ """
+ return self.execute_json(filename)[0]
+
+ def get_tags_batch(self, tags, filenames):
+ """Return only specified tags for the given files.
+
+ The first argument is an iterable of tags. The tag names may
+ include group names, as usual in the format <group>:<tag>.
+
+ The second argument is an iterable of file names.
+
+ The format of the return value is the same as for
+ :py:meth:`execute_json()`.
+ """
+ # Explicitly ruling out strings here because passing in a
+ # string would lead to strange and hard-to-find errors
+ if isinstance(tags, basestring):
+ raise TypeError("The argument 'tags' must be "
+ "an iterable of strings")
+ if isinstance(filenames, basestring):
+ raise TypeError("The argument 'filenames' must be "
+ "an iterable of strings")
+ params = ["-" + t for t in tags]
+ params.extend(filenames)
+ return self.execute_json(*params)
+
+ def get_tags(self, tags, filename):
+ """Return only specified tags for a single file.
+
+ The returned dictionary has the format described in the
+ documentation of :py:meth:`execute_json()`.
+ """
+ return self.get_tags_batch(tags, [filename])[0]
+
+ def get_tag_batch(self, tag, filenames):
+ """Extract a single tag from the given files.
+
+ The first argument is a single tag name, as usual in the
+ format <group>:<tag>.
+
+ The second argument is an iterable of file names.
+
+ The return value is a list of tag values or ``None`` for
+ non-existent tags, in the same order as ``filenames``.
+ """
+ data = self.get_tags_batch([tag], filenames)
+ result = []
+ for d in data:
+ d.pop("SourceFile")
+ result.append(next(iter(d.values()), None))
+ return result
+
+ def get_tag(self, tag, filename):
+ """Extract a single tag from a single file.
+
+ The return value is the value of the specified tag, or
+ ``None`` if this tag was not found in the file.
+ """
+ return self.get_tag_batch(tag, [filename])[0]
diff --git a/kinoraw_tools/functions.py b/kinoraw_tools/functions.py
new file mode 100644
index 00000000..cdbac3de
--- /dev/null
+++ b/kinoraw_tools/functions.py
@@ -0,0 +1,439 @@
+# ##### 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 os.path, operator, subprocess, random
+
+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
+
+imb_ext_image = [
+ # IMG
+ ".png", ".tga", ".bmp", ".jpg", ".jpeg", ".sgi", ".rgb",
+ ".rgba", ".tif", ".tiff", ".tx", ".jp2", ".hdr", ".dds",
+ ".dpx", ".cin", ".exr", ".rw2",
+ # IMG QT
+ ".gif", ".psd", ".pct", ".pict", ".pntg", ".qtif"]
+
+imb_ext_audio = [
+ ".wav", ".ogg", ".oga", ".mp3", ".mp2", ".ac3", ".aac",
+ ".flac", ".wma", ".eac3", ".aif", ".aiff", ".m4a"]
+
+imb_ext_movie = [
+ ".avi", ".flc", ".mov", ".movie", ".mp4", ".m4v", ".m2v",
+ ".m2t", ".m2ts", ".mts", ".mv", ".avs", ".wmv", ".ogv", ".ogg",
+ ".dv", ".mpeg", ".mpg", ".mpg2", ".vob", ".mkv", ".flv",
+ ".divx", ".xvid", ".mxf"]
+
+movieextdict = [("1", ".avi", ""),
+ ("2", ".flc", ""), ("3", ".mov", ""),
+ ("4", ".movie", ""), ("5", ".mp4", ""),
+ ("6", ".m4v", ""), ("7", ".m2v", ""),
+ ("8", ".m2t", ""), ("9", ".m2ts", ""),
+ ("10", ".mts", ""), ("11", ".mv", ""),
+ ("12", ".avs", ""), ("13", ".wmv", ""),
+ ("14", ".ogv", ""), ("15", ".dv", ""),
+ ("16", ".mpeg", ""), ("17", ".mpg", ""),
+ ("18", ".mpg2", ""), ("19", ".vob", ""),
+ ("20", ".mkv", ""), ("21", ".flv", ""),
+ ("22", ".divx", ""), ("23", ".xvid", ""),
+ ("24", ".mxf", "")]
+
+# Functions
+
+
+
+def initSceneProperties(context):
+ # initSceneProperties is ONLY for varaibles that should
+ # be keeped with the blend file. Any other addon preferences
+ # should go to the addon preferences operator in __init__
+ try:
+ if context.scene.kr_scn_init == True:
+ return False
+ except AttributeError:
+ pass
+
+ scn = context.scene
+
+ # JUMP TO CUT
+ bpy.types.Scene.kr_auto_markers = BoolProperty(
+ name='kr_auto_markers',
+ description='activate auto markers',
+ default=False)
+ scn.kr_auto_markers = False
+
+ bpy.types.Scene.kr_in_marker = IntProperty(
+ name='in',
+ description='in frame position',
+ min=-30000, max=30000,
+ default=1)
+ scn.kr_in_marker = 1
+
+ bpy.types.Scene.kr_out_marker = IntProperty(
+ name='out',
+ description='out frame position',
+ min=scn.kr_in_marker, max=30000,
+ default=75)
+ scn.kr_out_marker = 75
+
+ # SEQUENCER EXTRA ACTIONS
+ bpy.types.Scene.kr_default_fade_duration = IntProperty(
+ name='Duration',
+ description='Number of frames to fade',
+ min=1, max=250,
+ default=scn.render.fps)
+ scn.kr_default_fade_duration = scn.render.fps
+
+ bpy.types.Scene.kr_default_fade_amount = FloatProperty(
+ name='Amount',
+ description='Maximum value of fade',
+ min=0.0,
+ max=100.0,
+ default=1.0)
+ scn.kr_default_fade_amount = 1.0
+
+ # RECURSIVE LOADER
+ bpy.types.Scene.kr_recursive = BoolProperty(
+ name='Recursive',
+ description='Load in recursive folders',
+ default=False)
+ scn.kr_recursive = False
+
+ bpy.types.Scene.kr_recursive_select_by_extension = BoolProperty(
+ name='Recursive ext',
+ description='Load only clips with selected extension',
+ default=False)
+ scn.kr_recursive_select_by_extension = False
+
+ bpy.types.Scene.kr_default_ext = EnumProperty(
+ items=movieextdict,
+ name="ext enum",
+ default="3")
+ scn.kr_default_ext = "3"
+
+ bpy.types.Scene.kr_scn_init = BoolProperty(
+ name='Init',
+ default=False)
+ scn.kr_scn_init = True
+
+ return True
+
+def get_selected_strips(context):
+ "return a list of selected strips"
+ strips=[]
+ for i in context.scene.sequence_editor.sequences_all:
+ if i.select == True:
+ strips.append(i)
+ return strips
+
+def create_folder(path):
+ if not os.path.isdir(bpy.path.abspath(path)):
+ folder = bpy.path.abspath(path)
+ command = "mkdir "+folder
+ subprocess.call(command,shell=True)
+
+def add_marker(context, text, frame):
+ scn = context.scene
+ markers = scn.timeline_markers
+ mark = markers.new(name=text)
+ mark.frame = frame
+
+def act_strip(context):
+ try:
+ return context.scene.sequence_editor.active_strip
+ except AttributeError:
+ return None
+
+def detect_strip_type(filepath):
+ extension = os.path.splitext(filepath)[1]
+ extension = extension.lower()
+ if extension in imb_ext_image:
+ type = 'IMAGE'
+ elif extension in imb_ext_movie:
+ type = 'MOVIE'
+ elif extension in imb_ext_audio:
+ type = 'SOUND'
+ else:
+ type = None
+
+ return type
+
+# recursive load functions
+
+def getpathfrombrowser(context):
+ '''
+ returns path from filebrowser
+ '''
+ 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:
+ #print("no browser")
+ self.report({'ERROR_INVALID_INPUT'}, 'No visible File Browser')
+ return {'CANCELLED'}
+ path = params.directory
+ return path
+
+def getfilepathfrombrowser(context):
+ '''
+ returns path and file from filebrowser
+ '''
+ 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:
+ #print("no browser")
+ #self.report({'ERROR_INVALID_INPUT'}, 'No visible File Browser')
+ return {'CANCELLED'}
+
+ if params.filename == '':
+ #print("no file selected")
+ #self.report({'ERROR_INVALID_INPUT'}, 'No file selected')
+ return {'CANCELLED'}
+ path = params.directory
+ filename = params.filename
+ return path, filename
+
+def setpathinbrowser(context, path, file):
+ '''
+ set path and file in the filebrowser
+ '''
+ 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:
+ #print("no browser")
+ self.report({'ERROR_INVALID_INPUT'}, 'No visible File Browser')
+ return {'CANCELLED'}
+
+ params.directory = path
+ params.filename = file
+ return path, params
+
+def sortlist(filelist):
+ '''
+ given a list of tuplas (path, filename) returns a list sorted by filename
+ '''
+ filelist_sorted = sorted(filelist, key=operator.itemgetter(1))
+ return filelist_sorted
+
+def onefolder(context, recursive_select_by_extension, ext):
+ '''
+ returns a list of MOVIE type files from folder selected in file browser
+ '''
+ filelist = []
+ path, filename = getfilepathfrombrowser(context)
+
+ for i in movieextdict:
+ if i[0] == ext:
+ extension = i[1].rpartition(".")[2]
+ break
+
+ scn = context.scene
+
+ if detect_strip_type(path + filename) == 'MOVIE':
+ if recursive_select_by_extension == True:
+ #filtering by extension...
+ for file in os.listdir(path):
+ if file.rpartition(".")[2].lower() == extension:
+ filelist.append((path, file))
+ else:
+ #looking for all known extensions
+ for file in os.listdir(path):
+ for i in movieextdict:
+ if file.rpartition(".")[2].lower() == i[1].rpartition(".")[2]:
+ filelist.append((path, file))
+ return (filelist)
+
+def recursive(context, recursive_select_by_extension, ext):
+ '''
+ returns a list of MOVIE type files recursively from file browser
+ '''
+ filelist = []
+ path = getpathfrombrowser(context)
+
+ for i in movieextdict:
+ if i[0] == ext:
+ extension = i[1].rpartition(".")[2]
+ break
+
+ scn = context.scene
+ for root, dirs, files in os.walk(path):
+ for file in files:
+ if recursive_select_by_extension == True:
+ #filtering by extension...
+ if file.rpartition(".")[2].lower() == extension:
+ filelist.append((root, file))
+ else:
+ #looking for all known extensions
+ for i in movieextdict:
+ if file.rpartition(".")[2].lower() == i[1].rpartition(".")[2]:
+ filelist.append((root, file))
+ return filelist
+
+# jump to cut functions
+
+def triminout(strip, sin, sout):
+
+ """trim the strip to in and out, and returns
+ true if the strip is outside given in and out"""
+
+ start = strip.frame_start + strip.frame_offset_start - strip.frame_still_start
+ end = start + strip.frame_final_duration
+
+ remove = False
+ if end < sin: remove = True
+ if start > sout: remove = True
+
+ if end > sin:
+ if start < sin:
+ strip.select_right_handle = False
+ strip.select_left_handle = True
+ bpy.ops.sequencer.snap(frame=sin)
+ strip.select_left_handle = False
+ if start < sout:
+ if end > sout:
+ strip.select_left_handle = False
+ strip.select_right_handle = True
+ bpy.ops.sequencer.snap(frame=sout)
+ strip.select_right_handle = False
+
+ return remove
+
+
+#------------ random editor functions.
+
+def randompartition(lst,n,rand):
+ division = len(lst) / float(n)
+ lista = []
+ for i in range(n): lista.append(division)
+ var=0
+ for i in range(n-1):
+ lista[i]+= random.randint(-int(rand*division),int(rand*division))
+ var+=lista[i]
+ if lista[n-1] != len(lst)-var:
+ lista[n-1] = len(lst)-var
+ random.shuffle(lista)
+ division = len(lst) / float(n)
+ count = 0
+ newlist=[]
+ for i in range(n):
+ #print(lst[count : int(lista[i]-1)+count])
+ newlist.append([lst[count : int(lista[i]-1)+count]])
+ count += int(lista[i])
+ return newlist
+
+def randomframe(strip):
+ #random frame between a and b
+ a = strip.frame_start
+ b = strip.frame_final_duration
+ rand = a+int(random.random()*b)
+ #print(a, a+b, rand)
+ return rand
+
+# ???
+def get_matching_markers(scene, name=None):
+ '''return a list of markers with same name
+ from the scene, or all markers if name is None'''
+ selected_markers=[]
+ markers = scene.timeline_markers
+ for mark in markers:
+ #print(mark.name, name)
+ if mark.name == name or name==None:
+ selected_markers.append(mark.frame)
+ return selected_markers
+
+# ???
+def generate_subsets_list(number_of_subsets):
+ #generate marker subsets list
+ subset_list = []
+ subset_names = ['A','B','C','D','E','F']
+ for subset in range(number_of_subsets):
+ subset_list.append(subset_names[subset])
+ return subset_list
+
+
+def get_marker_dict(scene, number_of_subsets):
+ '''return a dict where:
+ keys = subset names
+ values = list of markers'''
+ #get markers from scene
+ markers = scene.timeline_markers
+ subset_list = generate_subsets_list(number_of_subsets)
+ #generate dict with a list for each subset
+ marker_dict = {}
+ for subset in subset_list:
+ list=get_matching_markers(scene, subset)
+ marker_dict[subset] = list
+ return marker_dict
+
+
+def get_cut_dict(scene, number_of_subsets):
+ '''return a dict where:
+ keys = markers in the scene + start and end
+ values = duration in frames from key marker to next marker'''
+ #generate cut_list
+
+ list=get_matching_markers(scene)
+ list.append(scene.frame_start)
+ list.append(scene.frame_end)
+ list.sort()
+ #print("lista:",len(list),list)
+ cut_dict ={}
+ for i, j in enumerate(list):
+ try:
+ #print(j,list[i+1]-1-j)
+ cut_dict[j] = list[i+1]-j
+ except IndexError:
+ continue
+ return cut_dict
+
+
+def random_edit_from_random_subset(cut_dict, sources_dict):
+ '''return a random edit from random subsets'''
+ #generate subset list (strings)
+ subset_list = generate_subsets_list(number_of_subsets)
+ # copy sources_dict
+ random_edit = []
+ for cut in sorted(cut_dict.keys()):
+ #escoge un subset al azar:
+ rand = random.randrange(number_of_subsets)
+ subset = subset_list[rand]
+ #check if source subset is empty
+ print(len(sources_dict[subset]),subset)
+ if len(sources_dict[subset]) == 0:
+ sources_dict[subset] = get_matching_markers(selected_scene, subset)
+ print("repeating "+ subset + " clips")
+ marker = sources_dict[subset].pop()
+ random_edit.append((cut, cut_dict[cut], subset, marker))
+ return random_edit
diff --git a/kinoraw_tools/jumptocut.py b/kinoraw_tools/jumptocut.py
new file mode 100644
index 00000000..0ccd05e0
--- /dev/null
+++ b/kinoraw_tools/jumptocut.py
@@ -0,0 +1,645 @@
+# ##### 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
+
+from . import functions
+from bpy.props import IntProperty, BoolProperty
+
+from bpy.app.handlers import persistent
+
+
+
+class OBJECT_OT_Setinout(bpy.types.Operator):
+ bl_label = "set IN and OUT to selected"
+ bl_idname = "sequencerextra.setinout"
+ bl_description = "set IN and OUT markers to the selected strips limits"
+
+ bl_options = {'REGISTER', 'UNDO'}
+
+ @classmethod
+ def poll(self, context):
+ scn = context.scene
+ if scn and scn.sequence_editor:
+ return scn.sequence_editor.active_strip
+ else:
+ return False
+
+ def execute(self, context):
+ functions.initSceneProperties(context)
+
+ scn = context.scene
+ markers =scn.timeline_markers
+ seq = scn.sequence_editor
+
+ meta_level = len(seq.meta_stack)
+ if meta_level > 0:
+ seq = seq.meta_stack[meta_level - 1]
+
+ #search for timeline limits
+ tl_start = 300000
+ tl_end = -300000
+ for i in context.selected_editable_sequences:
+ if i.select == True:
+ start = i.frame_start + i.frame_offset_start - i.frame_still_start
+ end = start + i.frame_final_duration
+ if start < tl_start:
+ tl_start = start
+ if end > tl_end:
+ tl_end = end
+ #print(tl_start,tl_end)
+
+
+ if scn.kr_auto_markers:
+ scn.kr_in_marker = tl_start
+ scn.kr_out_marker = tl_end
+ else:
+ scn.kr_in_marker = tl_start
+ scn.kr_out_marker = tl_end
+
+ if "IN" in markers:
+ mark=markers["IN"]
+ mark.frame=scn.kr_in_marker
+ else:
+ mark=markers.new(name="IN")
+ mark.frame=scn.kr_in_marker
+
+ if "OUT" in markers:
+ mark=markers["OUT"]
+ mark.frame=scn.kr_out_marker
+ else:
+ mark=markers.new(name="OUT")
+ mark.frame=scn.kr_in_marker
+
+ return {'FINISHED'}
+
+
+class OBJECT_OT_Triminout(bpy.types.Operator):
+ bl_label = "Trim to in & out"
+ bl_idname = "sequencerextra.triminout"
+ bl_description = "trim the selected strip to IN and OUT markers (if exists)"
+
+ bl_options = {'REGISTER', 'UNDO'}
+
+ @classmethod
+ def poll(self, context):
+ scn = context.scene
+ if scn and scn.sequence_editor:
+ if scn.sequence_editor.active_strip:
+ markers=scn.timeline_markers
+ if "IN" and "OUT" in markers:
+ return True
+ else:
+ return False
+
+
+ def execute(self, context):
+
+ scene=context.scene
+ seq=scene.sequence_editor
+
+ meta_level = len(seq.meta_stack)
+ if meta_level > 0:
+ seq = seq.meta_stack[meta_level - 1]
+
+ markers=scene.timeline_markers
+ sin=markers["IN"].frame
+ sout=markers["OUT"].frame
+ strips = context.selected_editable_sequences
+ #(triminout function only works fine
+ # with one strip selected at a time)
+ for strip in strips:
+ #deselect all other strips
+ for i in strips: i.select = False
+ #select current strip
+ strip.select = True
+ remove=functions.triminout(strip,sin,sout)
+ if remove == True:
+ bpy.ops.sequencer.delete()
+ #select all strips again
+ for strip in strips:
+ try:
+ strip.select=True
+ except ReferenceError:
+ pass
+ bpy.ops.sequencer.reload()
+ return {'FINISHED'}
+
+# SOURCE IN OUT
+
+class OBJECT_OT_Sourcein(bpy.types.Operator): #Operator source in
+ bl_label = "Source IN"
+ bl_idname = "sequencerextra.sourcein"
+ bl_description = "add or move a marker named IN"
+
+ bl_options = {'REGISTER', 'UNDO'}
+
+ @classmethod
+ def poll(self, context):
+ strip = functions.act_strip(context)
+ scn = context.scene
+ if scn:
+ return scn.sequence_editor
+ else:
+ return False
+
+ def execute(self, context):
+ functions.initSceneProperties(context)
+ scn=context.scene
+ seq = scn.sequence_editor
+ markers=scn.timeline_markers
+
+ if scn.kr_auto_markers:
+ scn.kr_in_marker = scn.frame_current
+
+ else:
+ scn.kr_in_marker = scn.frame_current
+ if "IN" in markers:
+ mark=markers["IN"]
+ mark.frame=scn.kr_in_marker
+ else:
+ mark=markers.new(name="IN")
+ mark.frame=scn.kr_in_marker
+
+ #limit OUT marker position with IN marker
+ if scn.kr_in_marker > scn.kr_out_marker:
+ scn.kr_out_marker = scn.kr_in_marker
+
+ if "OUT" in markers:
+ mark=markers["OUT"]
+ mark.frame=scn.kr_out_marker
+
+
+ for m in markers:
+ m.select = False
+ if m.name in {"IN", "OUT"}:
+ m.select = True
+ bpy.ops.sequencer.reload()
+
+ return {'FINISHED'}
+
+class OBJECT_OT_Sourceout(bpy.types.Operator): #Operator source out
+ bl_label = "Source OUT"
+ bl_idname = "sequencerextra.sourceout"
+ bl_description = "add or move a marker named OUT"
+
+ bl_options = {'REGISTER', 'UNDO'}
+
+ @classmethod
+ def poll(self, context):
+ strip = functions.act_strip(context)
+ scn = context.scene
+ if scn:
+ return scn.sequence_editor
+ else:
+ return False
+
+ def execute(self, context):
+ scn=context.scene
+ functions.initSceneProperties(context)
+ seq = scn.sequence_editor
+ markers=scn.timeline_markers
+
+ if scn.kr_auto_markers:
+ scn.kr_out_marker = scn.frame_current
+
+ else:
+ scn.kr_out_marker = scn.frame_current
+
+ #limit OUT marker position with IN marker
+ if scn.kr_out_marker < scn.kr_in_marker:
+ scn.kr_out_marker = scn.kr_in_marker
+
+ if "OUT" in markers:
+ mark=markers["OUT"]
+ mark.frame=scn.kr_out_marker
+ else:
+ mark=markers.new(name="OUT")
+ mark.frame=scn.kr_out_marker
+
+ for m in markers:
+ m.select = False
+ if m.name in {"IN", "OUT"}:
+ m.select = True
+ bpy.ops.sequencer.reload()
+ return {'FINISHED'}
+
+class OBJECT_OT_Setstartend(bpy.types.Operator): #Operator set start & end
+ bl_label = "set Start & End"
+ bl_idname = "sequencerextra.setstartend"
+ bl_description = "set Start and End to IN and OUT marker values"
+
+ bl_options = {'REGISTER', 'UNDO'}
+
+ @classmethod
+ def poll(self, context):
+ scn = context.scene
+ markers=scn.timeline_markers
+ if "IN" and "OUT" in markers:
+ return True
+ else:
+ return False
+
+ def execute(self, context):
+ functions.initSceneProperties(context)
+ scn=context.scene
+ markers=scn.timeline_markers
+ sin=markers["IN"]
+ sout=markers["OUT"]
+ scn.frame_start = sin.frame
+ scn.frame_end = sout.frame - 1
+ bpy.ops.sequencer.reload()
+ return {'FINISHED'}
+
+
+# COPY PASTE
+
+class OBJECT_OT_Metacopy(bpy.types.Operator): #Operator copy source in/out
+ bl_label = "Trim & Meta-Copy"
+ bl_idname = "sequencerextra.metacopy"
+ bl_description = "make meta from selected strips, trim it to in/out (if available) and copy it to clipboard"
+
+ bl_options = {'REGISTER', 'UNDO'}
+
+ def execute(self, context):
+ # rehacer
+ scene =bpy.context.scene
+ seq = scene.sequence_editor
+ markers =scene.timeline_markers
+ strip1 = seq.active_strip
+ if strip1 != None:
+ if "IN" and "OUT" in markers:
+ sin=markers["IN"].frame
+ sout=markers["OUT"].frame
+ bpy.ops.sequencer.meta_make()
+ strip2= seq.active_strip
+ functions.triminout(strip2,sin,sout)
+ bpy.ops.sequencer.copy()
+ bpy.ops.sequencer.meta_separate()
+ self.report({'INFO'}, "META2 has been trimed and copied")
+ else:
+ bpy.ops.sequencer.meta_make()
+ bpy.ops.sequencer.copy()
+ bpy.ops.sequencer.meta_separate()
+ self.report({'WARNING'}, "No In & Out!! META has been copied")
+ else:
+ self.report({'ERROR'}, "No strip selected")
+ return {'FINISHED'}
+
+class OBJECT_OT_Metapaste(bpy.types.Operator): #Operator paste source in/out
+ bl_label = "Paste in current Frame"
+ bl_idname = "sequencerextra.metapaste"
+ bl_description = "paste source from clipboard to current frame"
+
+ bl_options = {'REGISTER', 'UNDO'}
+
+ def execute(self, context):
+ # rehacer
+ scene=bpy.context.scene
+ bpy.ops.sequencer.paste()
+ bpy.ops.sequencer.snap(frame=scene.frame_current)
+ strips = context.selected_editable_sequences
+ context.scene.sequence_editor.active_strip=strips[0]
+ context.scene.update()
+ return {'FINISHED'}
+
+
+class OBJECT_OT_Unmetatrim(bpy.types.Operator): #Operator paste source in/out
+ bl_label = "Paste in current Frame"
+ bl_idname = "sequencerextra.meta_separate_trim"
+ bl_description = "unmeta and trim the content to meta duration"
+
+ bl_options = {'REGISTER', 'UNDO'}
+
+ @classmethod
+ def poll(self, context):
+ scn = context.scene
+ if scn and scn.sequence_editor:
+ if scn.sequence_editor.active_strip:
+ return scn.sequence_editor.active_strip.type == "META"
+ else:
+ return False
+
+ def execute(self, context):
+ scn=context.scene
+ seq=scn.sequence_editor
+ markers=scn.timeline_markers
+
+ # setting in and out around meta
+ # while keeping data to restore in and out positions
+ strip = seq.active_strip
+ sin = strip.frame_start + strip.frame_offset_start
+ sout = sin + strip.frame_final_duration
+
+ borrarin=False
+ borrarout=False
+ original_in = 0
+ original_out = 0
+
+ if "IN" in markers:
+ original_in = markers["IN"].frame
+ markers["IN"].frame = sin
+ else:
+ mark=markers.new(name="IN")
+ mark.frame=sin
+ borrarin=True
+
+ if "OUT" in markers:
+ original_out = markers["OUT"].frame
+ markers["OUT"].frame=sout
+ else:
+ mark= markers.new(name="OUT")
+ mark.frame=sout
+ borrarout=True
+
+
+ # here starts the operator...
+
+ #get all META from selected strips
+ metastrips=[]
+ for i in context.selected_editable_sequences:
+ if i.type == "META":
+ metastrips.append(i)
+
+ for meta in metastrips:
+ bpy.ops.sequencer.reload()
+
+ # deselect all strips
+ for i in context.selected_editable_sequences:
+ i.select = False
+
+ #make active current meta
+ meta.select = True
+ seq.active_strip = meta
+ bpy.ops.sequencer.reload()
+
+ #set in and out to meta
+ sin = meta.frame_start + meta.frame_offset_start
+ sout = sin + meta.frame_final_duration
+ #print("meta: ", sin, sout)
+
+ #grab meta content
+ newstrips = []
+ for i in meta.sequences:
+ newstrips.append(i)
+
+ #store meta channel
+ basechan = meta.channel
+ #look for upper and lower channels used by strips inside the meta
+ lowerchan = 32
+ upperchan = 0
+ for i in newstrips:
+ if i.channel < lowerchan: lowerchan = i.channel
+ if i.channel > upperchan: upperchan = i.channel
+ #calculate channel increment needed
+ deltachan = basechan - lowerchan
+ # reorder strips inside the meta
+ # before separate we need to store channel data
+ delta = upperchan - lowerchan + 1
+ for i in newstrips:
+ i.channel = i.channel + delta
+ chandict = {}
+ for i in newstrips:
+ i.channel = i.channel + deltachan - delta
+ chandict[i.name]=i.channel
+ #for i in chandict: print(i,chandict[i])
+
+ #go inside meta to trim strips
+ bpy.ops.sequencer.meta_toggle()
+
+ #update seq definition according to meta
+ meta_level = len(seq.meta_stack)
+ if meta_level > 0:
+ seq = seq.meta_stack[meta_level - 1]
+
+ #create a list to store clips outside selection
+ #that will be removed
+ rmlist=[]
+ #deselect all separated strips
+ for j in newstrips:
+ j.select = False
+ #print("newstrips: ",j.name, j.type)
+ #trim each strip separately
+ #first check special strips:
+ # (those who can move when any other does)
+ for i in newstrips:
+ if i.type in {"CROSS","SPEED","WIPE"}:
+ i.select = True
+ remove = functions.triminout(i,sin,sout)
+ if remove == True:
+ #print("checked: ",i.name, i.type)
+ rmlist.append(i)
+ i.select=False
+ # now for the rest of strips
+ for i in newstrips:
+ i.select = True
+ remove = functions.triminout(i,sin,sout)
+ if remove == True:
+ #print("checked: ",i.name, i.type)
+ rmlist.append(i)
+ i.select=False
+
+ # back outside the meta and separate it
+ bpy.ops.sequencer.meta_toggle()
+ bpy.ops.sequencer.meta_separate()
+
+ # reset seq definition
+ seq=scn.sequence_editor
+
+ #remove strips from outside the meta duration
+ for i in rmlist:
+ #print("removing: ",i.name, i.type)
+ for j in scn.sequence_editor.sequences_all: j.select = False
+ i.select = True
+ scn.sequence_editor.active_strip = i
+ bpy.ops.sequencer.delete()
+
+ #select all strips and set one of the strips as active
+ for i in newstrips:
+ if i not in rmlist:
+ i.select = True
+ scn.sequence_editor.active_strip = i
+
+ bpy.ops.sequencer.reload()
+
+ #restore original IN and OUT values
+ if borrarin:
+ markers.remove(markers['IN'])
+ else:
+ markers["IN"].frame = original_in
+ if borrarout:
+ markers.remove(markers['OUT'])
+ else:
+ markers["OUT"].frame = original_out
+ scn.update()
+
+
+
+ return {'FINISHED'}
+
+class OBJECT_OT_Extrasnap(bpy.types.Operator): #Operator paste source in/out
+ bl_label = "extrasnap"
+ bl_idname = "sequencerextra.extrasnap"
+ bl_description = "snap the right, center or left of the strip to current frame"
+
+ # align: 0 = left snap, 1 = center snap, 2= right snap
+ align = IntProperty(
+ name='align',
+ min=0, max=2,
+ default=1)
+
+ bl_options = {'REGISTER', 'UNDO'}
+
+ @classmethod
+ def poll(self, context):
+ strip = functions.act_strip(context)
+ scn = context.scene
+ if scn and scn.sequence_editor:
+ return scn.sequence_editor.active_strip
+ else:
+ return False
+
+ def execute(self, context):
+ scene=bpy.context.scene
+ bpy.ops.sequencer.snap(frame=scene.frame_current)
+
+ if self.align != 0:
+ strips = context.selected_editable_sequences
+ for strip in strips:
+ if self.align == 1: #center snap
+ strip.frame_start-=strip.frame_final_duration/2
+ else: #right snap
+ strip.frame_start-=strip.frame_final_duration
+
+ return {'FINISHED'}
+
+class OBJECT_OT_Extrahandles(bpy.types.Operator): #Operator paste source in/out
+ bl_label = "extrahandles"
+ bl_idname = "sequencerextra.extrahandles"
+ bl_description = "snap the right, center or left of the strip to current frame"
+
+ # side: 0 = left , 1 = both, 2= right
+ side = IntProperty(
+ name='side',
+ min=0, max=2,
+ default=1)
+
+ bl_options = {'REGISTER', 'UNDO'}
+
+ @classmethod
+ def poll(self, context):
+ strip = functions.act_strip(context)
+ scn = context.scene
+ if scn and scn.sequence_editor:
+ return scn.sequence_editor.active_strip
+ else:
+ return False
+
+ def execute(self, context):
+ scn=context.scene
+ strips = context.selected_editable_sequences
+
+ resetLeft = False
+ resetRight = False
+ changelistLeft = []
+ changelistRight = []
+ for strip in strips:
+ if self.side == 0 or self.side == 1:
+ if strip.select_left_handle:
+ resetLeft = True
+ changelistLeft.append(strip)
+ if self.side == 1 or self.side == 2:
+ if strip.select_right_handle:
+ resetRight = True
+ changelistRight.append(strip)
+ if len(changelistLeft) == len(strips):
+ resetLeft = False
+ if len(changelistRight) == len(strips):
+ resetRight = False
+ if ((len(changelistRight) != len(strips)) \
+ or (len(changelistRight) != len(strips))) \
+ and self.side == 1:
+ resetLeft = True
+ resetRight = True
+
+ for strip in strips:
+ if resetLeft:
+ strip.select_left_handle = False
+ if self.side == 0 or self.side == 1:
+ if strip.select_left_handle:
+ strip.select_left_handle = False
+ else:
+ strip.select_left_handle = True
+ if resetRight:
+ strip.select_right_handle = False
+ if self.side == 1 or self.side == 2:
+ if strip.select_right_handle:
+ strip.select_right_handle = False
+ else:
+ strip.select_right_handle = True
+
+ return {'FINISHED'}
+
+
+
+#-----------------------------------------------------------------------------------------------------
+
+
+
+@persistent
+def marker_handler(scn):
+ context=bpy.context
+ functions.initSceneProperties(context)
+ #preferences = context.user_preferences
+ #prefs = preferences.addons[__package__].preferences
+ if scn.kr_auto_markers:
+ #scn = context.scene
+
+ markers =scn.timeline_markers
+
+ if "IN" in markers:
+ mark=markers["IN"]
+ mark.frame=scn.kr_in_marker
+ else:
+ mark=markers.new(name="IN")
+ mark.frame=scn.kr_in_marker
+
+ if "OUT" in markers:
+ mark=markers["OUT"]
+ mark.frame=scn.kr_out_marker
+ else:
+ mark= markers.new(name="OUT")
+ mark.frame=scn.kr_out_marker
+
+ #limit OUT marker position with IN marker
+ if scn.kr_in_marker > scn.kr_out_marker:
+ scn.kr_out_marker = scn.kr_in_marker
+
+ return {'FINISHED'}
+ else:
+ return {'CANCELLED'}
+
+bpy.app.handlers.scene_update_post.append(marker_handler)
+
+
+
+
+
+
+
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'}
diff --git a/kinoraw_tools/proxy_tools.py b/kinoraw_tools/proxy_tools.py
new file mode 100644
index 00000000..099dda60
--- /dev/null
+++ b/kinoraw_tools/proxy_tools.py
@@ -0,0 +1,333 @@
+import bpy, os
+from bpy.props import IntProperty, StringProperty, BoolProperty
+import subprocess
+
+from . import functions
+
+
+proxy_qualities = [ ( "1", "25%", "" ), ( "2", "50%", "" ),
+ ( "3", "75%", "" ), ( "4", "100%", "" ),
+ ( "5", "none", "" )]
+
+
+# functions
+
+def setup_proxy(context, strip, size):
+ preferences = context.user_preferences
+ prefs = preferences.addons[__package__].preferences
+
+ # set up proxy settings
+ strip.use_proxy = True
+
+ if prefs.use_bi_custom_directory:
+ strip.use_proxy_custom_directory = True
+ filename = strip.filepath.rpartition("/")[2].rpartition(".")[0]
+ strip.proxy.directory = bpy.path.relpath(prefs.proxy_dir+filename)
+ else:
+ strip.use_proxy_custom_directory = False
+
+ if strip.use_proxy_custom_file == True:
+ strip.use_proxy_custom_file = False
+
+ strip.proxy.quality = prefs.quality
+ strip.proxy.timecode = prefs.timecode
+
+ if size == 5:
+ strip.use_proxy = False
+ strip.proxy.build_25 = False
+ strip.proxy.build_50 = False
+ strip.proxy.build_75 = False
+ strip.proxy.build_100 = False
+
+ else:
+ proxysuffix = proxy_qualities[size-1][1].split("%")[0]
+
+ if (proxysuffix == "25"):
+ strip.proxy.build_25 = True
+ if (proxysuffix == "50"):
+ strip.proxy.build_50 = True
+ if (proxysuffix == "75"):
+ strip.proxy.build_75 = True
+ if (proxysuffix == "100"):
+ strip.proxy.build_100 = True
+
+
+ return {"FINISHED"}
+
+
+def create_proxy(context, strip, size, res):
+ # calculate proxy resolution
+ div = 4/size
+ newres = (int(int(res[0])/div), int(int(res[1])/div))
+
+ preferences = context.user_preferences
+ proxy_dir = preferences.addons[__package__].preferences.proxy_dir
+ scripts = preferences.addons[__package__].preferences.proxy_scripts
+ ffmpeg_command = preferences.addons[__package__].preferences.ffmpeg_command
+
+ functions.create_folder(proxy_dir)
+
+ if scripts:
+ commands = []
+
+ # get filename
+ if strip.type == "MOVIE":
+ filename = bpy.path.abspath(strip.filepath)
+ proxysuffix = proxy_qualities[size-1][1].split("%")[0]
+ proxy_dir = bpy.path.abspath(proxy_dir)
+ newfilename = os.path.join(proxy_dir,filename.rpartition("/")[2])
+ fileoutput = newfilename.rpartition(".")[0]+"-"+proxysuffix+".avi"
+
+ #default value for ffmpeg_command = "fmpeg -i {} -vcodec mjpeg -qv 1 -s {}x{} -y {}"
+
+ command = ffmpeg_command.format(filename, newres[0], newres[1], fileoutput)
+ print(command)
+
+ if scripts:
+ commands.append(command)
+ else:
+ # check for existing file
+ if not os.path.isfile(fileoutput):
+ subprocess.call(command, shell=True)
+ else:
+ print("ya existe")
+
+ # set up proxy settings
+ strip.use_proxy = True
+ strip.use_proxy_custom_file = True
+ strip.proxy.filepath = bpy.path.relpath(fileoutput)
+ if (proxysuffix == "25"):
+ strip.proxy.build_25 = True
+ if (proxysuffix == "50"):
+ strip.proxy.build_50 = True
+ if (proxysuffix == "75"):
+ strip.proxy.build_75 = True
+ if (proxysuffix == "100"):
+ strip.proxy.build_100 = True
+
+ if scripts:
+ return commands
+ else:
+ return None
+
+
+def create_proxy_scripts(scripts_dir, commands, strip_name = None):
+
+ functions.create_folder(bpy.path.abspath(scripts_dir))
+
+ for i in commands:
+ #print(i)
+ filename = "{}/proxy_script_{}.sh".format(scripts_dir, strip_name)
+ text_file = open(bpy.path.abspath(filename), "w")
+ #print(filename)
+ text_file.write(i)
+ text_file.close()
+
+
+
+# classes
+
+class CreateProxyOperator(bpy.types.Operator):
+ """ Use ffmpeg to create a proxy from video and setup proxies \
+ for selected strip"""
+ bl_idname = "sequencer.create_proxy_operator"
+ bl_label = " Create proxy"
+
+ size = IntProperty(
+ name='proxysize',
+ default=1)
+ 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')
+ else:
+ return False
+
+ def execute(self, context):
+
+ preferences = context.user_preferences
+ proxy_dir = preferences.addons[__package__].preferences.proxy_dir
+ scripts = preferences.addons[__package__].preferences.proxy_scripts
+ proxy_scripts_path = preferences.addons[__package__].preferences.proxy_scripts_path
+ for strip in context.selected_editable_sequences:
+
+ # get resolution from active strip
+ bpy.ops.sequencerextra.read_exif()
+ sce = context.scene
+ try:
+ res = sce['metadata'][0]['Composite:ImageSize'].split("x")
+ except:
+ res=(sce.render.resolution_x, sce.render.resolution_y)
+ #print(res)
+
+ commands = create_proxy(context, strip, self.size, res)
+
+ if commands == None:
+ # Update scene
+ context.scene.update()
+ newstrip = context.scene.sequence_editor.active_strip
+
+ # deselect all other strips
+ for i in context.selected_editable_sequences:
+ if i.name != newstrip.name:
+ i.select=False
+
+ # Update scene
+ context.scene.update()
+ else:
+ create_proxy_scripts(proxy_scripts_path, commands, strip.name)
+
+
+ return {'FINISHED'}
+
+
+class CreateBIProxyOperator(bpy.types.Operator):
+ """ Use BI system to create a proxy"""
+ bl_idname = "sequencer.create_bi_proxy_operator"
+ bl_label = " Create proxy with blender internal"
+
+ size = IntProperty(
+ name='proxysize',
+ default=1)
+ 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')
+ else:
+ return False
+
+ def execute(self, context):
+
+ preferences = context.user_preferences
+ strips = functions.get_selected_strips(context)
+
+ for strip in strips:
+ #deselect all other strips
+ for i in strips: i.select = False
+ #select current strip
+ strip.select = True
+ if strip.type == "MOVIE":
+ setup_proxy(context, strip, self.size)
+ #select all strips again
+ for strip in strips:
+ try:
+ strip.select=True
+ except ReferenceError:
+ pass
+ bpy.ops.sequencer.reload()
+ return {'FINISHED'}
+
+
+class CreateProxyToolPanel(bpy.types.Panel):
+ """ """
+ bl_label = "Proxy Tools"
+ bl_idname = "OBJECT_PT_ProxyTool"
+ bl_space_type = 'SEQUENCE_EDITOR'
+ bl_region_type = 'UI'
+
+
+ @classmethod
+ def poll(self, context):
+ if context.space_data.view_type in {'SEQUENCER',
+ 'SEQUENCER_PREVIEW'}:
+ strip = functions.act_strip(context)
+ scn = context.scene
+ preferences = context.user_preferences
+ prefs = preferences.addons[__package__].preferences
+ if scn and scn.sequence_editor and scn.sequence_editor.active_strip:
+ if prefs.use_proxy_tools:
+ return strip.type in ('MOVIE')
+ else:
+ return False
+
+ def draw_header(self, context):
+ layout = self.layout
+ layout.label(text="", icon="AUTO")
+
+ def draw(self, context):
+
+ preferences = context.user_preferences
+ prefs = preferences.addons[__package__].preferences
+
+ layout = self.layout
+ layout.prop(prefs, "use_internal_proxy", text="use BI proxy builder")
+
+ strip = functions.act_strip(context)
+
+ if prefs.use_internal_proxy:
+ layout = self.layout
+ row = layout.row(align=True)
+ row.prop(prefs, "use_bi_custom_directory")
+
+ if prefs.use_bi_custom_directory:
+ row.prop(prefs, "proxy_dir", text="")
+ filename = strip.filepath.rpartition("/")[2].rpartition(".")[0]
+ layout.label("sample dir: //"+bpy.path.abspath(prefs.proxy_dir+filename))
+
+ layout = self.layout
+ col = layout.column()
+ col.label(text="Build JPEG quality")
+ col.prop(prefs, "quality")
+
+ if strip.type == 'MOVIE':
+ col = layout.column()
+ col.label(text="Use timecode index:")
+
+ col.prop(prefs, "timecode")
+
+ layout = self.layout
+ layout.label("setup and create BI proxy:")
+ row = layout.row(align=True)
+ for i in range(4):
+ proxysuffix = proxy_qualities[i][1]
+ row.operator("sequencer.create_bi_proxy_operator",
+ text=proxysuffix).size=i+1
+
+ layout = self.layout
+ layout.operator("sequencer.create_bi_proxy_operator",
+ text="Clear proxy sizes").size=5
+
+ else:
+
+ layout = self.layout
+ layout.prop(prefs, "proxy_dir", text="path for proxies")
+
+ layout = self.layout
+ layout.label("create and import proxy from clip:")
+ row = layout.row(align=True)
+
+ layout = self.layout
+ layout.prop(prefs, "ffmpeg_command", text="command")
+
+ layout.label("{} = filename, with, height, fileoutput")
+ label = prefs.ffmpeg_command.format("filename", "with", "height", "fileoutput")
+ layout.label(label)
+
+ for i in range(4):
+ proxysuffix = proxy_qualities[i][1]
+ row.operator("sequencer.create_proxy_operator",text=proxysuffix).size=i+1
+
+ layout = self.layout
+ layout.prop(prefs, "proxy_scripts")
+
+ if prefs.proxy_scripts:
+ layout = self.layout
+ layout.prop(prefs, "proxy_scripts_path", text="path for scripts")
+
+ layout = self.layout
+ box = layout.box()
+ box.prop(context.space_data, "proxy_render_size")
+ box.operator("sequencer.rebuild_proxy",
+ text="Rebuild Proxies and TC")
+
+
+
+
diff --git a/kinoraw_tools/random_editor.py b/kinoraw_tools/random_editor.py
new file mode 100644
index 00000000..5735bb3e
--- /dev/null
+++ b/kinoraw_tools/random_editor.py
@@ -0,0 +1,218 @@
+# ##### 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, os, random
+from bpy.props import IntProperty, BoolProperty, StringProperty
+
+from . import functions
+
+# CLASSES
+
+class RandomScratchOperator(bpy.types.Operator):
+ """ Random Scratch Operator """
+ bl_idname = "sequencer.randomscratchoperator"
+ bl_label = "Random Scratch Operator"
+
+ @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 ('META')
+ else:
+ return False
+
+ def invoke(self, context, event):
+
+ preferences = context.user_preferences
+ random_frames = preferences.addons[__package__].preferences.random_frames
+
+ sce = context.scene
+ seq=sce.sequence_editor
+ markers=sce.timeline_markers
+ if seq:
+ strip= seq.active_strip
+ if strip != None:
+ if "IN" and "OUT" in markers:
+ sin=markers["IN"].frame
+ sout=markers["OUT"].frame
+
+ # select active strip
+ strip = context.scene.sequence_editor.active_strip
+ stripname = strip.name
+ # collect strip names inside the meta
+ stripnames = []
+ stripnames.append(strip.name)
+ for i in seq.active_strip.sequences:
+ stripnames.append(i.name)
+ # get strip channel
+ channel = strip.channel
+ repeat = range(int((sout-sin)/random_frames))
+ print(sin, sout, sout-sin, (sout-sin)/random_frames, repeat)
+ for i in repeat:
+
+ # select all related strips
+ for j in stripnames:
+ strip = seq.sequences_all[j]
+ strip.select = True
+ strip = seq.sequences_all[stripname]
+ seq.active_strip = strip
+ # deselect all other strips
+ for j in context.selected_editable_sequences:
+ if j.name not in stripnames:
+ j.select=False
+ a = bpy.ops.sequencer.duplicate_move()
+ # select new strip
+ newstrip = seq.active_strip
+ # deselect all other strips
+ for j in context.selected_editable_sequences:
+ if j.name != newstrip.name:
+ j.select=False
+ # random cut
+ newstrip.frame_start = sin + i * random_frames
+ rand = functions.randomframe(newstrip)
+ functions.triminout(newstrip, rand, rand + random_frames)
+ newstrip.frame_start = i * random_frames + sin - newstrip.frame_offset_start
+ newstrip.channel = channel + 1
+
+
+ else:
+ self.report({'WARNING'}, "there is no IN and OUT")
+ bpy.ops.sequencer.reload()
+ return {'FINISHED'}
+
+
+class LoadRandomEditOperator(bpy.types.Operator):
+ """ Random Editor Operator """
+ bl_idname = "sequencer.loadrandomeditoperator"
+ bl_label = "Random Editor Operator"
+
+ @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 ('META')
+ else:
+ return False
+
+ def invoke(self, context, event):
+
+ #generate sources_dict
+ sources_dict = functions.get_marker_dict(random_selected_scene, random_number_of_subsets)
+ print("sources: ",sources_dict)
+
+ #generate cut_list
+ cut_dict = functions.get_cut_dict(currentscene, random_number_of_subsets)
+ print("cuts: ",cut_dict)
+
+ #generate random_edit
+ random_edit = functions.random_edit_from_random_subset(cut_dict, sources_dict)
+ print("random edit")
+ for i in random_edit: print("fr {}: dur: {} / {} {}".format(i[0],i[1],i[2],i[3]))
+
+ sce = bpy.context.scene
+ seq=sce.sequence_editor
+ markers=sce.timeline_markers
+ strip= seq.active_strip
+ stripname = strip.name
+ if "IN" and "OUT" in markers:
+ sin=markers["IN"].frame
+ sout=markers["OUT"].frame
+ # collect strip names inside the meta
+ stripnames = []
+ stripnames.append(strip.name)
+ for i in seq.active_strip.sequences:
+ stripnames.append(i.name)
+ # get strip channel
+ channel = strip.channel
+ #repeat = range(int((sout-sin)/random_frames))
+ #print(sin, sout, sout-sin, (sout-sin)/random_frames, repeat)
+ for i in random_edit:
+ # select all related strips
+ for j in stripnames:
+ strip = seq.sequences_all[j]
+ strip.select = True
+ strip = seq.sequences_all[stripname]
+ seq.active_strip = strip
+ # deselect all other strips
+ for j in bpy.context.selected_editable_sequences:
+ if j.name not in stripnames:
+ j.select=False
+
+ a = bpy.ops.sequencer.duplicate_move()
+ # select new strip
+ newstrip = seq.active_strip
+ # deselect all other strips
+ for j in bpy.context.selected_editable_sequences:
+ if j.name != newstrip.name:
+ j.select=False
+ # random cut
+ #newstrip.frame_start = sin + i * random_frames
+ #rand = functions.randomframe(newstrip)
+ functions.triminout(newstrip, MARKER, MARKER+DURATION)
+ newstrip.frame_start = i * random_frames + sin - newstrip.frame_offset_start
+ newstrip.channel = channel + 1
+ else:
+ self.report({'WARNING'}, "there is no IN and OUT")
+ bpy.ops.sequencer.reload()
+ return {'FINISHED'}
+
+
+class RandomEditorPanel(bpy.types.Panel):
+ """-_-_-"""
+ bl_label = "Random Editor"
+ bl_idname = "OBJECT_PT_RandomEditor"
+ bl_space_type = 'SEQUENCE_EDITOR'
+ bl_region_type = 'UI'
+
+ @classmethod
+ def poll(self, context):
+ if context.space_data.view_type in {'SEQUENCER',
+ 'SEQUENCER_PREVIEW'}:
+ strip = functions.act_strip(context)
+ scn = context.scene
+ preferences = context.user_preferences
+ prefs = preferences.addons[__package__].preferences
+ if scn and scn.sequence_editor and scn.sequence_editor.active_strip:
+ if prefs.use_random_editor:
+ return strip.type in ('META')
+ else:
+ return False
+
+ def draw_header(self, context):
+ layout = self.layout
+ layout.label(text="", icon="MOD_BUILD")
+
+ def draw(self, context):
+
+ preferences = context.user_preferences
+ prefs = preferences.addons[__package__].preferences
+
+ layout = self.layout
+ layout.label("______ cut duration:)")
+ layout = self.layout
+ layout.prop(prefs, "random_frames")
+ layout = self.layout
+ layout.operator("sequencer.randomscratchoperator")
+ layout = self.layout
+ layout.operator("sequencer.loadrandomeditoperator")
+
+
+
+
diff --git a/kinoraw_tools/recursive_loader.py b/kinoraw_tools/recursive_loader.py
new file mode 100644
index 00000000..82e4820e
--- /dev/null
+++ b/kinoraw_tools/recursive_loader.py
@@ -0,0 +1,261 @@
+# ##### 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
+
+
+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")
+
+
+ @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.recursive = scn.kr_recursive
+ self.recursive_select_by_extension = scn.kr_recursive_select_by_extension
+ self.ext = scn.kr_default_ext
+ except AttributeError:
+ functions.initSceneProperties(context)
+ self.recursive = scn.kr_recursive
+ self.recursive_select_by_extension = scn.kr_recursive_select_by_extension
+ self.ext = scn.kr_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(context, i[0], i[1])
+ try:
+ bpy.ops.sequencerextra.placefromfilebrowser()
+ except:
+ print("Error loading file (recursive loader error): ", i[1])
+ functions.add_marker(context, i[1], scn.frame_current)
+ self.report({'ERROR_INVALID_INPUT'}, 'Error loading file ')
+ pass
+
+
+ def execute(self, context):
+ scn = context.scene
+ if self.recursive == True:
+ #recursive
+ self.loader(context, functions.sortlist(\
+ functions.recursive(context, self.recursive_select_by_extension,\
+ self.ext)))
+ else:
+ #non recursive
+ self.loader(context, functions.sortlist(functions.onefolder(\
+ context, self.recursive_select_by_extension, self.ext)))
+ try:
+ scn.kr_recursive = self.recursive
+ scn.kr_recursive_select_by_extension = self.recursive_select_by_extension
+ scn.kr_default_ext = self.ext
+ except AttributeError:
+ functions.initSceneProperties(context)
+ self.recursive = scn.kr_recursive
+ self.recursive_select_by_extension = scn.kr_recursive_select_by_extension
+ self.ext = scn.kr_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):
+ scn=context.scene
+ if scn and scn.sequence_editor and scn.sequence_editor.active_strip:
+ return scn.sequence_editor.active_strip.type in ('IMAGE', '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 getexifvalues_image(lista):
+ metadata = []
+ with exiftool.ExifTool() as et:
+ try:
+ metadata = et.get_metadata_batch(lista)
+ except UnicodeDecodeError as Err:
+ print(Err)
+ #print(metadata[0])
+ print(len(metadata))
+ return metadata
+
+ def getexifvalues_movie(path):
+ metadata = []
+ with exiftool.ExifTool() as et:
+ try:
+ metadata = et.get_metadata_batch([path])
+ except UnicodeDecodeError as Err:
+ print(Err)
+ print(metadata[0])
+ print(len(metadata))
+ return metadata
+
+ 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
+
+ if strip.type == "IMAGE":
+ path = bpy.path.abspath(strip.directory)
+ os.chdir(path)
+ #get a list of files
+ lista = []
+ for i in strip.elements:
+ lista.append(i.filename)
+ print(lista)
+ return getexifvalues_image(lista)
+
+ if strip.type == "MOVIE":
+ path = bpy.path.abspath(strip.filepath)
+ print([path])
+ return getexifvalues_movie(path)
+
+
+
+ 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'}
+
+
+class ExifInfoPanel(bpy.types.Panel):
+ """Creates a Panel in the Object properties window"""
+ """ TODO: fix poll to hide when unuseful"""
+ bl_label = "EXIF Info Panel"
+ bl_space_type = 'SEQUENCE_EDITOR'
+ bl_region_type = 'UI'
+
+ @classmethod
+ def poll(self, context):
+ if context.space_data.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}:
+ strip = functions.act_strip(context)
+ scn = context.scene
+ preferences = context.user_preferences
+ prefs = preferences.addons[__package__].preferences
+
+ if scn and scn.sequence_editor and scn.sequence_editor.active_strip:
+ if prefs.use_exif_panel:
+ return strip.type in ('MOVIE', 'IMAGE')
+ else:
+ return False
+
+ def draw_header(self, context):
+ layout = self.layout
+ layout.label(text="", icon="RADIO")
+
+ def draw(self, context):
+ layout = self.layout
+ sce = context.scene
+ row = layout.row()
+ row.operator("sequencerextra.read_exif")
+ row = layout.row()
+ row.label(text="Exif Data", icon='RENDER_REGION')
+ row = layout.row()
+
+ try:
+ strip = context.scene.sequence_editor.active_strip
+
+ f = strip.frame_start
+ frame = sce.frame_current
+ try:
+ if len(sce['metadata']) == 1:
+ for d in sce['metadata'][0]:
+ split = layout.split(percentage=0.5)
+ col = split.column()
+ row = col.row()
+ col.label(text=d)
+ col = split.column()
+ col.label(str(sce['metadata'][0][d]))
+ else:
+ for d in sce['metadata'][frame - f]:
+ split = layout.split(percentage=0.5)
+ col = split.column()
+ row = col.row()
+ col.label(text=d)
+ col = split.column()
+ col.label(str(sce['metadata'][frame - f][d]))
+ except (IndexError, KeyError):
+ pass
+ except AttributeError:
+ pass \ No newline at end of file
diff --git a/kinoraw_tools/ui.py b/kinoraw_tools/ui.py
new file mode 100644
index 00000000..d9be3042
--- /dev/null
+++ b/kinoraw_tools/ui.py
@@ -0,0 +1,757 @@
+# ##### 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
+
+from . import functions
+
+
+
+# UI
+class SEQUENCER_EXTRA_MT_input(bpy.types.Menu):
+ bl_label = "Input"
+
+ def draw(self, context):
+ self.layout.operator_context = 'INVOKE_REGION_WIN'
+ self.layout.operator('sequencerextra.striprename',
+ text='File Name to Strip Name', icon='PLUGIN')
+ self.layout.operator('sequencerextra.editexternally',
+ text='Open with External Editor', icon='PLUGIN')
+ self.layout.operator('sequencerextra.edit',
+ text='Open with Editor', icon='PLUGIN')
+ self.layout.operator('sequencerextra.createmovieclip',
+ text='Create Movieclip strip', icon='PLUGIN')
+
+def sequencer_header_func(self, context):
+ self.layout.menu("SEQUENCER_EXTRA_MT_input")
+
+def sequencer_add_menu_func(self, context):
+ self.layout.operator('sequencerextra.placefromfilebrowser',
+ text='place from file browser', icon='PLUGIN').insert = False
+ self.layout.operator('sequencerextra.placefromfilebrowser',
+ text='insert from file browser', icon='PLUGIN').insert = True
+ self.layout.operator('sequencerextra.recursiveload',
+ text='recursive load from browser', icon='PLUGIN')
+ self.layout.separator()
+
+
+def sequencer_select_menu_func(self, context):
+ self.layout.operator_menu_enum('sequencerextra.select_all_by_type',
+ 'type', text='All by Type', icon='PLUGIN')
+ self.layout.separator()
+ self.layout.operator('sequencerextra.selectcurrentframe',
+ text='Before Current Frame', icon='PLUGIN').mode = 'BEFORE'
+ self.layout.operator('sequencerextra.selectcurrentframe',
+ text='After Current Frame', icon='PLUGIN').mode = 'AFTER'
+ self.layout.operator('sequencerextra.selectcurrentframe',
+ text='On Current Frame', icon='PLUGIN').mode = 'ON'
+ self.layout.separator()
+ self.layout.operator('sequencerextra.selectsamechannel',
+ text='Same Channel', icon='PLUGIN')
+
+
+def sequencer_strip_menu_func(self, context):
+ self.layout.operator('sequencerextra.extendtofill',
+ text='Extend to Fill', icon='PLUGIN')
+ self.layout.operator_menu_enum('sequencerextra.fadeinout',
+ 'mode', text='Fade', icon='PLUGIN')
+ self.layout.operator_menu_enum('sequencerextra.copyproperties',
+ 'prop', icon='PLUGIN')
+
+ self.layout.operator('sequencerextra.insert',
+ text='Insert (Single Channel)', icon='PLUGIN').singlechannel = True
+ self.layout.operator('sequencerextra.insert',
+ text='Insert', icon='PLUGIN').singlechannel = False
+ self.layout.operator('sequencerextra.ripplecut',
+ text='Ripple Cut', icon='PLUGIN')
+ self.layout.operator('sequencerextra.rippledelete',
+ text='Ripple Delete', icon='PLUGIN')
+ self.layout.separator()
+
+
+def time_frame_menu_func(self, context):
+ self.layout.operator('timeextra.trimtimelinetoselection',
+ text='Trim to Selection', icon='PLUGIN')
+ self.layout.operator('timeextra.trimtimeline',
+ text='Trim to Timeline Content', icon='PLUGIN')
+ self.layout.separator()
+ self.layout.operator('screenextra.frame_skip',
+ text='Skip Forward One Second', icon='PLUGIN').back = False
+ self.layout.operator('screenextra.frame_skip',
+ text='Skip Back One Second', icon='PLUGIN').back = True
+ self.layout.separator()
+
+
+def time_header_func(self, context):
+ self.layout.operator('sequencerextra.jogshuttle',
+ text='Jog/Shuttle', icon='NDOF_TURN')
+
+
+def clip_header_func(self, context):
+ self.layout.operator('sequencerextra.jogshuttle',
+ text='Jog/Shuttle', icon='NDOF_TURN')
+
+
+def clip_clip_menu_func(self, context):
+ self.layout.operator('clipextra.openactivestrip',
+ text='Open Active Strip', icon='PLUGIN')
+ self.layout.operator('clipextra.openfromfilebrowser',
+ text='Open from File Browser', icon='PLUGIN')
+ self.layout.separator()
+
+
+
+###############################
+
+
+def draw_color_balance(layout, color_balance):
+ layout = layout.split(percentage=0.33)
+ col = layout.column()
+ col.label(text="Lift:")
+ col.template_color_picker(color_balance, "lift", value_slider=True, cubic=True)
+ row = col.row()
+ row.prop(color_balance, "lift", text="")
+ row.prop(color_balance, "invert_lift", text="Invert")
+
+ col = layout.column()
+ col.label(text="Gamma:")
+ col.template_color_picker(color_balance, "gamma", value_slider=True, lock_luminosity=True, cubic=True)
+ row = col.row()
+ row.prop(color_balance, "gamma", text="")
+ row.prop(color_balance, "invert_gamma", text="Invert")
+
+ col = layout.column()
+ col.label(text="Gain:")
+ col.template_color_picker(color_balance, "gain", value_slider=True, lock_luminosity=True, cubic=True)
+ row = col.row()
+ row.prop(color_balance, "gain", text="")
+ row.prop(color_balance, "invert_gain", text="Invert")
+
+
+############################
+
+
+
+class JumptoCut(bpy.types.Panel):
+ bl_space_type = "SEQUENCE_EDITOR"
+ bl_region_type = "UI"
+ bl_label = "Jump to Cut 6"
+
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+ _frame_rate_args_prev = None
+ _preset_class = None
+
+ @staticmethod
+ def _draw_framerate_label(*args):
+ # avoids re-creating text string each draw
+ if JumptoCut._frame_rate_args_prev == args:
+ return JumptoCut._frame_rate_ret
+
+ fps, fps_base, preset_label = args
+
+ if fps_base == 1.0:
+ fps_rate = round(fps)
+ else:
+ fps_rate = round(fps / fps_base, 2)
+
+ # TODO: Change the following to iterate over existing presets
+ custom_framerate = (fps_rate not in {23.98, 24, 25, 29.97, 30, 50, 59.94, 60})
+
+ if custom_framerate is True:
+ fps_label_text = "Custom (%r fps)" % fps_rate
+ show_framerate = True
+ else:
+ fps_label_text = "%r fps" % fps_rate
+ show_framerate = (preset_label == "Custom")
+
+ JumptoCut._frame_rate_args_prev = args
+ JumptoCut._frame_rate_ret = args = (fps_label_text, show_framerate)
+ return args
+
+ @staticmethod
+ def draw_framerate(sub, rd):
+ if JumptoCut._preset_class is None:
+ JumptoCut._preset_class = bpy.types.RENDER_MT_framerate_presets
+
+ args = rd.fps, rd.fps_base, JumptoCut._preset_class.bl_label
+ fps_label_text, show_framerate = JumptoCut._draw_framerate_label(*args)
+
+ sub.menu("RENDER_MT_framerate_presets", text=fps_label_text)
+
+ if show_framerate:
+ sub.prop(rd, "fps")
+ sub.prop(rd, "fps_base", text="/")
+
+ @classmethod
+ def poll(self, context):
+ if context.space_data.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}:
+ strip = functions.act_strip(context)
+ scn = context.scene
+ preferences = context.user_preferences
+ prefs = preferences.addons[__package__].preferences
+ if scn and scn.sequence_editor:
+ if prefs.use_jumptocut:
+ return True
+ else:
+ return False
+
+ def draw_header(self, context):
+ layout = self.layout
+ layout.label(text="", icon="RENDER_ANIMATION")
+
+ def draw(self, context):
+ scn = context.scene
+ strip = functions.act_strip(context)
+
+ preferences = context.user_preferences
+ prefs = preferences.addons[__package__].preferences
+
+ layout = self.layout
+ layout = layout.box()
+ # jump to cut main controls
+
+ row=layout.row(align=True)
+
+ row.label(icon='TIME', text="Secs")
+ row.operator('screenextra.frame_skip',
+ text="", icon='TRIA_LEFT').back = True
+ row.operator('screenextra.frame_skip',
+ text="", icon='TRIA_RIGHT').back = False
+
+ row.separator()
+
+ row.label(icon='IPO_BOUNCE', text="Cuts")
+ row.operator("sequencer.strip_jump", icon="PLAY_REVERSE", text="").next=False
+ row.operator("sequencer.strip_jump", icon='PLAY', text="").next=True
+
+ row.separator()
+
+ row.label(icon='MARKER_HLT', text="Marker")
+ row.operator("screen.marker_jump", icon="TRIA_LEFT", text="").next=False
+ row.operator("screen.marker_jump", icon='TRIA_RIGHT', text="").next=True
+
+
+ rd = scn.render
+ screen = context.screen
+ row = layout.row(align=True)
+ row.operator("screen.frame_jump", text="", icon='REW').end = False
+ row.operator("screen.keyframe_jump", text="", icon='PREV_KEYFRAME').next = False
+ if not screen.is_animation_playing:
+ # if using JACK and A/V sync:
+ # hide the play-reversed button
+ # since JACK transport doesn't support reversed playback
+ if scn.sync_mode == 'AUDIO_SYNC' and context.user_preferences.system.audio_device == 'JACK':
+ sub = row.row(align=True)
+ sub.scale_x = 2.0
+ sub.operator("screen.animation_play", text="", icon='PLAY')
+ else:
+ row.operator("screen.animation_play", text="", icon='PLAY_REVERSE').reverse = True
+ row.operator("screen.animation_play", text="", icon='PLAY')
+ else:
+ sub = row.row(align=True)
+ sub.scale_x = 2.0
+ sub.operator("screen.animation_play", text="", icon='PAUSE')
+ row.operator("screen.keyframe_jump", text="", icon='NEXT_KEYFRAME').next = True
+ row.operator("screen.frame_jump", text="", icon='FF').end = True
+
+ row.separator()
+ row.prop(scn, "sync_mode", text="")
+ row.separator()
+ self.draw_framerate(row, rd)
+
+ row=layout.row(align=True)
+ row.operator("sequencer.refresh_all")
+ row.operator('sequencer.rendersize', text='set render size', icon='PLUGIN')
+
+ row=layout.row(align=True)
+ row.operator("sequencerextra.setstartend", icon="PREVIEW_RANGE", text="IN/OUT")
+ row.operator('timeextra.trimtimelinetoselection', text='Selection', icon='PREVIEW_RANGE')
+ row.operator('timeextra.trimtimeline', text='All', icon='PREVIEW_RANGE')
+
+ layout = self.layout
+ # layout = layout.box()
+ # panel setup ------------------------------------------------------
+ row=layout.row(align=True)
+ row = row.split(percentage=0.25)
+ row.prop(prefs, "kr_show_tools", text="Tools", icon='SEQ_SEQUENCER')
+ if prefs.kr_show_tools:
+ row = row.split(percentage=0.25)
+ row.prop(prefs, "kr_mini_ui", text="mini")
+ else:
+ row = row.split(percentage=0.25)
+ row.label(text = "")
+
+ row = row.split(percentage=0.25)
+ row.label(text = "")
+ row = row.split(percentage=0.25)
+ row.label(text = "")
+ row = row.split(percentage=0.33)
+ row.prop(prefs, "kr_show_info", text="", icon='VIEWZOOM')
+ row = row.split(percentage=0.5)
+ row.prop(prefs, "kr_extra_info", text="", icon='BORDERMOVE')
+ row = row.split(percentage=1)
+ row.prop(prefs, "kr_show_modifiers", text="", icon='RESTRICT_VIEW_OFF')
+
+ ##########
+
+ if prefs.kr_show_tools:
+ layout = self.layout
+ layout = layout.box()
+ # snap, handler selector and meta tools
+
+ if prefs.kr_mini_ui:
+ row=layout.row(align=True)
+ row.operator('sequencerextra.extrasnap', text='', icon='SNAP_ON').align=0
+ row.operator('sequencerextra.extrasnap', text='', icon='SNAP_SURFACE').align=1
+ row.operator('sequencerextra.extrasnap', text='', icon='SNAP_ON').align=2
+
+ row.separator()
+ row.operator('sequencerextra.extrahandles', text='', icon='TRIA_LEFT').side=0
+ row.operator('sequencerextra.extrahandles', text='', icon='PMARKER').side=1
+ row.operator('sequencerextra.extrahandles', text='', icon='TRIA_RIGHT').side=2
+
+ row.separator()
+ row.operator("sequencerextra.metacopy", icon="COPYDOWN", text="")
+ row.operator("sequencerextra.metapaste", icon='PASTEDOWN', text="")
+ row.separator()
+ row.operator('sequencerextra.meta_separate_trim', text='', icon='ALIGN')
+ row.separator()
+ row.prop(prefs, "use_io_tools", text="I/O tools")
+
+ # IN /OUT TOOLS
+
+
+ if prefs.use_io_tools:
+ row=layout.row(align=True)
+ if scn.kr_auto_markers == True:
+ row.prop(scn, "kr_auto_markers", text="")
+
+ row.separator()
+ row.operator("sequencerextra.sourcein", icon="MARKER_HLT", text="")
+ row.prop(scn, "kr_in_marker")
+ row.operator("sequencerextra.sourceout", icon='MARKER_HLT', text="")
+ row.prop(scn, "kr_out_marker")
+ else:
+ row.prop(scn, "kr_auto_markers", text="AUTO i/o")
+ row.operator("sequencerextra.sourcein", icon="MARKER_HLT", text="IN")
+ row.operator("sequencerextra.sourceout", icon='MARKER_HLT', text="OUT")
+ row.separator()
+ row.operator("sequencerextra.setinout", icon="ARROW_LEFTRIGHT", text="")
+ row.operator("sequencerextra.triminout", icon="FULLSCREEN_EXIT", text="")
+
+ # miniUI extra actions
+ row=layout.row(align=True)
+ row.operator('sequencerextra.jogshuttle',
+ text='', icon='NDOF_TURN')
+ row.operator('sequencerextra.navigateup',
+ text='', icon='FILE_PARENT')
+ row.operator('sequencerextra.extendtofill',
+ text='', icon='STYLUS_PRESSURE')
+ row.operator('sequencerextra.placefromfilebrowser',
+ text='', icon='TRIA_DOWN').insert = False
+ row.operator('sequencerextra.placefromfilebrowser',
+ text='', icon='TRIA_RIGHT').insert = True
+ row.operator('sequencer.slip',
+ text='', icon='MOD_SHRINKWRAP')
+ row.operator_menu_enum('sequencerextra.fadeinout',
+ 'mode', text='fade', icon='MOD_ARRAY')
+ row.operator_menu_enum('sequencerextra.copyproperties',
+ 'prop', text='copy',icon='SCRIPT')
+
+ else: # NOT prefs.kr_mini_ui:
+
+ row=layout.row(align=True)
+ row.label("Snap:")
+ #row=layout.row(align=True)
+ row.operator('sequencerextra.extrasnap', text='left', icon='SNAP_ON').align=0
+ row.operator('sequencerextra.extrasnap', text='center', icon='SNAP_SURFACE').align=1
+ row.operator('sequencerextra.extrasnap', text='right', icon='SNAP_ON').align=2
+
+ row=layout.row(align=True)
+ row.label("Handlers:")
+ #row=layout.row(align=True)
+ row.operator('sequencerextra.extrahandles', text='left', icon='TRIA_LEFT').side=0
+ row.operator('sequencerextra.extrahandles', text='both', icon='PMARKER').side=1
+ row.operator('sequencerextra.extrahandles', text='right', icon='TRIA_RIGHT').side=2
+
+ row=layout.row()
+ row=layout.row(align=True)
+ split = row.split(percentage=0.99)
+ colR2 = split.column()
+ row1 = colR2.row(align=True)
+ row1.operator("sequencerextra.metacopy", icon="COPYDOWN", text="meta-copy")
+ row1.operator("sequencerextra.metapaste", icon='PASTEDOWN', text="paste-snap")
+ split = row.split(percentage=0.99)
+ colR3 = split.column()
+ colR3.operator('sequencerextra.meta_separate_trim', text='unMeta & Trim', icon='ALIGN')
+
+
+
+ # IN /OUT TOOLS
+ row=layout.box()
+ split=row.split(percentage=0.7)
+ colR1 = split.column()
+ row1=colR1.row(align=True)
+ row1.operator("sequencerextra.sourcein", icon="MARKER_HLT", text="set IN")
+ row1.operator("sequencerextra.sourceout", icon='MARKER_HLT', text="set OUT")
+ colR3 = split.column()
+ colR3.operator("sequencerextra.setinout", icon="ARROW_LEFTRIGHT", text="selected")
+
+ split=row.split(percentage=0.7)
+ colR1 = split.column()
+ row1=colR1.row(align=True)
+ if scn.kr_auto_markers == False:
+ row1.prop(scn, "kr_auto_markers", text="auto markers")
+ else:
+ row1.prop(scn, "kr_auto_markers", text="")
+ row1.prop(scn, "kr_in_marker")
+ row1.prop(scn, "kr_out_marker")
+ row1.active = scn.kr_auto_markers
+ colR3 = split.column()
+ colR3.operator("sequencerextra.triminout", icon="FULLSCREEN_EXIT", text="trim",emboss=True)
+
+
+ # UI extra actions
+ row=layout.row(align=True)
+ row.operator('sequencerextra.jogshuttle',
+ text='Jog/Shuttle', icon='NDOF_TURN')
+ row.operator('sequencerextra.navigateup',
+ text='Navigate Up', icon='FILE_PARENT')
+ row.operator('sequencerextra.extendtofill',
+ text='Extend to Fill', icon='STYLUS_PRESSURE')
+ row=layout.row(align=True)
+ row.operator('sequencerextra.placefromfilebrowser',
+ text='File Place', icon='TRIA_DOWN').insert = False
+ row.operator('sequencerextra.placefromfilebrowser',
+ text='File Insert', icon='TRIA_RIGHT').insert = True
+ row.operator('sequencer.slip',
+ text='Slip', icon='MOD_SHRINKWRAP')
+ row=layout.row(align=True)
+ row.operator_menu_enum('sequencerextra.fadeinout',
+ 'mode', text='Fade', icon='MOD_ARRAY')
+ row.operator_menu_enum('sequencerextra.copyproperties',
+ 'prop', icon='SCRIPT')
+
+ # INFO boxes
+ # INFO boxes
+ # INFO boxes
+ if strip != None:
+ if prefs.kr_show_info:
+ layout = layout.box()
+ row = layout.split(percentage=0.075)
+ row.prop(prefs, "kr_show_info", text="",icon='VIEWZOOM', emboss=True)
+ row = row.split(percentage=0.3)
+ row.prop(strip, "type", text="")
+ row = row.split(percentage=1)
+ row.prop(strip, "name", text="")
+
+ # MUTE INFORMATION
+ layout.active = (not strip.mute)
+
+ # basic info
+ row = layout.row()
+ row.prop(strip, "channel")
+ row.prop(strip, "frame_start")
+ row.prop(strip, "frame_final_duration")
+
+ # source info
+ row = layout.split(percentage=0.8)
+
+ if strip.type in {'MOVIE', 'SOUND'}:
+ row.prop(strip, "filepath", text="")
+
+ if strip.type == 'IMAGE':
+ row.prop(strip, "directory", text="")
+ # Current element for the filename
+ elem = strip.strip_elem_from_frame(context.scene.frame_current)
+ if elem:
+ row = layout.row()
+ row.prop(elem, "filename", text="File") # strip.elements[0] could be a fallback
+ row.operator("sequencer.change_path", text="change files")
+
+ if strip.type == 'COLOR':
+ row.prop(strip, "color", text = "")
+
+ # trim info
+ if strip.type not in {"SPEED", "WIPE", "CROSS", "ADJUSTMENT"}:
+ row = row.split(percentage=1)
+ row.prop(prefs, "kr_show_trim", text="Trim")
+ if prefs.kr_show_trim:
+ if not isinstance(strip, bpy.types.EffectSequence):
+ row = layout.row(align=True)
+ row.label(text="hard:")
+ row.prop(strip, "animation_offset_start", text="Start")
+ row.prop(strip, "animation_offset_end", text="End")
+ row = layout.row(align=True)
+ row.label(text="soft:")
+ row.prop(strip, "frame_offset_start", text="Start")
+ row.prop(strip, "frame_offset_end", text="End")
+
+
+
+ row = layout.row()
+
+ # special strips info
+ if strip.type == 'SPEED':
+ row.prop(strip, "multiply_speed")
+ if strip.type in {'CROSS', 'GAMMA_CROSS', 'WIPE', 'ALPHA_OVER', 'ALPHA_UNDER', 'OVER_DROP'}:
+ row.prop(strip, "use_default_fade", "Default Fade")
+ if not strip.use_default_fade:
+ row.prop(strip, "effect_fader", text="Effect fader")
+ if strip.type == 'GAUSSIAN_BLUR':
+ row.prop(strip, "size_x")
+ row.prop(strip, "size_y")
+
+ if strip.type == 'WIPE':
+ row = layout.row()
+ row.prop(strip, "transition_type", expand=True)
+ row = layout.row()
+ row.prop(strip, "direction", expand=True)
+ row.prop(strip, "blur_width", slider=True)
+ if strip.transition_type in {'SINGLE', 'DOUBLE'}:
+ row.prop(strip, "angle")
+
+ if strip.type == 'GLOW':
+ flow = layout.column_flow()
+ flow.prop(strip, "threshold", slider=True)
+ flow.prop(strip, "clamp", slider=True)
+ flow.prop(strip, "boost_factor")
+ flow.prop(strip, "blur_radius")
+
+ row = layout.row()
+ row.prop(strip, "quality", slider=True)
+ row.prop(strip, "use_only_boost")
+
+ if strip.type == 'SPEED':
+ row = layout.row()
+ row.prop(strip, "use_default_fade", "Stretch to input strip length")
+ if not strip.use_default_fade:
+ row.prop(strip, "use_as_speed")
+ if strip.use_as_speed:
+ layout.prop(strip, "speed_factor")
+ else:
+ layout.prop(strip, "speed_factor", text="Frame number")
+ layout.prop(strip, "scale_to_length")
+
+ if strip.type == 'TRANSFORM':
+ row = layout.row(align=True)
+ row.prop(strip, "interpolation")
+ row.prop(strip, "translation_unit")
+ row = layout.row(align=True)
+ row.prop(strip, "translate_start_x", text="Pos X")
+ row.prop(strip, "translate_start_y", text="Pos Y")
+
+ row = layout.row(align=True)
+ if strip.use_uniform_scale:
+ row.prop(strip, "scale_start_x", text="Scale")
+ else:
+ row.prop(strip, "scale_start_x", text="Scale X")
+ row.prop(strip, "scale_start_y", text="Scale Y")
+ row = layout.row(align=True)
+ row.prop(strip, "use_uniform_scale")
+ row.prop(strip, "rotation_start", text="Rotation")
+
+ if strip.type == 'MULTICAM':
+ layout.prop(strip, "multicam_source")
+
+ row = layout.row(align=True)
+ sub = row.row(align=True)
+ sub.scale_x = 2.0
+
+ sub.operator("screen.animation_play", text="", icon='PAUSE' if context.screen.is_animation_playing else 'PLAY')
+
+ row.label("Cut To")
+ for i in range(1, strip.channel):
+ row.operator("sequencer.cut_multicam", text="%d" % i).camera = i
+
+
+ try:
+ if strip.input_count > 0:
+ col = layout.column()
+ col.prop(strip, "input_1")
+ if strip.input_count > 1:
+ col.prop(strip, "input_2")
+ except AttributeError:
+ pass
+
+ # extra info box:
+ if prefs.kr_extra_info:
+ layout = self.layout
+ box = layout.box()
+ if strip.type not in {'SOUND'}:
+ row = box.row(align=True)
+ sub = row.row(align=True)
+ # MUTE THIS BOX
+ box.active = (not strip.mute)
+ sub.prop(prefs, "kr_extra_info", text = "", icon='BORDERMOVE', emboss = True)
+ sub.separator()
+ sub.prop(strip, "blend_alpha", text="Opacity", slider=True)
+ row.prop(strip, "mute", toggle=True, icon_only=True)
+ row.prop(strip, "lock", toggle=True, icon_only=True)
+
+ row = box.row(align=True)
+
+ col = row.column()
+ col.prop(strip, "strobe")
+ col.prop(strip, "use_flip_x", text="flip X")
+ col.prop(strip, "use_flip_y", text="flip Y")
+ col.prop(strip, "use_reverse_frames", text="Backwards")
+ col.prop(strip, "use_deinterlace")
+
+ col = row.column()
+ col.prop(strip, "blend_type", icon='COLOR', text = "")
+
+ col.prop(strip, "color_saturation", text="Saturation")
+ col.prop(strip, "color_multiply", text="Multiply")
+ col.prop(strip, "use_float", text="Convert Float")
+ col.prop(strip, "alpha_mode")
+
+ row = box.row(align=True)
+ row.prop(strip, "use_translation", text="Image Offset", icon = "AXIS_TOP")
+ row.prop(strip, "use_crop", text="Image Crop", icon = "BORDER_RECT")
+ if strip.use_translation:
+ row = box.row(align=True)
+ row.prop(strip.transform, "offset_x", text="X")
+ row.prop(strip.transform, "offset_y", text="Y")
+ if strip.use_crop:
+ row = box.row(align=True)
+ row.prop(strip.crop, "max_y")
+ row.prop(strip.crop, "min_x")
+ row.prop(strip.crop, "min_y")
+ row.prop(strip.crop, "max_x")
+
+
+ #sound type
+ else:
+ row = box.row(align=True)
+ row.prop(strip, "volume")
+ row.prop(strip, "mute", toggle=True, icon_only=True)
+ row.prop(strip, "lock", toggle=True, icon_only=True)
+
+ sound = strip.sound
+ if sound is not None:
+ row = box.row()
+ if sound.packed_file:
+ row.operator("sound.unpack", icon='PACKAGE', text="Unpack")
+ else:
+ row.operator("sound.pack", icon='UGLYPACKAGE', text="Pack")
+
+ row.prop(sound, "use_memory_cache")
+
+ row.prop(strip, "show_waveform")
+
+ row = box.row()
+ row.prop(strip, "pitch")
+ row.prop(strip, "pan")
+
+
+ ## modifiers
+ if strip.type != 'SOUND' and prefs.kr_show_modifiers:
+ sequencer = context.scene.sequence_editor
+ layout = self.layout
+ layout = layout.box()
+ # MUTE THIS BOX
+ layout.active = (not strip.mute)
+ row = layout.split(percentage=0.075)
+ row.prop(prefs, "kr_show_modifiers", text = "", icon='RESTRICT_VIEW_OFF', emboss = True)
+ row = row.split(percentage=0.40)
+ row.prop(strip, "use_linear_modifiers", text="Linear")
+ row = row.split(percentage=1)
+ row.operator_menu_enum("sequencer.strip_modifier_add", "type")
+ for mod in strip.modifiers:
+ box = layout.box()
+
+ row = box.row()
+ row.prop(mod, "show_expanded", text="", emboss=False)
+ row.prop(mod, "name", text="")
+
+ row.prop(mod, "mute", text="")
+
+ sub = row.row(align=True)
+ props = sub.operator("sequencer.strip_modifier_move", text="", icon='TRIA_UP')
+ props.name = mod.name
+ props.direction = 'UP'
+ props = sub.operator("sequencer.strip_modifier_move", text="", icon='TRIA_DOWN')
+ props.name = mod.name
+ props.direction = 'DOWN'
+
+ row.operator("sequencer.strip_modifier_remove", text="", icon='X', emboss=False).name = mod.name
+
+ if mod.show_expanded:
+ row = box.row()
+ row.prop(mod, "input_mask_type", expand=True)
+
+ if mod.input_mask_type == 'STRIP':
+ sequences_object = sequencer
+ if sequencer.meta_stack:
+ sequences_object = sequencer.meta_stack[-1]
+ box.prop_search(mod, "input_mask_strip", sequences_object, "sequences", text="Mask")
+ else:
+ box.prop(mod, "input_mask_id")
+ row = box.row()
+ row.prop(mod, "mask_time", expand=True)
+
+ if mod.type == 'COLOR_BALANCE':
+ box.prop(mod, "color_multiply")
+ draw_color_balance(box, mod.color_balance)
+ elif mod.type == 'CURVES':
+ box.template_curve_mapping(mod, "curve_mapping", type='COLOR')
+ elif mod.type == 'HUE_CORRECT':
+ box.template_curve_mapping(mod, "curve_mapping", type='HUE')
+ elif mod.type == 'BRIGHT_CONTRAST':
+ col = box.column()
+ col.prop(mod, "bright")
+ col.prop(mod, "contrast")
+ elif mod.type == 'WHITE_BALANCE':
+ col = box.column()
+ col.prop(mod, "white_value")
+ elif mod.type == 'TONEMAP':
+ col = box.column()
+ col.prop(mod, "tonemap_type")
+ if mod.tonemap_type == 'RD_PHOTORECEPTOR':
+ col.prop(mod, "intensity")
+ col.prop(mod, "contrast")
+ col.prop(mod, "adaptation")
+ col.prop(mod, "correction")
+ elif mod.tonemap_type == 'RH_SIMPLE':
+ col.prop(mod, "key")
+ col.prop(mod, "offset")
+ col.prop(mod, "gamma")
+
+ if "copy_modifiers" in dir(bpy.ops.sequencer):
+ row = layout.row(align=True)
+ row.operator("sequencer.copy_modifiers", text="Copy Modifiers", icon='COPYDOWN')
+ row.operator("sequencer.paste_modifiers", text="Paste Modifiers", icon='PASTEDOWN')
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+