Initial commit: GnommoEditor video pipeline
A code-first, declarative video editing system that compiles text documents into rendered video via FFmpeg. Uses a compiler-style ETL pipeline: Extract (parse inputs) → Validate → Transform (build timeline) → Render (FFmpeg). Features: - Text-based project definition (manuscript, transcript, JSON configs) - Slide markers [S1], [S2] in transcript map to timed overlays - Strict validation with fail-fast error reporting - FFmpeg filter_complex generation with time-based enables - CLI with validate/render/dry-run modes Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
"""Validation stage: fail-fast checks on parsed data."""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from .errors import ValidationError, ValidationIssue
|
||||
from .models import ProjectConfig, SlideDefinition, VideoSource, SLIDE_LAYOUTS
|
||||
|
||||
|
||||
def validate_project(
|
||||
project_path: Path,
|
||||
manuscript_markers: list[str],
|
||||
config: ProjectConfig,
|
||||
slides: dict[str, SlideDefinition],
|
||||
videos: dict[str, VideoSource],
|
||||
) -> None:
|
||||
"""
|
||||
Validate all parsed project data. Raises ValidationError if any issues found.
|
||||
|
||||
Checks:
|
||||
- All slide markers in manuscript exist in slides.json
|
||||
- All slide images exist on disk
|
||||
- All video files exist on disk
|
||||
- Background video exists (if specified)
|
||||
- Slide types are valid
|
||||
"""
|
||||
issues: list[ValidationIssue] = []
|
||||
|
||||
# Check all manuscript markers have corresponding slides
|
||||
for marker in manuscript_markers:
|
||||
if marker not in slides:
|
||||
issues.append(ValidationIssue(
|
||||
f"Slide marker [{marker}] referenced in manuscript but not defined in slides.json",
|
||||
project_path / "manuscript.txt"
|
||||
))
|
||||
|
||||
# Check all slide images exist
|
||||
media_path = project_path / "media"
|
||||
slides_path = media_path / "slides"
|
||||
|
||||
for slide_id, slide_def in slides.items():
|
||||
image_path = slides_path / slide_def.image
|
||||
if not image_path.exists():
|
||||
issues.append(ValidationIssue(
|
||||
f"Slide image not found: {slide_def.image}",
|
||||
project_path / "slides.json"
|
||||
))
|
||||
|
||||
# Check slide type is valid
|
||||
if slide_def.type not in SLIDE_LAYOUTS:
|
||||
issues.append(ValidationIssue(
|
||||
f"Unknown slide type '{slide_def.type}' for slide {slide_id}. "
|
||||
f"Valid types: {list(SLIDE_LAYOUTS.keys())}",
|
||||
project_path / "slides.json"
|
||||
))
|
||||
|
||||
# Check all video files exist
|
||||
for video_id, video_source in videos.items():
|
||||
video_path = project_path / video_source.file
|
||||
if not video_path.exists():
|
||||
issues.append(ValidationIssue(
|
||||
f"Video file not found: {video_source.file}",
|
||||
project_path / "videos.json"
|
||||
))
|
||||
|
||||
# Check background video exists (if specified)
|
||||
if config.background_video:
|
||||
bg_path = project_path / config.background_video
|
||||
if not bg_path.exists():
|
||||
issues.append(ValidationIssue(
|
||||
f"Background video not found: {config.background_video}",
|
||||
project_path / "project.json"
|
||||
))
|
||||
|
||||
# Check we have at least one video source
|
||||
if not videos:
|
||||
issues.append(ValidationIssue(
|
||||
"No video sources defined in videos.json",
|
||||
project_path / "videos.json"
|
||||
))
|
||||
|
||||
# Check resolution is reasonable
|
||||
width, height = config.resolution
|
||||
if width < 100 or height < 100:
|
||||
issues.append(ValidationIssue(
|
||||
f"Resolution too small: {width}x{height}",
|
||||
project_path / "project.json"
|
||||
))
|
||||
|
||||
if width > 7680 or height > 4320:
|
||||
issues.append(ValidationIssue(
|
||||
f"Resolution too large: {width}x{height} (max 8K)",
|
||||
project_path / "project.json"
|
||||
))
|
||||
|
||||
# Check FPS is reasonable
|
||||
if config.fps < 1 or config.fps > 120:
|
||||
issues.append(ValidationIssue(
|
||||
f"Invalid FPS: {config.fps} (must be 1-120)",
|
||||
project_path / "project.json"
|
||||
))
|
||||
|
||||
# If any issues, raise ValidationError
|
||||
if issues:
|
||||
raise ValidationError(issues)
|
||||
Reference in New Issue
Block a user