Initial commit kinoraw_tools by Carlos Padial T51109 T50357
This commit is contained in:
parent
725dbe4d41
commit
e9f96e1d93
|
@ -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()
|
|
@ -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'}
|
||||
|
||||
|
|
@ -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")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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'}
|
||||
|
||||
|
||||
|
|
@ -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]
|
|
@ -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
|
|
@ -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
|
@ -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")
|
||||
|
||||
|
||||
|
||||
|
|
@ -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")
|
||||
|
||||
|
||||
|
||||
|
|
@ -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
|
|
@ -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')
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue