Initial commit kinoraw_tools by Carlos Padial T51109 T50357

This commit is contained in:
Brendon Murphy 2017-04-15 14:25:27 +10:00
parent 725dbe4d41
commit e9f96e1d93
12 changed files with 5328 additions and 0 deletions

335
kinoraw_tools/__init__.py Normal file
View File

@ -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()

View File

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

194
kinoraw_tools/datamosh.py Normal file
View File

@ -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")

140
kinoraw_tools/eco.py Normal file
View File

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

330
kinoraw_tools/exiftool.py Normal file
View File

@ -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]

439
kinoraw_tools/functions.py Normal file
View File

@ -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

645
kinoraw_tools/jumptocut.py Normal file
View File

@ -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)

File diff suppressed because it is too large Load Diff

View File

@ -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")

View File

@ -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")

View File

@ -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

757
kinoraw_tools/ui.py Normal file
View File

@ -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')