Blender 5.0 compat + STB XML import + sequence/stamps improvements
- Fix Blender 5.0 API: active_sequence_strip → active_strip, bracket property access on AddonPreferences, _RestrictContext during register() - Fix new_effect() frame_end → length for Blender 5.0 - Fix OTIO adapter kwargs (rate/ignore_timecode_mismatch only for cmx_3600) - Fix OTIO global_start_time RationalTime → int conversion - Add Import STB XML operator with movie strip import and XML patching for Storyboard Pro transitions missing <alignment> - Add Create Sequence Strip operator (select shots → create sequence) - Improve Set Stamps: channel at top, no conflict with existing strips, use raw strip names in templates - TVSHOW episode fixes: sequence loading, episode creation - Kitsu: new_asset, new_episode, admin_connect fix, gazu version pin - Fix escape warnings in file_utils regex patterns - Guard handler registration against duplicates Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+30
-22
@@ -41,17 +41,17 @@ def frame_to_timecode(frame, fps):
|
||||
return timecode
|
||||
|
||||
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,
|
||||
x=0.5, y=0.5, anchor_x='CENTER', anchor_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)
|
||||
strips = bpy.context.scene.sequence_editor.strips
|
||||
strip = strips.new_effect(name, 'TEXT', channel, frame_start=start, length=end - start)
|
||||
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.anchor_y = anchor_y
|
||||
strip.anchor_x = anchor_x
|
||||
strip.channel = channel
|
||||
strip.font_size = font_size
|
||||
|
||||
@@ -74,7 +74,7 @@ def get_strips(channel=0, selected_only=False):
|
||||
if isinstance(channel, str):
|
||||
channel = get_channel_index(channel)
|
||||
|
||||
strips = [s for s in scn.sequence_editor.sequences if s.channel==channel]
|
||||
strips = [s for s in scn.sequence_editor.strips if s.channel==channel]
|
||||
|
||||
if selected_only:
|
||||
strips = [s for s in strips if s.select]
|
||||
@@ -141,7 +141,7 @@ def rename_strips(strips, template, increment=10, start_number=0, padding=3, by_
|
||||
|
||||
name = template.format(**format_data)
|
||||
|
||||
existing_strip = scn.sequence_editor.sequences_all.get(name)
|
||||
existing_strip = scn.sequence_editor.strips_all.get(name)
|
||||
if existing_strip:
|
||||
existing_strip.name = f"{name}_tmp"
|
||||
|
||||
@@ -331,7 +331,7 @@ def import_edit(filepath, adapter="cmx_3600", channel='Shots', match_by='name'):
|
||||
from opentimelineio.schema import Clip
|
||||
|
||||
scn = bpy.context.scene
|
||||
sequencer = scn.sequence_editor.sequences
|
||||
sequencer = scn.sequence_editor.strips
|
||||
strips = get_strips(channel='Shots')
|
||||
shot_channel = get_channel_index('Shots')
|
||||
|
||||
@@ -341,18 +341,26 @@ def import_edit(filepath, adapter="cmx_3600", channel='Shots', match_by='name'):
|
||||
s.channel = empty_channel
|
||||
|
||||
edl = Path(filepath)
|
||||
|
||||
# Extra kwargs only supported by cmx_3600 adapter
|
||||
extra_kwargs = {}
|
||||
if adapter == 'cmx_3600':
|
||||
extra_kwargs = dict(rate=scn.render.fps, ignore_timecode_mismatch=True)
|
||||
|
||||
try:
|
||||
timeline = opentimelineio.adapters.read_from_file(
|
||||
str(edl), adapter, rate=scn.render.fps, ignore_timecode_mismatch=True)
|
||||
str(edl), adapter, **extra_kwargs)
|
||||
except:
|
||||
print("[>.] read_from_file Failed. Using read_from_string method.")
|
||||
data = edl.read_text(encoding='latin-1')
|
||||
timeline = opentimelineio.adapters.read_from_string(
|
||||
data, adapter, rate=scn.render.fps, ignore_timecode_mismatch=True)
|
||||
data, adapter, **extra_kwargs)
|
||||
|
||||
scn.frame_start = (
|
||||
0 if timeline.global_start_time is None else timeline.global_start_time
|
||||
)
|
||||
global_start = timeline.global_start_time
|
||||
if global_start is None:
|
||||
scn.frame_start = 0
|
||||
else:
|
||||
scn.frame_start = int(opentimelineio.opentime.to_frames(global_start))
|
||||
|
||||
# Get all video clips only
|
||||
clips = []
|
||||
@@ -411,7 +419,7 @@ def import_edit(filepath, adapter="cmx_3600", channel='Shots', match_by='name'):
|
||||
type='COLOR',
|
||||
channel=shot_channel,
|
||||
frame_start=frame_start,
|
||||
frame_end=frame_end,
|
||||
length=frame_end - frame_start,
|
||||
)
|
||||
|
||||
strip.blend_alpha = 0.0
|
||||
@@ -439,7 +447,7 @@ def import_movie(filepath, frame_start=None, frame_end=None):
|
||||
if len(relpath.as_posix()) < len(filepath.as_posix()):
|
||||
filepath = relpath
|
||||
|
||||
strip = scn.sequence_editor.sequences.new_movie(
|
||||
strip = scn.sequence_editor.strips.new_movie(
|
||||
name=Path(filepath).stem,
|
||||
filepath=str(filepath),
|
||||
channel=get_channel_index('Movie'),
|
||||
@@ -480,7 +488,7 @@ def import_sound(filepath, frame_start=None, frame_end=None):
|
||||
if len(relpath.as_posix()) < len(filepath.as_posix()):
|
||||
filepath = relpath
|
||||
|
||||
strip = scn.sequence_editor.sequences.new_sound(
|
||||
strip = scn.sequence_editor.strips.new_sound(
|
||||
name=f'{filepath.stem} Audio',
|
||||
filepath=str(filepath),
|
||||
channel=get_channel_index('Audio'),
|
||||
@@ -495,7 +503,7 @@ def import_sound(filepath, frame_start=None, frame_end=None):
|
||||
return strip
|
||||
|
||||
def get_empty_channel():
|
||||
return max(s.channel for s in bpy.context.scene.sequence_editor.sequences) + 1
|
||||
return max(s.channel for s in bpy.context.scene.sequence_editor.strips) + 1
|
||||
|
||||
def clean_sequencer(edit=False, movie=False, sound=False):
|
||||
scn = bpy.context.scene
|
||||
@@ -509,7 +517,7 @@ def clean_sequencer(edit=False, movie=False, sound=False):
|
||||
sequences.extend(get_strips('Audio'))
|
||||
|
||||
for sequence in sequences:
|
||||
scn.sequence_editor.sequences.remove(sequence)
|
||||
scn.sequence_editor.strips.remove(sequence)
|
||||
|
||||
@persistent
|
||||
def set_active_strip(scene):
|
||||
@@ -531,7 +539,7 @@ def set_active_strip(scene):
|
||||
|
||||
@persistent
|
||||
def update_text_strips(scene):
|
||||
if not scene.sequence_editor or not scene.sequence_editor.sequences_all:
|
||||
if not scene.sequence_editor or not scene.sequence_editor.strips_all:
|
||||
return
|
||||
|
||||
#print("update_text_strips")
|
||||
@@ -577,7 +585,7 @@ def update_text_strips(scene):
|
||||
})
|
||||
format_data.update(shot_strip.vsetb_strip_settings.format_data)
|
||||
|
||||
for strip in scene.sequence_editor.sequences_all:
|
||||
for strip in scene.sequence_editor.strips_all:
|
||||
if not strip.type == 'TEXT':
|
||||
continue
|
||||
|
||||
@@ -593,12 +601,12 @@ def update_text_strips(scene):
|
||||
|
||||
def create_shot_strip(name, start, end, channel='Shots'):
|
||||
|
||||
shot_strip = bpy.context.scene.sequence_editor.sequences.new_effect(
|
||||
shot_strip = bpy.context.scene.sequence_editor.strips.new_effect(
|
||||
name,
|
||||
'COLOR',
|
||||
get_channel_index(channel),
|
||||
frame_start=start,
|
||||
frame_end=end
|
||||
length=end - start
|
||||
)
|
||||
shot_strip.blend_alpha = 0
|
||||
shot_strip.color = (0.5, 0.5, 0.5)
|
||||
|
||||
Reference in New Issue
Block a user