sequencer_kinoraw_tools: move to contrib: T63750
This commit is contained in:
parent
774a8331c3
commit
dccb34a639
|
@ -1,399 +0,0 @@
|
|||
# ##### 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, 2),
|
||||
"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 importlib
|
||||
importlib.reload(jumptocut)
|
||||
importlib.reload(operators_extra_actions)
|
||||
importlib.reload(audio_tools)
|
||||
importlib.reload(proxy_tools)
|
||||
importlib.reload(recursive_loader)
|
||||
importlib.reload(eco)
|
||||
importlib.reload(random_editor)
|
||||
importlib.reload(ui)
|
||||
importlib.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
|
||||
from bpy.types import (
|
||||
AddonPreferences,
|
||||
)
|
||||
from bpy.props import (
|
||||
IntProperty,
|
||||
StringProperty,
|
||||
BoolProperty,
|
||||
EnumProperty,
|
||||
)
|
||||
|
||||
|
||||
class KinorawToolsAddon(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",
|
||||
description="Show modifiers from selected strip",
|
||||
default=False
|
||||
)
|
||||
kr_extra_info: BoolProperty(
|
||||
name="Show extra info",
|
||||
description="Show extra info and settings from selected strip",
|
||||
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="Jump to Cut Panel",
|
||||
default=True
|
||||
)
|
||||
use_io_tools: BoolProperty(
|
||||
name="Enable in and out tools in Jump to Cut 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 scripts",
|
||||
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's 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 scripts",
|
||||
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
|
||||
)
|
||||
show_shortcuts = BoolProperty(
|
||||
name="Hot Keys",
|
||||
default=False,
|
||||
description="List of the shortcuts used for the included various tools",
|
||||
)
|
||||
show_experimental = BoolProperty(
|
||||
name="Tools with External Dependencies / Experimental",
|
||||
default=False,
|
||||
description="List of various tools that need an External Library "
|
||||
"or are Experimental\nPlease read the Documentation "
|
||||
"before enabling them",
|
||||
)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
icon_1 = "TRIA_RIGHT" if not self.show_shortcuts else "TRIA_DOWN"
|
||||
icon_2 = "TRIA_RIGHT" if not self.show_experimental else "TRIA_DOWN"
|
||||
|
||||
box = layout.box()
|
||||
box.prop(self, "use_jumptocut")
|
||||
box = layout.box()
|
||||
box.prop(self, "use_eco_tools")
|
||||
|
||||
box_exp = layout.box()
|
||||
box_exp.prop(self, "show_experimental", emboss=False, icon=icon_2)
|
||||
|
||||
if self.show_experimental:
|
||||
box = box_exp.box()
|
||||
box.prop(self, "use_audio_tools")
|
||||
box = box_exp.box()
|
||||
box.prop(self, "use_proxy_tools")
|
||||
box = box_exp.box()
|
||||
box.prop(self, "use_exif_panel")
|
||||
box = box_exp.box()
|
||||
box.prop(self, "use_glitch_panel")
|
||||
box = box_exp.box()
|
||||
box.prop(self, "use_random_editor")
|
||||
|
||||
box = layout.box()
|
||||
box.prop(self, "show_shortcuts", emboss=False, icon=icon_1)
|
||||
|
||||
if self.show_shortcuts:
|
||||
box.label(text="Skip One Second Forward: Ctrl + Shift + Right Arrow", icon="LAYER_USED")
|
||||
box.label(text="Skip One Second Backwards: Ctrl + Shift + Left Arrow", icon="LAYER_USED")
|
||||
box.label(text="Jump to Previous Strip: Q", icon="LAYER_USED")
|
||||
box.label(text="Jump to Next Strip: W", icon="LAYER_USED")
|
||||
box.label(text="Source IN: Ctrl + Shift + I", icon="LAYER_USED")
|
||||
box.label(text="Source OUT: Ctrl + Shift + O", icon="LAYER_USED")
|
||||
box.label(text="Jump to Previous Marker: Shift + Q", icon="LAYER_USED")
|
||||
box.label(text="Jump to Next Marker: Shift + W", icon="LAYER_USED")
|
||||
|
||||
|
||||
# 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()
|
|
@ -1,358 +0,0 @@
|
|||
# gpl: authors Carlos Padial, Turi Scandurra
|
||||
|
||||
import bpy
|
||||
import os
|
||||
from bpy.types import (
|
||||
Operator,
|
||||
Panel,
|
||||
)
|
||||
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(Operator):
|
||||
bl_idname = "sequencer.extract_wav_operator"
|
||||
bl_label = "Extract Wav from movie strip Operator"
|
||||
bl_description = "Use ffmpeg to extract audio from video and import it synced"
|
||||
|
||||
@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.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("The audio File exists")
|
||||
|
||||
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 view_layer
|
||||
context.view_layer.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 view_layer
|
||||
context.view_layer.update()
|
||||
|
||||
# Match the original clip's length
|
||||
newstrip.frame_start = strip.frame_start - strip.animation_offset_start
|
||||
|
||||
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(Operator):
|
||||
bl_idname = "sequencer.external_audio_set_sync"
|
||||
bl_label = "set sync info"
|
||||
bl_description = ("Get sync info from selected audio and video strip "
|
||||
"and store it into a text file")
|
||||
|
||||
@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.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 is not 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(Operator):
|
||||
bl_idname = "sequencer.external_audio_reload"
|
||||
bl_label = "Reload External audio"
|
||||
bl_description = ("Reload external audio synced to selected movie strip "
|
||||
"according to info from a text file")
|
||||
|
||||
@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.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 view_layer
|
||||
context.view_layer.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 view_layer
|
||||
context.view_layer.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(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'}:
|
||||
scn = context.scene
|
||||
preferences = context.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.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(Operator):
|
||||
bl_idname = "sequencer.openmeterbridge"
|
||||
bl_label = "External VU meter"
|
||||
bl_description = "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.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'}
|
|
@ -1,189 +0,0 @@
|
|||
# gpl: authors Carlos Padial, Turi Scandurra
|
||||
|
||||
import bpy
|
||||
import os
|
||||
from bpy.props import IntProperty
|
||||
from bpy.types import (
|
||||
Operator,
|
||||
Panel,
|
||||
)
|
||||
from . import functions
|
||||
|
||||
|
||||
proxy_qualities = [
|
||||
("1", "25%", ""), ("2", "50%", ""),
|
||||
("3", "75%", ""), ("4", "100%", ""),
|
||||
("5", "none", "")
|
||||
]
|
||||
|
||||
|
||||
# functions
|
||||
def createdatamosh(context, strip):
|
||||
preferences = context.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):
|
||||
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):
|
||||
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(Operator):
|
||||
bl_idname = "sequencer.createavi"
|
||||
bl_label = "Create avi file"
|
||||
bl_description = "Create an avi output file"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
size: IntProperty(
|
||||
name="proxysize",
|
||||
default=1
|
||||
)
|
||||
|
||||
@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):
|
||||
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(Operator):
|
||||
bl_idname = "sequencer.createdatamosh"
|
||||
bl_label = "Create Datamosh"
|
||||
bl_description = "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.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(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.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.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
|
||||
|
||||
layout.prop(prefs, "all_keyframes")
|
||||
layout.prop(prefs, "load_glitch")
|
||||
|
||||
layout.operator("sequencer.createdatamosh")
|
|
@ -1,127 +0,0 @@
|
|||
# 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.types import (
|
||||
Operator,
|
||||
Panel,
|
||||
)
|
||||
from . import functions
|
||||
|
||||
|
||||
class EcoPanel(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.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):
|
||||
strip = functions.act_strip(context)
|
||||
seq_type = strip.type
|
||||
|
||||
preferences = context.preferences
|
||||
prefs = preferences.addons[__package__].preferences
|
||||
|
||||
if seq_type in ('MOVIE', 'IMAGE', 'META', 'MOVIECLIP', 'SCENE'):
|
||||
layout = self.layout
|
||||
col = layout.column()
|
||||
|
||||
col.prop(prefs, "eco_value", text="Ecos")
|
||||
col.prop(prefs, "eco_offset", text="Offset")
|
||||
col.prop(prefs, "eco_use_add_blend_mode", text="Use add blend mode")
|
||||
col.operator("sequencer.eco")
|
||||
|
||||
|
||||
class OBJECT_OT_EcoOperator(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.preferences
|
||||
prefs = preferences.addons[__package__].preferences
|
||||
|
||||
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'}
|
|
@ -1,330 +0,0 @@
|
|||
# -*- 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 undefined 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]
|
|
@ -1,455 +0,0 @@
|
|||
# ##### 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
|
||||
import operator
|
||||
import subprocess
|
||||
import random
|
||||
from bpy.props import (
|
||||
IntProperty,
|
||||
FloatProperty,
|
||||
EnumProperty,
|
||||
BoolProperty,
|
||||
)
|
||||
|
||||
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 error_handlers(self, op_name, errors, reports="ERROR"):
|
||||
if self and reports:
|
||||
self.report({'INFO'},
|
||||
reports + ": some operations could not be performed "
|
||||
"(See Console for more info)")
|
||||
|
||||
print("\n[Kinoraw Tools]\nOperator: {}\nWarning: {}\n".format(op_name, errors))
|
||||
|
||||
|
||||
def initSceneProperties(context):
|
||||
# initSceneProperties is ONLY for variables 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 is 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 is 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
|
||||
'''
|
||||
for a in context.window.screen.areas:
|
||||
if a.type == 'FILE_BROWSER':
|
||||
params = a.spaces[0].params
|
||||
break
|
||||
try:
|
||||
params
|
||||
except UnboundLocalError:
|
||||
return {'CANCELLED'}
|
||||
|
||||
path = params.directory
|
||||
return path
|
||||
|
||||
|
||||
def getfilepathfrombrowser(context):
|
||||
"""
|
||||
returns path and file from filebrowser
|
||||
"""
|
||||
for a in context.window.screen.areas:
|
||||
if a.type == 'FILE_BROWSER':
|
||||
params = a.spaces[0].params
|
||||
break
|
||||
try:
|
||||
params
|
||||
except UnboundLocalError:
|
||||
return {'CANCELLED'}
|
||||
|
||||
if params.filename == '':
|
||||
return {'CANCELLED'}
|
||||
|
||||
path = params.directory
|
||||
filename = params.filename
|
||||
return path, filename
|
||||
|
||||
|
||||
def setpathinbrowser(context, path, file):
|
||||
'''
|
||||
set path and file in the filebrowser
|
||||
'''
|
||||
for a in context.window.screen.areas:
|
||||
if a.type == 'FILE_BROWSER':
|
||||
params = a.spaces[0].params
|
||||
break
|
||||
try:
|
||||
params
|
||||
except UnboundLocalError:
|
||||
|
||||
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
|
||||
|
||||
if detect_strip_type(path + filename) == 'MOVIE':
|
||||
if recursive_select_by_extension is 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
|
||||
|
||||
for root, dirs, files in os.walk(path):
|
||||
for file in files:
|
||||
if recursive_select_by_extension is 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)
|
||||
|
||||
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 is 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
|
||||
"""
|
||||
|
||||
subset_list = generate_subsets_list(number_of_subsets)
|
||||
# generate dict with a list for each subset
|
||||
marker_dict = {}
|
||||
|
||||
for subset in subset_list:
|
||||
lists = get_matching_markers(scene, subset)
|
||||
marker_dict[subset] = lists
|
||||
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
|
||||
|
||||
lists = get_matching_markers(scene)
|
||||
lists.append(scene.frame_start)
|
||||
lists.append(scene.frame_end)
|
||||
lists.sort()
|
||||
cut_dict = {}
|
||||
|
||||
for i, j in enumerate(lists):
|
||||
try:
|
||||
cut_dict[j] = lists[i + 1] - j
|
||||
except IndexError:
|
||||
continue
|
||||
return cut_dict
|
|
@ -1,656 +0,0 @@
|
|||
# ##### 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.types import (
|
||||
Operator,
|
||||
)
|
||||
from bpy.props import (
|
||||
IntProperty,
|
||||
)
|
||||
from bpy.app.handlers import persistent
|
||||
|
||||
|
||||
class OBJECT_OT_Setinout(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 is 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(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 is 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(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):
|
||||
scn = context.scene
|
||||
if scn:
|
||||
return scn.sequence_editor
|
||||
else:
|
||||
return False
|
||||
|
||||
def execute(self, context):
|
||||
functions.initSceneProperties(context)
|
||||
scn = context.scene
|
||||
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(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):
|
||||
scn = context.scene
|
||||
if scn:
|
||||
return scn.sequence_editor
|
||||
else:
|
||||
return False
|
||||
|
||||
def execute(self, context):
|
||||
scn = context.scene
|
||||
functions.initSceneProperties(context)
|
||||
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(Operator): # Operator set start & end
|
||||
bl_label = "Set Start and 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(Operator): # Operator copy source in/out
|
||||
bl_label = "Trim and Meta-Copy"
|
||||
bl_idname = "sequencerextra.metacopy"
|
||||
bl_description = ("Make meta from selected strips, trim it to in / out\n"
|
||||
"(if available) and copy it to clipboard")
|
||||
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
def execute(self, context):
|
||||
try:
|
||||
# redo
|
||||
scene = bpy.context.scene
|
||||
seq = scene.sequence_editor
|
||||
markers = scene.timeline_markers
|
||||
strip1 = seq.active_strip
|
||||
|
||||
if strip1 is None:
|
||||
self.report({'ERROR'}, "No strip selected")
|
||||
return {"CANCELLED"}
|
||||
|
||||
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 trimmed and copied")
|
||||
else:
|
||||
bpy.ops.sequencer.meta_make()
|
||||
bpy.ops.sequencer.copy()
|
||||
bpy.ops.sequencer.meta_separate()
|
||||
self.report({'WARNING'}, "No In and Out!! META has been copied")
|
||||
|
||||
except Exception as e:
|
||||
functions.error_handlers(self,
|
||||
"sequencerextra.metacopy", e, "Trim and Meta-Copy")
|
||||
|
||||
return {"CANCELLED"}
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class OBJECT_OT_Metapaste(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):
|
||||
# redo
|
||||
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.view_layer.update()
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
# Operator paste source in/out
|
||||
class OBJECT_OT_Unmetatrim(Operator):
|
||||
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 is 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 is 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(Operator): # Operator paste source in/out
|
||||
bl_label = "Extra Snap"
|
||||
bl_idname = "sequencerextra.extrasnap"
|
||||
bl_description = "Snap the right, center or left of the strip to current frame"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
# align: 0 = left snap, 1 = center snap, 2= right snap
|
||||
align: IntProperty(
|
||||
name="Align",
|
||||
min=0, max=2,
|
||||
default=1
|
||||
)
|
||||
|
||||
@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):
|
||||
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(Operator): # Operator paste source in/out
|
||||
bl_label = "Extra Handles"
|
||||
bl_idname = "sequencerextra.extrahandles"
|
||||
bl_description = "Snap the right, center or left of the strip to current frame"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
# side: 0 = left , 1 = both, 2= right
|
||||
side: IntProperty(
|
||||
name="Side",
|
||||
min=0, max=2,
|
||||
default=1
|
||||
)
|
||||
|
||||
@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):
|
||||
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)
|
||||
|
||||
if scn.kr_auto_markers:
|
||||
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
|
@ -1,344 +0,0 @@
|
|||
# gpl: authors Carlos Padial, Turi Scandurra
|
||||
|
||||
import bpy
|
||||
import os
|
||||
from bpy.types import (
|
||||
Operator,
|
||||
Panel,
|
||||
)
|
||||
from bpy.props import IntProperty
|
||||
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.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 is 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.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("File already exists")
|
||||
|
||||
# set up proxy settings
|
||||
strip.use_proxy = True
|
||||
try:
|
||||
strip.use_proxy_custom_file = True
|
||||
strip.proxy.filepath = bpy.path.relpath(fileoutput)
|
||||
except:
|
||||
pass
|
||||
|
||||
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(Operator):
|
||||
bl_idname = "sequencer.create_proxy_operator"
|
||||
bl_label = "Create Proxy"
|
||||
bl_description = ("Use ffmpeg to create a proxy from video\n"
|
||||
"and setup proxies for selected strip")
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
size: IntProperty(
|
||||
name="Proxy Size",
|
||||
default=1
|
||||
)
|
||||
|
||||
@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.preferences
|
||||
proxy_scripts_path = preferences.addons[__package__].preferences.proxy_scripts_path
|
||||
|
||||
for strip in context.selected_editable_sequences:
|
||||
# get resolution from active strip
|
||||
try:
|
||||
bpy.ops.sequencerextra.read_exif()
|
||||
except:
|
||||
pass
|
||||
|
||||
sce = context.scene
|
||||
try:
|
||||
res = sce['metadata'][0]['Composite:ImageSize'].split("x")
|
||||
except:
|
||||
res = (sce.render.resolution_x, sce.render.resolution_y)
|
||||
|
||||
commands = create_proxy(context, strip, self.size, res)
|
||||
|
||||
if commands is None:
|
||||
# Update view_layer
|
||||
context.view_layer.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 view_layer
|
||||
context.view_layer.update()
|
||||
else:
|
||||
create_proxy_scripts(proxy_scripts_path, commands, strip.name)
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class CreateBIProxyOperator(Operator):
|
||||
bl_idname = "sequencer.create_bi_proxy_operator"
|
||||
bl_label = "Create proxy with Blender Internal"
|
||||
bl_description = "Use BI system to create a proxy"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
size: IntProperty(
|
||||
name="Proxy Size",
|
||||
default=1
|
||||
)
|
||||
|
||||
@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):
|
||||
try:
|
||||
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)
|
||||
except Exception as e:
|
||||
functions.error_handlers(
|
||||
self,
|
||||
"sequencer.create_bi_proxy_operator", e,
|
||||
"Create proxy with blender internal"
|
||||
)
|
||||
return {"CANCELLED"}
|
||||
|
||||
# select all strips again
|
||||
for strip in strips:
|
||||
try:
|
||||
strip.select = True
|
||||
except ReferenceError:
|
||||
pass
|
||||
bpy.ops.sequencer.reload()
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class CreateProxyToolPanel(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.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.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(text="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(text="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(text="Create and import proxy from clip:")
|
||||
row = layout.row(align=True)
|
||||
|
||||
layout = self.layout
|
||||
layout.prop(prefs, "ffmpeg_command", text="command")
|
||||
|
||||
layout.label(text="{} = 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")
|
|
@ -1,138 +0,0 @@
|
|||
# ##### 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 #####
|
||||
|
||||
# Note: the Operator LoadRandomEditOperator was removed since is not
|
||||
# working. If it is fixed, reimplemented it can be reintroduced later
|
||||
|
||||
import bpy
|
||||
from bpy.types import (
|
||||
Operator,
|
||||
Panel,
|
||||
)
|
||||
from . import functions
|
||||
|
||||
|
||||
# classes
|
||||
class RandomScratchOperator(Operator):
|
||||
bl_idname = "sequencer.randomscratchoperator"
|
||||
bl_label = "Random Scratch Operator"
|
||||
bl_description = "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.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 is not 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 Markers")
|
||||
bpy.ops.sequencer.reload()
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class RandomEditorPanel(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.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.preferences
|
||||
prefs = preferences.addons[__package__].preferences
|
||||
|
||||
layout = self.layout
|
||||
col = layout.column(align=True)
|
||||
col.label(text="Cut duration:")
|
||||
col.prop(prefs, "random_frames")
|
||||
col.operator("sequencer.randomscratchoperator")
|
|
@ -1,266 +0,0 @@
|
|||
# ##### 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
|
||||
from bpy.types import (
|
||||
Operator,
|
||||
Panel,
|
||||
)
|
||||
from bpy.props import (
|
||||
EnumProperty,
|
||||
BoolProperty,
|
||||
)
|
||||
from . import functions
|
||||
from . import exiftool
|
||||
|
||||
|
||||
class Sequencer_Extra_RecursiveLoader(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 is 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
|
||||
# load exifdata from strip to scene['metadata'] property
|
||||
|
||||
class Sequencer_Extra_ReadExifData(Operator):
|
||||
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
|
||||
strip = context.scene.sequence_editor.active_strip
|
||||
sce['metadata'] = getexifdata(strip)
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
# TODO: fix poll to hide when useless
|
||||
|
||||
class ExifInfoPanel(Panel):
|
||||
"""Creates a Panel in the Object properties window"""
|
||||
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.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(factor=0.5)
|
||||
col = split.column()
|
||||
row = col.row()
|
||||
col.label(text=d)
|
||||
col = split.column()
|
||||
col.label(text=str(sce['metadata'][0][d]))
|
||||
else:
|
||||
for d in sce['metadata'][frame - f]:
|
||||
split = layout.split(factor=0.5)
|
||||
col = split.column()
|
||||
row = col.row()
|
||||
col.label(text=d)
|
||||
col = split.column()
|
||||
col.label(text=str(sce['metadata'][frame - f][d]))
|
||||
|
||||
except (IndexError, KeyError):
|
||||
pass
|
||||
except AttributeError:
|
||||
pass
|
|
@ -1,765 +0,0 @@
|
|||
# ##### 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.types import (
|
||||
Menu,
|
||||
Panel,
|
||||
)
|
||||
from . import functions
|
||||
|
||||
|
||||
# UI
|
||||
class SEQUENCER_EXTRA_MT_input(Menu):
|
||||
bl_label = "Input"
|
||||
|
||||
def draw(self, context):
|
||||
self.layout.label(text="Kinoraw Input")
|
||||
self.layout.separator()
|
||||
|
||||
self.layout.operator_context = "INVOKE_REGION_WIN"
|
||||
self.layout.operator("sequencerextra.striprename",
|
||||
text="File Name to Strip Name")
|
||||
self.layout.operator("sequencerextra.editexternally",
|
||||
text="Open with External Editor")
|
||||
self.layout.operator("sequencerextra.edit",
|
||||
text="Open with Editor")
|
||||
self.layout.operator("sequencerextra.createmovieclip",
|
||||
text="Create Movieclip strip")
|
||||
|
||||
|
||||
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").insert = False
|
||||
self.layout.operator("sequencerextra.placefromfilebrowser",
|
||||
text="Insert from File Browser").insert = True
|
||||
self.layout.operator("sequencerextra.recursiveload",
|
||||
text="Recursive Load from Browser")
|
||||
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')
|
||||
self.layout.separator()
|
||||
self.layout.operator('sequencerextra.selectcurrentframe',
|
||||
text='Before Current Frame').mode = 'BEFORE'
|
||||
self.layout.operator('sequencerextra.selectcurrentframe',
|
||||
text='After Current Frame').mode = 'AFTER'
|
||||
self.layout.operator('sequencerextra.selectcurrentframe',
|
||||
text='On Current Frame').mode = 'ON'
|
||||
self.layout.separator()
|
||||
self.layout.operator('sequencerextra.selectsamechannel',
|
||||
text='Same Channel')
|
||||
|
||||
|
||||
def sequencer_strip_menu_func(self, context):
|
||||
self.layout.operator('sequencerextra.extendtofill',
|
||||
text='Extend to Fill')
|
||||
self.layout.operator_menu_enum('sequencerextra.fadeinout',
|
||||
'mode', text='Fade')
|
||||
self.layout.operator_menu_enum('sequencerextra.copyproperties',
|
||||
'prop')
|
||||
|
||||
self.layout.operator("sequencerextra.insert",
|
||||
text="Insert (Single Channel)").singlechannel = True
|
||||
self.layout.operator("sequencerextra.insert",
|
||||
text="Insert").singlechannel = False
|
||||
self.layout.operator("sequencerextra.ripplecut",
|
||||
text="Ripple Cut")
|
||||
self.layout.operator("sequencerextra.rippledelete",
|
||||
text="Ripple Delete")
|
||||
self.layout.separator()
|
||||
|
||||
|
||||
def time_frame_menu_func(self, context):
|
||||
self.layout.operator('timeextra.trimtimelinetoselection',
|
||||
text='Trim to Selection')
|
||||
self.layout.operator('timeextra.trimtimeline',
|
||||
text='Trim to Timeline Content')
|
||||
self.layout.separator()
|
||||
self.layout.operator('screenextra.frame_skip',
|
||||
text='Skip Forward One Second').back = False
|
||||
self.layout.operator('screenextra.frame_skip',
|
||||
text='Skip Back One Second').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')
|
||||
self.layout.operator('clipextra.openfromfilebrowser',
|
||||
text='Open from File Browser')
|
||||
self.layout.separator()
|
||||
|
||||
|
||||
def draw_color_balance(layout, color_balance):
|
||||
layout = layout.split(factor=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(Panel):
|
||||
bl_space_type = "SEQUENCE_EDITOR"
|
||||
bl_region_type = "UI"
|
||||
bl_label = "Jump to Cut"
|
||||
|
||||
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'}:
|
||||
scn = context.scene
|
||||
preferences = context.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.preferences
|
||||
prefs = preferences.addons[__package__].preferences
|
||||
|
||||
layout = self.layout
|
||||
layout = layout.box()
|
||||
# jump to cut main controls
|
||||
col = layout.column(align=True)
|
||||
row = col.row(align=True)
|
||||
split = row.split(factor=0.33, align=True)
|
||||
box = split.box()
|
||||
|
||||
row = box.row(align=True)
|
||||
row.label(icon='TIME', text="Jump(sec)")
|
||||
row.operator('screenextra.frame_skip',
|
||||
text="", icon='TRIA_LEFT').back = True
|
||||
row.operator('screenextra.frame_skip',
|
||||
text="", icon='TRIA_RIGHT').back = False
|
||||
|
||||
box = split.box()
|
||||
row = box.row(align=True)
|
||||
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
|
||||
|
||||
box = split.box()
|
||||
row = box.row(align=True)
|
||||
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 = col.row(align=True)
|
||||
split = row.split(factor=0.33, align=True)
|
||||
sub_box = split.box()
|
||||
sub_row = sub_box.row(align=True)
|
||||
sub_row.alignment = "CENTER"
|
||||
|
||||
sub_row.operator("screen.frame_jump", text="", icon='REW').end = False
|
||||
sub_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.preferences.system.audio_device == 'JACK':
|
||||
sub = sub_row.row(align=True)
|
||||
sub.scale_x = 2.0
|
||||
sub.operator("screen.animation_play", text="", icon='PLAY')
|
||||
else:
|
||||
sub_row.operator("screen.animation_play", text="",
|
||||
icon='PLAY_REVERSE').reverse = True
|
||||
sub_row.operator("screen.animation_play", text="", icon='PLAY')
|
||||
else:
|
||||
sub = sub_row.row(align=True)
|
||||
sub.scale_x = 2.0
|
||||
sub.operator("screen.animation_play", text="", icon="PAUSE")
|
||||
sub_row.operator("screen.keyframe_jump", text="", icon="NEXT_KEYFRAME").next = True
|
||||
sub_row.operator("screen.frame_jump", text="", icon="FF").end = True
|
||||
|
||||
sub_box = split.box()
|
||||
sub_box.prop(scn, "sync_mode", text="")
|
||||
sub_box = split.box()
|
||||
self.draw_framerate(sub_box, rd)
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.operator("sequencer.refresh_all")
|
||||
row.operator("sequencer.rendersize", text="Set Render size")
|
||||
|
||||
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
|
||||
|
||||
# panel setup
|
||||
row = layout.row(align=True)
|
||||
split = row.split(factor=0.5)
|
||||
sub_row = split.row(align=True)
|
||||
sub_row.prop(prefs, "kr_show_tools", text="Tools", icon='SEQ_SEQUENCER')
|
||||
if prefs.kr_show_tools:
|
||||
sub_row.prop(prefs, "kr_mini_ui", text="Compact UI", toggle=True)
|
||||
|
||||
row = split.row()
|
||||
row = row.split(factor=0.33)
|
||||
row.prop(prefs, "kr_show_info", text="", icon='VIEWZOOM')
|
||||
row = row.split(factor=0.5)
|
||||
row.prop(prefs, "kr_extra_info", text="", icon='BORDERMOVE')
|
||||
row = row.split(factor=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="NONE").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 is True:
|
||||
row.prop(scn, "kr_auto_markers", text="", toggle=True, icon="SPACE2")
|
||||
|
||||
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", toggle=True, icon="SPACE2")
|
||||
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:
|
||||
row = layout.row(align=True)
|
||||
row.label(text="Snap:")
|
||||
|
||||
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(text="Handlers:")
|
||||
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
|
||||
|
||||
box = layout.box()
|
||||
col = box.column_flow(columns=3, align=True)
|
||||
row1 = col.row(align=True)
|
||||
row1.operator("sequencerextra.metacopy", icon="COPYDOWN", text="Meta Copy")
|
||||
row2 = col.row(align=True)
|
||||
row2.operator("sequencerextra.metapaste", icon='PASTEDOWN', text="Paste Snap")
|
||||
row3 = col.row()
|
||||
row3.operator("sequencerextra.meta_separate_trim",
|
||||
text="unMeta & Trim", icon="ALIGN")
|
||||
|
||||
# in /out tools
|
||||
box = layout.box()
|
||||
col = box.column_flow(columns=3, align=True)
|
||||
row1 = col.row(align=True)
|
||||
row1.operator("sequencerextra.sourcein", icon="MARKER_HLT", text="Set IN")
|
||||
row2 = col.row(align=True)
|
||||
row2.operator("sequencerextra.sourceout", icon='MARKER_HLT', text="Set OUT")
|
||||
row3 = col.row()
|
||||
row3.operator("sequencerextra.setinout", icon='ARROW_LEFTRIGHT', text="Selected")
|
||||
|
||||
sub_col = box.split(factor=0.67, align=True)
|
||||
row4 = sub_col.row(align=True)
|
||||
|
||||
if scn.kr_auto_markers is False:
|
||||
row4.prop(scn, "kr_auto_markers",
|
||||
text="Auto Markers", toggle=True, icon="SPACE2")
|
||||
else:
|
||||
row4.prop(scn, "kr_auto_markers", text="", icon="SPACE2")
|
||||
row4.prop(scn, "kr_in_marker")
|
||||
row4.prop(scn, "kr_out_marker")
|
||||
box4 = row4.box()
|
||||
box4.scale_x = 0.25
|
||||
box4.scale_y = 0.5
|
||||
box4.label(text="", icon="BLANK1")
|
||||
|
||||
row5 = sub_col.row()
|
||||
row5.operator("sequencerextra.triminout", icon="FULLSCREEN_EXIT",
|
||||
text="Trim", emboss=True)
|
||||
|
||||
# UI extra actions
|
||||
box = layout.box()
|
||||
row = box.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 = box.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
|
||||
if strip is not None:
|
||||
if prefs.kr_show_info:
|
||||
layout = layout.box()
|
||||
row = layout.split(factor=0.075)
|
||||
row.prop(prefs, "kr_show_info", text="", icon='VIEWZOOM', emboss=True)
|
||||
row = row.split(factor=0.3)
|
||||
row.prop(strip, "type", text="")
|
||||
row = row.split(factor=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(factor=0.8)
|
||||
|
||||
if strip.type == 'MOVIE':
|
||||
row.prop(strip, "filepath", text="")
|
||||
|
||||
if strip.type == 'SOUND':
|
||||
# Note: sound strip has a different structure
|
||||
sound = strip.sound
|
||||
if sound is not None:
|
||||
row.prop(sound, "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()
|
||||
# strip.elements[0] could be a fallback
|
||||
row.prop(elem, "filename", text="File")
|
||||
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(factor=1)
|
||||
row.prop(prefs, "kr_show_trim", text="Trim", toggle=True)
|
||||
if prefs.kr_show_trim:
|
||||
box = layout.box()
|
||||
if not isinstance(strip, bpy.types.EffectSequence):
|
||||
row = box.row(align=True)
|
||||
row.label(text="Hard:")
|
||||
row.prop(strip, "animation_offset_start", text="Start")
|
||||
row.prop(strip, "animation_offset_end", text="End")
|
||||
row = box.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(text="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)
|
||||
|
||||
split = box.split(factor=0.5)
|
||||
left_box = split.box()
|
||||
row = left_box.row()
|
||||
row.prop(strip, "strobe")
|
||||
|
||||
col = left_box.column(align=True)
|
||||
col.separator()
|
||||
col.prop(strip, "use_flip_x", text="Flip X", toggle=True)
|
||||
col.prop(strip, "use_flip_y", text="Flip Y", toggle=True)
|
||||
col.prop(strip, "use_reverse_frames", text="Backwards", toggle=True)
|
||||
col.prop(strip, "use_deinterlace", toggle=True)
|
||||
|
||||
right_box = split.box()
|
||||
col = right_box.column()
|
||||
col.prop(strip, "blend_type", icon='COLOR')
|
||||
col.prop(strip, "alpha_mode")
|
||||
|
||||
col = right_box.column()
|
||||
col.prop(strip, "color_saturation", text="Saturation")
|
||||
col.prop(strip, "color_multiply", text="Multiply")
|
||||
col.prop(strip, "use_float", text="Convert Float")
|
||||
|
||||
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")
|
||||
|
||||
else:
|
||||
# sound type
|
||||
row = box.row()
|
||||
row.prop(prefs, "kr_extra_info", text="", icon='BORDERMOVE', emboss=True)
|
||||
sub = row.row(align=True)
|
||||
sub.prop(strip, "volume")
|
||||
sub.prop(strip, "mute", toggle=True, icon_only=True)
|
||||
sub.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", toggle=True, icon="DISK_DRIVE")
|
||||
|
||||
row.prop(strip, "show_waveform", toggle=True, icon="RNDCURVE")
|
||||
|
||||
row = box.row(align=True)
|
||||
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(factor=0.075)
|
||||
row.prop(prefs, "kr_show_modifiers", text="",
|
||||
icon='RESTRICT_VIEW_OFF', emboss=True)
|
||||
row = row.split(factor=0.40)
|
||||
row.prop(strip, "use_linear_modifiers", text="Linear")
|
||||
row = row.split(factor=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