This commit is contained in:
2026-03-15 11:13:09 +01:00
parent 6949124fa7
commit 757d966803
5 changed files with 20 additions and 5 deletions
+4 -3
View File
@@ -911,14 +911,15 @@ def cmd_stitch(
narration, narration,
stitch_output, stitch_output,
verbose=verbose, verbose=verbose,
default_end_trim=config.default_end_trim if config else 0.0,
) )
# Run import videos again, because at this point narration_combined might have been created. # Run import videos again, because at this point narration_combined might have been created.
_import_videos(videos_dir, config, verbose) _import_videos(videos_dir, config, verbose)
# Always update the MAIN videos.json (parent directory when in proxy mode) # Always update the MAIN videos.json (parent of subdir when using low/tiny res)
# Proxy mode only affects file paths, not JSON metadata updates # Downscaled dirs only affect file paths, not JSON metadata updates
main_videos_dir = videos_dir.parent if proxy else videos_dir main_videos_dir = videos_dir.parent if res != "full" else videos_dir
videos_json_path = main_videos_dir / "videos.json" videos_json_path = main_videos_dir / "videos.json"
if True: # Always update JSON regardless of proxy mode if True: # Always update JSON regardless of proxy mode
existing_videos: dict = {} existing_videos: dict = {}
+2
View File
@@ -56,6 +56,8 @@ class ProjectConfig:
gnommo_scratch: Optional[ gnommo_scratch: Optional[
str str
] = None # directory for intermediate files (e.g., external SSD) ] = None # directory for intermediate files (e.g., external SSD)
default_begin: float = 0.0 # Trim this many seconds from the start of each segment (if no explicit begin/skip)
default_end_trim: float = 0.0 # Trim this many seconds from the end of each segment (if no explicit end/take)
# Outro sequence - plays after narration ends (not marker-triggered) # Outro sequence - plays after narration ends (not marker-triggered)
outro: list[str] = field( outro: list[str] = field(
default_factory=list default_factory=list
+6 -2
View File
@@ -199,6 +199,8 @@ def parse_project_config(project_path: Path) -> ProjectConfig:
audio_source=data.get("audio_source"), audio_source=data.get("audio_source"),
main_video=data.get("main_video"), main_video=data.get("main_video"),
gnommo_scratch=data.get("gnommo_scratch"), gnommo_scratch=data.get("gnommo_scratch"),
default_begin=float(data.get("default_begin", 0.0)),
default_end_trim=float(data.get("default_end_trim", 0.0)),
outro=data.get("outro", []), outro=data.get("outro", []),
description=data.get("description", ""), description=data.get("description", ""),
footer=data.get("footer", ""), footer=data.get("footer", ""),
@@ -518,10 +520,12 @@ def parse_narration(
filter_list = filter_value filter_list = filter_value
# Handle skip/take - can use begin/end as user-friendly alternatives # Handle skip/take - can use begin/end as user-friendly alternatives
skip = segment_data.get("skip", 0.0) # Fall back to project-level defaults if no explicit value is set
default_begin = config.default_begin if config else 0.0
skip = segment_data.get("skip", default_begin)
take = segment_data.get("take") take = segment_data.get("take")
# Convert begin/end to skip/take if provided # Explicit begin/end always override defaults
if "begin" in segment_data and segment_data["begin"]: if "begin" in segment_data and segment_data["begin"]:
skip = parse_timestamp(segment_data["begin"]) skip = parse_timestamp(segment_data["begin"])
if "end" in segment_data and segment_data["end"]: if "end" in segment_data and segment_data["end"]:
+6
View File
@@ -1951,6 +1951,7 @@ def stitch_narration_segments(
videos: dict[str, VideoSource], videos: dict[str, VideoSource],
output_path: Path, output_path: Path,
verbose: bool = False, verbose: bool = False,
default_end_trim: float = 0.0,
) -> Path: ) -> Path:
""" """
Stitch multiple narration video segments into a single file. Stitch multiple narration video segments into a single file.
@@ -1965,6 +1966,7 @@ def stitch_narration_segments(
videos: Dict of video ID -> VideoSource from videos.json videos: Dict of video ID -> VideoSource from videos.json
output_path: Path for the concatenated output file output_path: Path for the concatenated output file
verbose: Enable verbose output verbose: Enable verbose output
default_end_trim: Seconds to trim from the end when no explicit end/take is set
Returns: Returns:
Path to the stitched video file. Path to the stitched video file.
@@ -2003,6 +2005,10 @@ def stitch_narration_segments(
skip = video_source.skip or 0.0 skip = video_source.skip or 0.0
take = video_source.take take = video_source.take
# Apply default end trim if no explicit take/end was set
if take is None and default_end_trim > 0:
take = max(0.0, full_duration - skip - default_end_trim)
# Calculate effective duration # Calculate effective duration
if take is not None: if take is not None:
effective_duration = min(take, full_duration - skip) effective_duration = min(take, full_duration - skip)
+2
View File
@@ -269,6 +269,8 @@ def build_ffmpeg_command(plan: RenderPlan, output_path: Path) -> list[str]:
always_visible_inputs.append(input_idx) always_visible_inputs.append(input_idx)
input_idx += 1 input_idx += 1
from .cache import resolve_with_cache
# Input: background — resolved via handle in shared_assets/videos.json # Input: background — resolved via handle in shared_assets/videos.json
import json as _json import json as _json
bg_handle = plan.config.background bg_handle = plan.config.background