Refactor CLI and add preprocessing pipeline

- New CLI structure: -p project, -a action (required flags)
- Add -i import, -f force, -v verbose, --dry-run, --no-cache options
- Add preprocessor.py with chroma key filter (ProRes 4444 output)
- Support background images from shared_assets folder
- Support video metadata JSON files (talkinghead.json)
- Add validation for preprocessed output before render
- Update gnommo.sh with import command and new CLI interface
- Fix Python 3.9 compatibility (Optional[] instead of | None)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-12 15:45:19 +01:00
co-authored by Claude Opus 4.5
parent df900dfd59
commit 93fa820275
9 changed files with 763 additions and 287 deletions
+21 -5
View File
@@ -10,7 +10,7 @@ from .models import (
TimedWord,
VideoSource,
)
from .parser import get_video_duration
from .parser import get_video_duration, resolve_video_file
def build_render_plan(
@@ -26,12 +26,27 @@ def build_render_plan(
This transforms transcript markers into timed slide events and
assembles all information needed for the render stage.
"""
# For POC: use the first video as the talking head
talking_head_id = next(iter(videos.keys()))
talking_head = videos[talking_head_id]
# Determine talking head source:
# 1. If config.talking_head.file is set, use that (may be JSON metadata)
# 2. Otherwise, use first video from videos.json
if config.talking_head.file:
video_path, metadata = resolve_video_file(project_path, config.talking_head.file)
# Create a VideoSource from the resolved metadata
if metadata:
talking_head = VideoSource(
file=str(video_path.relative_to(project_path)) if video_path.is_relative_to(project_path) else str(video_path),
preprocess=metadata.preprocess,
output_file=metadata.output.get("file") if metadata.output else None,
)
else:
talking_head = VideoSource(file=config.talking_head.file)
else:
# Fall back to first video in videos.json
talking_head_id = next(iter(videos.keys()))
talking_head = videos[talking_head_id]
video_path = project_path / talking_head.file
# Get video duration for end time calculations
video_path = project_path / talking_head.file
total_duration = get_video_duration(video_path)
# Build slide events from transcript markers
@@ -49,6 +64,7 @@ def build_render_plan(
total_duration=total_duration,
slides=slides,
slides_dir=slides_dir,
talking_head_path=video_path,
)