Adding changes version 1
This commit is contained in:
+140
-55
@@ -3,7 +3,13 @@
|
||||
from pathlib import Path
|
||||
|
||||
from .errors import ValidationError, ValidationIssue
|
||||
from .models import ProjectConfig, SlideDefinition, VideoSource, SLIDE_LAYOUTS
|
||||
from .models import (
|
||||
ProjectConfig,
|
||||
SlideDefinition,
|
||||
VideoSource,
|
||||
SLIDE_LAYOUTS,
|
||||
CAMERA_PRESETS,
|
||||
)
|
||||
|
||||
|
||||
def validate_project(
|
||||
@@ -12,6 +18,7 @@ def validate_project(
|
||||
config: ProjectConfig,
|
||||
slides: dict[str, SlideDefinition],
|
||||
videos: dict[str, VideoSource],
|
||||
videos_dir: Path,
|
||||
malformed_markers: list[tuple[int, str]] = None,
|
||||
) -> None:
|
||||
"""
|
||||
@@ -30,19 +37,59 @@ def validate_project(
|
||||
# Check for malformed markers first (these are likely typos)
|
||||
if malformed_markers:
|
||||
for line_num, marker_text in malformed_markers:
|
||||
issues.append(ValidationIssue(
|
||||
f"Malformed marker: {marker_text}",
|
||||
project_path / "manuscript.txt",
|
||||
line_num
|
||||
))
|
||||
issues.append(
|
||||
ValidationIssue(
|
||||
f"Malformed marker: {marker_text}",
|
||||
project_path / "manuscript.txt",
|
||||
line_num,
|
||||
)
|
||||
)
|
||||
|
||||
# Check all manuscript markers have corresponding slides
|
||||
# Check all manuscript markers have corresponding slides or videos
|
||||
for marker in manuscript_markers:
|
||||
# Skip camera effect markers (Zoom0, TiltLeft, Reset, etc.)
|
||||
if marker in CAMERA_PRESETS:
|
||||
continue
|
||||
# Skip audio markers (start with 'A' followed by audio id, e.g., Awoosh)
|
||||
if marker.startswith("A") and len(marker) > 1 and marker[1:].isalnum():
|
||||
continue
|
||||
# Validate video trigger markers (video:xxx) - slide-like videos
|
||||
if marker.startswith("video:"):
|
||||
video_id = marker[6:] # Remove 'video:' prefix
|
||||
if video_id not in videos:
|
||||
# Check if it's a file extension mismatch
|
||||
hint = ""
|
||||
if "." in video_id:
|
||||
base_name = video_id.rsplit(".", 1)[0]
|
||||
if base_name in videos:
|
||||
hint = f" (Did you mean [video:{base_name}]? Don't include file extensions in markers)"
|
||||
issues.append(
|
||||
ValidationIssue(
|
||||
f"Video marker [{marker}] referenced in manuscript but '{video_id}' not defined in videos.json{hint}",
|
||||
project_path / "manuscript.txt",
|
||||
)
|
||||
)
|
||||
continue
|
||||
|
||||
# Validate narration trigger markers (narration:xxx) - continuous videos
|
||||
if marker.startswith("narration:"):
|
||||
video_id = marker[10:] # Remove 'narration:' prefix
|
||||
if video_id not in videos:
|
||||
issues.append(
|
||||
ValidationIssue(
|
||||
f"Narration marker [{marker}] referenced in manuscript but '{video_id}' not defined in videos.json",
|
||||
project_path / "manuscript.txt",
|
||||
)
|
||||
)
|
||||
continue
|
||||
|
||||
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"
|
||||
))
|
||||
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
|
||||
# Slides are in the same directory as the slides.json file
|
||||
@@ -52,37 +99,68 @@ def validate_project(
|
||||
for slide_id, slide_def in slides.items():
|
||||
image_path = slides_dir / slide_def.image
|
||||
if not image_path.exists():
|
||||
issues.append(ValidationIssue(
|
||||
f"Slide image not found: {slide_def.image}",
|
||||
slides_json_path
|
||||
))
|
||||
issues.append(
|
||||
ValidationIssue(
|
||||
f"Slide image not found: {slide_def.image}", slides_json_path
|
||||
)
|
||||
)
|
||||
|
||||
# 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"
|
||||
))
|
||||
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 (paths relative to videos_dir or shared_assets)
|
||||
videos_json_path = project_path / config.videos_path
|
||||
|
||||
# Find shared_assets directory
|
||||
shared_assets_dir = None
|
||||
if (project_path / "shared_assets").exists():
|
||||
shared_assets_dir = project_path / "shared_assets"
|
||||
elif (project_path.parent / "shared_assets").exists():
|
||||
shared_assets_dir = project_path.parent / "shared_assets"
|
||||
|
||||
# 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"
|
||||
))
|
||||
# Determine base directory based on is_shared flag
|
||||
if video_source.is_shared:
|
||||
if shared_assets_dir:
|
||||
base_dir = shared_assets_dir
|
||||
else:
|
||||
issues.append(
|
||||
ValidationIssue(
|
||||
f"Video '{video_id}' has is_shared=true but shared_assets directory not found",
|
||||
videos_json_path,
|
||||
)
|
||||
)
|
||||
continue
|
||||
else:
|
||||
base_dir = videos_dir
|
||||
|
||||
# Check preprocessed output exists if preprocessing is defined
|
||||
if video_source.preprocess and video_source.output_file:
|
||||
output_path = project_path / video_source.output_file
|
||||
video_path = base_dir / video_source.source_file
|
||||
if not video_path.exists():
|
||||
issues.append(
|
||||
ValidationIssue(
|
||||
f"Video file not found: {video_source.source_file}",
|
||||
videos_json_path,
|
||||
)
|
||||
)
|
||||
|
||||
# Check preprocessed output exists if filters are defined
|
||||
if video_source.filter and video_source.output_file:
|
||||
output_path = base_dir / video_source.output_file
|
||||
if not output_path.exists():
|
||||
issues.append(ValidationIssue(
|
||||
f"Preprocessed output not found: {video_source.output_file}. "
|
||||
f"Run with -a preprocess first.",
|
||||
project_path / "videos.json"
|
||||
))
|
||||
issues.append(
|
||||
ValidationIssue(
|
||||
f"Preprocessed output not found: {video_source.output_file}. "
|
||||
f"Run with -a preprocess first.",
|
||||
videos_json_path,
|
||||
)
|
||||
)
|
||||
|
||||
# Check background exists (image or video)
|
||||
# Try 'background' first, fall back to deprecated 'background_video'
|
||||
@@ -94,38 +172,45 @@ def validate_project(
|
||||
# Try parent directory (shared_assets at repo root)
|
||||
bg_path = project_path.parent / bg_file
|
||||
if not bg_path.exists():
|
||||
issues.append(ValidationIssue(
|
||||
f"Background not found: {bg_file}",
|
||||
project_path / "project.json"
|
||||
))
|
||||
issues.append(
|
||||
ValidationIssue(
|
||||
f"Background not found: {bg_file}", 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"
|
||||
))
|
||||
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"
|
||||
))
|
||||
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"
|
||||
))
|
||||
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"
|
||||
))
|
||||
issues.append(
|
||||
ValidationIssue(
|
||||
f"Invalid FPS: {config.fps} (must be 1-120)",
|
||||
project_path / "project.json",
|
||||
)
|
||||
)
|
||||
|
||||
# If any issues, raise ValidationError
|
||||
if issues:
|
||||
|
||||
Reference in New Issue
Block a user