Adding pexels downloader and fixes
This commit is contained in:
+65
-17
@@ -4,7 +4,7 @@ from pathlib import Path
|
||||
|
||||
from .cache import resolve_with_cache
|
||||
from .errors import ValidationError, ValidationIssue
|
||||
from .parser import _read_json
|
||||
from .parser import _read_json, resolve_missing_videos
|
||||
from .models import (
|
||||
ProjectConfig,
|
||||
SlideDefinition,
|
||||
@@ -38,6 +38,24 @@ def validate_project(
|
||||
issues: list[ValidationIssue] = []
|
||||
warnings: list[ValidationIssue] = []
|
||||
|
||||
# Collect video IDs actually referenced in the manuscript (for file-existence checks)
|
||||
_VIDEO_PREFIXES = {
|
||||
"video:": 6,
|
||||
"vft:": 4, "vfb:": 4, "vfm:": 4,
|
||||
"vf2t:": 5, "vf2b:": 5, "vf2m:": 5,
|
||||
"vst:": 4, "vsb:": 4, "vsm:": 4,
|
||||
"vftp:": 5, "vfbp:": 5, "vfmp:": 5,
|
||||
"vf2tp:": 6, "vf2bp:": 6, "vf2mp:": 6,
|
||||
"vstp:": 5, "vsbp:": 5, "vsmp:": 5,
|
||||
}
|
||||
referenced_video_ids: set[str] = set()
|
||||
for marker in manuscript_markers:
|
||||
prefix = next((p for p in _VIDEO_PREFIXES if marker.startswith(p)), None)
|
||||
if prefix is not None:
|
||||
referenced_video_ids.add(marker[_VIDEO_PREFIXES[prefix]:])
|
||||
elif marker.startswith("narration:"):
|
||||
referenced_video_ids.add(marker[10:])
|
||||
|
||||
# Check for malformed markers first (these are likely typos)
|
||||
if malformed_markers:
|
||||
for line_num, marker_text in malformed_markers:
|
||||
@@ -62,21 +80,6 @@ def validate_project(
|
||||
continue
|
||||
# Validate video trigger markers — both legacy [video:xxx] and
|
||||
# shorthand [vft:xxx] / [vfb:xxx] / [vst:xxx] / [vsb:xxx].
|
||||
_VIDEO_PREFIXES = {
|
||||
"video:": 6,
|
||||
"vft:": 4,
|
||||
"vfb:": 4,
|
||||
"vf2t:": 5,
|
||||
"vf2b:": 5,
|
||||
"vst:": 4,
|
||||
"vsb:": 4,
|
||||
"vftp:": 5,
|
||||
"vfbp:": 5,
|
||||
"vf2tp:": 6,
|
||||
"vf2bp:": 6,
|
||||
"vstp:": 5,
|
||||
"vsbp:": 5,
|
||||
}
|
||||
matched_prefix = next(
|
||||
(p for p in _VIDEO_PREFIXES if marker.startswith(p)), None
|
||||
)
|
||||
@@ -94,6 +97,16 @@ def validate_project(
|
||||
project_path / "manuscript.txt",
|
||||
)
|
||||
)
|
||||
else:
|
||||
vs = videos[video_id]
|
||||
if not vs.cutout or vs.cutout not in config.cutouts:
|
||||
warnings.append(
|
||||
ValidationIssue(
|
||||
f"[{marker}] video '{video_id}' has no valid cutout in videos.json — "
|
||||
f"run 'gnommo import' to project values, or set cutout manually.",
|
||||
project_path / "manuscript.txt",
|
||||
)
|
||||
)
|
||||
continue
|
||||
|
||||
# Validate narration trigger markers (narration:xxx) - continuous videos
|
||||
@@ -106,6 +119,16 @@ def validate_project(
|
||||
project_path / "manuscript.txt",
|
||||
)
|
||||
)
|
||||
else:
|
||||
vs = videos[video_id]
|
||||
if not vs.cutout or vs.cutout not in config.cutouts:
|
||||
warnings.append(
|
||||
ValidationIssue(
|
||||
f"[{marker}] video '{video_id}' has no valid cutout in videos.json — "
|
||||
f"run 'gnommo import' to project values, or set cutout manually.",
|
||||
project_path / "manuscript.txt",
|
||||
)
|
||||
)
|
||||
continue
|
||||
|
||||
# Segment markers are structural annotations, not slide references
|
||||
@@ -168,6 +191,10 @@ def validate_project(
|
||||
shared_assets_dir = project_path.parent / "shared_assets"
|
||||
|
||||
for video_id, video_source in videos.items():
|
||||
# Only check files for videos actually used in this manuscript
|
||||
if video_id not in referenced_video_ids:
|
||||
continue
|
||||
|
||||
# Determine base directory based on is_shared flag
|
||||
if video_source.is_shared:
|
||||
if shared_assets_dir:
|
||||
@@ -186,9 +213,15 @@ def validate_project(
|
||||
video_path = base_dir / video_source.source_file
|
||||
video_path, _ = resolve_with_cache(video_path, project_path)
|
||||
if not video_path.exists():
|
||||
sf = video_source.source_file
|
||||
hint = (
|
||||
" — run 'gnommo pexels' to download"
|
||||
if sf.startswith("pexels/")
|
||||
else " — falling back to PlaceholderVideo"
|
||||
)
|
||||
warnings.append(
|
||||
ValidationIssue(
|
||||
f"Video file not found: {video_source.source_file} — falling back to PlaceholderVideo",
|
||||
f"Video file not found: {sf}{hint}",
|
||||
videos_json_path,
|
||||
)
|
||||
)
|
||||
@@ -229,6 +262,7 @@ def validate_project(
|
||||
)
|
||||
else:
|
||||
bg_path = shared_assets_dir / bg_videos[bg_handle]["source_file"]
|
||||
bg_path, _ = resolve_with_cache(bg_path, project_path)
|
||||
if not bg_path.exists():
|
||||
issues.append(
|
||||
ValidationIssue(
|
||||
@@ -272,6 +306,20 @@ def validate_project(
|
||||
)
|
||||
)
|
||||
|
||||
# Check outro videos exist in videos.json or shared_assets
|
||||
if config.outro:
|
||||
missing_outro = [vid_id for vid_id in config.outro if vid_id not in videos]
|
||||
if missing_outro:
|
||||
found = resolve_missing_videos(missing_outro, project_path, config)
|
||||
still_missing = [vid_id for vid_id in missing_outro if vid_id not in found]
|
||||
for vid_id in still_missing:
|
||||
warnings.append(
|
||||
ValidationIssue(
|
||||
f"Outro video '{vid_id}' not found in videos.json or shared_assets — will be skipped at render",
|
||||
project_path / "project.json",
|
||||
)
|
||||
)
|
||||
|
||||
# If any issues, raise ValidationError
|
||||
if issues:
|
||||
raise ValidationError(issues)
|
||||
|
||||
Reference in New Issue
Block a user