vse_toolbox/sequencer_utils.py

331 lines
9.8 KiB
Python

# SPDX-License-Identifier: GPL-2.0-or-later
import re
from pathlib import Path
import bpy
from bpy.app.handlers import persistent
from vse_toolbox.bl_utils import get_scene_settings, get_strip_settings
from vse_toolbox.constants import SOUND_SUFFIXES
def new_text_strip(name='Text', channel=0, start=0, end=50, text='Text', font_size=48,
x=0.5, y=0.5, align_x='CENTER', align_y='CENTER', select=False,
box_color=None, box_margin=0.005):
sequences = bpy.context.scene.sequence_editor.sequences
strip = sequences.new_effect(name, 'TEXT', channel, frame_start=start, frame_end=end)
strip.select = select
strip.text = text
strip.location.x = x
strip.location.y = y
strip.align_y = align_y
strip.align_x = align_x
strip.channel = channel
strip.font_size = font_size
if box_color:
strip.use_box = True
strip.box_color = box_color
strip.box_margin = box_margin
return strip
def is_strip_at(strip, frame=None):
if frame is None:
frame = bpy.context.scene.frame_current
return (strip.frame_final_start <= frame < strip.frame_final_end)
def get_strips(channel=0, selected_only=False):
scn = bpy.context.scene
if isinstance(channel, str):
channel = get_channel_index(channel)
strips = [s for s in scn.sequence_editor.sequences_all if s.channel==channel]
if selected_only:
strips = [s for s in strips if s.select]
return sorted(strips, key=lambda x : x.frame_final_start)
def get_strip_at(channel=0, frame=None):
strips = get_strips(channel=channel)
return next((s for s in strips if is_strip_at(s, frame)), None)
def get_channel_index(name):
scn = bpy.context.scene
channel_id = 0
channel = scn.sequence_editor.channels.get(name)
if channel:
channel_id = scn.sequence_editor.channels.keys().index(name)
return channel_id
def get_channel_name(strip):
if not strip:
return
scn = bpy.context.scene
return scn.sequence_editor.channels[strip.channel].name
def get_strip_sequence_name(strip):
sequence_strip = get_strip_at(channel='Sequences', frame=strip.frame_final_start)
if sequence_strip:
return sequence_strip.name
else:
return 'NoSequence'
def rename_strips(
strips, template, increment=10, start_number=0, by_sequence=False):
scn = bpy.context.scene
settings = get_scene_settings()
project = settings.active_project
episode_name = ''
if settings.active_episode:
episode_name = settings.active_episode.name
prev_sequence_name = None
strip_number = 0
for strip in strips:
sequence_name = get_strip_sequence_name(strip)
if (by_sequence and prev_sequence_name and
sequence_name and sequence != prev_sequence_name):
strip_number = 0
name = template.format(
sequence=sequence_name,
episode=episode_name,
index=strip_number*increment+start_number
)
existing_strip = scn.sequence_editor.sequences_all.get(name)
if existing_strip:
existing_strip.name = f"{name}_tmp"
print(f'Renaming {strip.name} -> {name}')
strip.name = name
prev_sequence_name = sequence_name
strip_number += 1
def set_channels():
scn = bpy.context.scene
settings = get_scene_settings()
items = settings.rna_type.bl_rna.properties['channel'].enum_items
for i, c in enumerate(items.keys(), start=1):
scn.sequence_editor.channels[i].name = c.title()
def get_strip_render_path(strip, template):
return template.format(strip_name=strip.name, ext=Path(scn.render.frame_path()).suffix)
def render_strips(strips, template):
scn = bpy.context.scene
scene_start = scn.frame_start
scene_end = scn.frame_end
for strip in strips:
#print(render_template, strip.name, path)
scn.frame_start = strip.frame_final_start
scn.frame_end = strip.frame_final_end - 1
## render animatic
#strip_render_path = render_template.format(strip_name=strip.name, ext=Path(scn.render.frame_path()).suffix)
scn.render.filepath = get_strip_render_path(strip, template)
#print(scn.render.filepath)
print(f'Render Strip to {scn.render.filepath}')
#bpy.ops.render.render(animation=True)
bpy.ops.render.opengl(animation=True, sequencer=True)
scn.frame_start = scene_start
scn.frame_end = scene_end
def import_edit(filepath, adapter="cmx_3600", clean_sequencer=False):
import opentimelineio as otio
from opentimelineio.schema import (
Clip,
ExternalReference,
Gap,
ImageSequenceReference,
Stack,
Timeline,
Track,
)
scn = bpy.context.scene
sequences = scn.sequence_editor.sequences
if clean_sequencer:
movie = get_strips(channel='Movie')
audio = get_strips(channel='Audio')
for strip in sequences:
if strip not in (movie+audio):
sequences.remove(strip)
edl = Path(filepath)
try:
timeline = otio.adapters.read_from_file(
str(edl), adapter, rate=scn.render.fps, ignore_timecode_mismatch=True)
except:
print("[>.] read_from_file Failed. Using read_from_string method.")
data = edl.read_text(encoding='latin-1')
timeline = otio.adapters.read_from_string(
data, adapter, rate=scn.render.fps, ignore_timecode_mismatch=True)
scn.frame_start = (
0 if timeline.global_start_time is None else timeline.global_start_time
)
for track in timeline.tracks:
for child in track.each_child(shallow_search=True):
# FIXME Exclude Gaps for now. Gaps are Transitions, Blank Spaces...
if not isinstance(child, Clip):
continue
# FIXME Exclude Audio for now
if any(child.name.lower().endswith(ext) for ext in SOUND_SUFFIXES):
channel = get_channel_index('Audio')
continue
channel = get_channel_index('Shots')
frame_start = otio.opentime.to_frames(
child.range_in_parent().start_time)
frame_end = frame_start + otio.opentime.to_frames(
child.range_in_parent().duration)
try:
strip = sequences.new_effect(
name=child.name,
type='COLOR',
channel=channel,
frame_start=frame_start,
frame_end=frame_end,
)
strip.blend_alpha = 0.0
strip.select = False
strip.vsetb_strip_settings.source_name = child.name
except Exception as e:
print('e: ', e)
continue
scn.frame_end = frame_end-1
return timeline
def import_movie(filepath):
scn = bpy.context.scene
res_x = scn.render.resolution_x
res_y = scn.render.resolution_y
strip = scn.sequence_editor.sequences.new_movie(
name=filepath.stem,
filepath=str(filepath),
channel=get_channel_index('Movie'),
frame_start=scn.frame_start
)
elem = strip.strip_elem_from_frame(scn.frame_current)
src_width, src_height = elem.orig_width, elem.orig_height
if src_width != res_x:
strip.transform.scale_x = (res_x / src_width)
if src_height != res_y:
strip.transform.scale_y = (res_y / src_height)
if bpy.data.is_saved:
strip.filepath = bpy.path.relpath(str(filepath))
return strip
def import_sound(filepath):
scn = bpy.context.scene
strip = scn.sequence_editor.sequences.new_sound(
name=filepath.stem,
filepath=str(filepath),
channel=get_channel_index('Audio'),
frame_start=scn.frame_start
)
if bpy.data.is_saved:
strip.sound.filepath = bpy.path.relpath(str(filepath))
strip.show_waveform = True
return strip
def clean_sequencer(edit=False, movie=False, sound=False):
scn = bpy.context.scene
sequences = []
if edit:
sequences.extend(get_strips('Shots'))
if movie:
sequences.extend(get_strips('Movie'))
if sound:
sequences.extend(get_strips('Audio'))
for sequence in sequences:
scn.sequence_editor.sequences.remove(sequence)
@persistent
def set_active_strip(scene):
#scn = bpy.context.scene
settings = get_scene_settings()
if not settings.auto_select_strip:
return
bpy.ops.sequencer.select_all(action="DESELECT")
#scene.sequence_editor.active_strip = None
shot_strip = get_strip_at('Shots')
if shot_strip:
shot_strip.select = True
scene.sequence_editor.active_strip = shot_strip
@persistent
def update_text_strips(scene):
#scn = bpy.context.scene
format_data = {
'scene': scene,
'active_shot_name': 'None',
'active_shot_frame': 0,
'active_shot_duration': 0,
'active_shot_start': 0,
'active_shot_end': 0
}
shot_strip = get_strip_at('Shots', frame=scene.frame_current)
if shot_strip:
format_data.update({
'active_shot_name': shot_strip.name,
'active_shot_duration': shot_strip.frame_final_duration,
'active_shot_frame': scene.frame_current - shot_strip.frame_final_start + 1,
'active_shot_start': shot_strip.frame_final_start,
'active_shot_end': shot_strip.frame_final_end,
})
for strip in scene.sequence_editor.sequences_all:
if not strip.type == 'TEXT':
continue
if not is_strip_at(strip):
continue
if '{' in strip.text:
strip['text_pattern'] = strip.text
if 'text_pattern' in strip.keys():
strip.text = strip['text_pattern'].format(**format_data)