# SPDX-License-Identifier: GPL-2.0-or-later import re from pathlib import Path import os 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 import multiprocessing 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): scn = bpy.context.scene suffix = Path(scn.render.frame_path()).suffix render_path = template.format(strip_name=strip.name, ext=suffix[1:]) return Path(os.path.abspath(bpy.path.abspath(render_path))) def render_strips(strips, template): scn = bpy.context.scene scene_start = scn.frame_start scene_end = scn.frame_end render_path = scn.render.filepath # pool = multiprocessing.Pool(4) # p.map(func, range(1, 100)) # def render_strip_background(index): # cmd = [bpy.app.binary_path, etc] # process = subprocess.Popen(substr + " --index {}".format(index), shell=True, stdout=subprocess.PIPE) 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 = str(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 scn.render.filepath = render_path 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)