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:
2026-03-10 16:09:03 +01:00
co-authored by Claude Opus 4.6
parent c46d78f2ae
commit d2d64dc8a8
14 changed files with 548 additions and 122 deletions
+34 -10
View File
@@ -148,18 +148,27 @@ class VSETB_OT_load_projects(Operator):
episode_datas = tracker.get_episodes(project_data)
for episode_data in episode_datas:
episode = project.episodes.get(episode_data['name'])
if not episode:
episode = project.episodes.add()
episode.name = episode_data['name']
episode.id = episode_data['id']
# Clear deleted episodes
ep_names = [e['name'] for e in episode_datas]
for ep in reversed(project.episodes):
if ep.name not in ep_names:
project.episodes.remove(list(project.episodes).index(ep))
# Load sequences for first episode so they're available immediately
if project.episodes:
first_episode = project.episodes[0]
project.sequences.clear()
for seq in tracker.get_sequences(project_data, episode=first_episode.id):
sequence = project.sequences.add()
sequence.name = seq['name']
sequence.id = seq['id']
else:
# Add sequences
sequences_data = tracker.get_sequences(project_data)
@@ -257,15 +266,24 @@ class VSETB_OT_new_episode(Operator):
settings = get_scene_settings()
prefs = get_addon_prefs()
tracker = prefs.tracker
project = settings.active_project
episode_name = settings.episode_template.format(index=int(self.episode_name))
episode = tracker.get_episode(episode_name)
if episode:
if not project:
self.report({'ERROR'}, 'No active project')
return {"CANCELLED"}
episode_name = project.episode_template.format(index=int(self.episode_name))
episode_data = tracker.get_episode(episode_name, project=project.id)
if episode_data:
self.report({'ERROR'}, f'Episode {episode_name} already exists')
return {"CANCELLED"}
tracker.new_episode(episode_name)
tracker.update_project()
episode_data = tracker.new_episode(episode_name, project=project.id)
# Add to local episode collection
episode = project.episodes.add()
episode.name = episode_data['name']
episode.id = episode_data['id']
self.report({'INFO'}, f'Episode {episode_name} successfully created')
@@ -355,6 +373,9 @@ class VSETB_OT_upload_to_tracker(Operator):
context.window_manager.progress_update(i)
strip_settings = strip.vsetb_strip_settings
sequence_name = get_strip_sequence_name(strip)
if sequence_name == 'NoSequence':
self.report({'WARNING'}, f"Strip '{strip.name}' has no sequence — skipped")
continue
shot_name = strip.name
sequence = tracker.get_sequence(sequence_name, episode=episode)
metadata = strip_settings.metadata.to_dict(use_name=False)
@@ -401,6 +422,7 @@ class VSETB_OT_upload_to_tracker(Operator):
preview = None
comment_data = None
preview_data = None
if status or upload_to_tracker.comment or preview:
comment_data = tracker.new_comment(task, comment=upload_to_tracker.comment, status=status)
if preview:
@@ -410,7 +432,7 @@ class VSETB_OT_upload_to_tracker(Operator):
comment=comment_data,
preview=preview)
if upload_to_tracker.set_main_preview:
if upload_to_tracker.set_main_preview and preview_data:
bpy.app.timers.register(partial(tracker.set_main_preview, preview_data), first_interval=10)
params = {}
@@ -434,7 +456,9 @@ class VSETB_OT_upload_to_tracker(Operator):
task = getattr(strip_settings.tasks, norm_name(task_type.name))
tracker_task = tracker.get_task(task_type.name, entity=shot)
if task.comment and tracker_task.get('last_comment') != task.comment:
last_comment = tracker_task.get('last_comment')
last_comment_text = last_comment.get('text', '') if isinstance(last_comment, dict) else str(last_comment or '')
if task.comment and last_comment_text != task.comment:
tracker.new_comment(tracker_task, comment=task.comment)
context.window_manager.progress_end()