Adding handoff functionality for reviews
This commit is contained in:
+58
-11
@@ -442,6 +442,7 @@ def build_render_plan(
|
||||
audio: Optional[dict[str, AudioDefinition]] = None,
|
||||
audio_dir: Optional[Path] = None,
|
||||
slide_range: Optional[tuple[str, Optional[str]]] = None,
|
||||
proxy: Optional[bool] = False,
|
||||
) -> tuple[RenderPlan, list[MarkerTiming]]:
|
||||
"""
|
||||
Build a complete render plan from manuscript and transcription.
|
||||
@@ -461,9 +462,15 @@ def build_render_plan(
|
||||
audio_dir = audio_dir or project_path
|
||||
|
||||
# Find the main narration video first (need skip value for timing adjustment)
|
||||
narration_video_id = config.main_video
|
||||
narration_video_id = "narration_combined.mov" # Default narration video ID
|
||||
# Handle legacy list format - use first element
|
||||
if isinstance(narration_video_id, list):
|
||||
narration_video_id = narration_video_id[0] if narration_video_id else None
|
||||
if not (narration_video_id and narration_video_id in videos):
|
||||
raise ValueError("Main video not specified or not found in videos.")
|
||||
raise ValueError(
|
||||
f"Main video '{narration_video_id}' not specified or not found in videos. "
|
||||
f"Available: {list(videos.keys())}"
|
||||
)
|
||||
narration_video = videos[narration_video_id]
|
||||
|
||||
# Align markers to transcription timestamps
|
||||
@@ -495,8 +502,13 @@ def build_render_plan(
|
||||
narration_video = videos[narration_video_id]
|
||||
cutout = config.cutouts[narration_video.cutout]
|
||||
|
||||
# Track which files are loaded from external cache
|
||||
cached_files: set[str] = set()
|
||||
|
||||
narration_videos: list[tuple[str, VideoSource, CutoutDefinition]] = []
|
||||
video_path = _resolve_video_path(videos_dir, narration_video, shared_assets_dir)
|
||||
video_path, is_cached = _resolve_video_path(videos_dir, narration_video, shared_assets_dir, project_path)
|
||||
if is_cached:
|
||||
cached_files.add(narration_video_id)
|
||||
full_duration = get_video_duration(video_path)
|
||||
# Adjust duration for skip (content starts at skip, so effective duration is less)
|
||||
effective_duration = full_duration - narration_skip
|
||||
@@ -536,6 +548,14 @@ def build_render_plan(
|
||||
time_range=(time_offset, render_end_time) if slide_range else None,
|
||||
)
|
||||
|
||||
# Track cached files for triggered videos
|
||||
for event in video_events:
|
||||
_, is_cached = _resolve_video_path(
|
||||
videos_dir, event.video_source, shared_assets_dir, project_path
|
||||
)
|
||||
if is_cached:
|
||||
cached_files.add(event.video_id)
|
||||
|
||||
audio_events = _extract_audio_events(
|
||||
marker_timings,
|
||||
audio,
|
||||
@@ -622,6 +642,8 @@ def build_render_plan(
|
||||
total_duration,
|
||||
videos_dir,
|
||||
shared_assets_dir,
|
||||
project_path,
|
||||
cached_files,
|
||||
)
|
||||
|
||||
# Update total duration to include outro
|
||||
@@ -654,6 +676,7 @@ def build_render_plan(
|
||||
narration_pauses=narration_pauses,
|
||||
outro_events=outro_events,
|
||||
narration_end_time=narration_end_time,
|
||||
cached_files=cached_files,
|
||||
)
|
||||
|
||||
return plan, marker_timings
|
||||
@@ -663,8 +686,16 @@ def _resolve_video_path(
|
||||
videos_dir: Path,
|
||||
video_source: VideoSource,
|
||||
shared_assets_dir: Path = None,
|
||||
) -> Path:
|
||||
"""Resolve the actual video file path."""
|
||||
project_path: Path = None,
|
||||
) -> tuple[Path, bool]:
|
||||
"""Resolve the actual video file path with cache fallback.
|
||||
|
||||
Returns:
|
||||
Tuple of (resolved_path, is_cached) where is_cached=True if
|
||||
the file was found in the external cache.
|
||||
"""
|
||||
from .cache import resolve_with_cache
|
||||
|
||||
if video_source.is_shared and shared_assets_dir:
|
||||
base_dir = shared_assets_dir
|
||||
else:
|
||||
@@ -672,12 +703,24 @@ def _resolve_video_path(
|
||||
|
||||
if video_source.output_file:
|
||||
video_path = base_dir / video_source.output_file
|
||||
if video_path.exists():
|
||||
return video_path
|
||||
if project_path:
|
||||
resolved, is_cached = resolve_with_cache(video_path, project_path)
|
||||
if resolved.exists():
|
||||
return resolved, is_cached
|
||||
elif video_path.exists():
|
||||
return video_path, False
|
||||
webm_path = video_path.with_suffix(".mov")
|
||||
if webm_path.exists():
|
||||
return webm_path
|
||||
return base_dir / video_source.source_file
|
||||
if project_path:
|
||||
resolved, is_cached = resolve_with_cache(webm_path, project_path)
|
||||
if resolved.exists():
|
||||
return resolved, is_cached
|
||||
elif webm_path.exists():
|
||||
return webm_path, False
|
||||
|
||||
source_path = base_dir / video_source.source_file
|
||||
if project_path:
|
||||
return resolve_with_cache(source_path, project_path)
|
||||
return source_path, False
|
||||
|
||||
|
||||
def _extract_slide_events(
|
||||
@@ -932,6 +975,8 @@ def _extract_outro_events(
|
||||
narration_end_time: float,
|
||||
videos_dir: Path,
|
||||
shared_assets_dir: Path = None,
|
||||
project_path: Path = None,
|
||||
cached_files: set = None,
|
||||
) -> list[OutroEvent]:
|
||||
"""
|
||||
Extract outro events that play after the narration ends.
|
||||
@@ -949,7 +994,9 @@ def _extract_outro_events(
|
||||
video_source = videos[video_id]
|
||||
|
||||
# Get the video duration
|
||||
video_path = _resolve_video_path(videos_dir, video_source, shared_assets_dir)
|
||||
video_path, is_cached = _resolve_video_path(videos_dir, video_source, shared_assets_dir, project_path)
|
||||
if is_cached and cached_files is not None:
|
||||
cached_files.add(video_id)
|
||||
if video_path.exists():
|
||||
full_duration = get_video_duration(video_path)
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user