diff --git a/gnommo/cli.py b/gnommo/cli.py index 90b82ea..817dd37 100644 --- a/gnommo/cli.py +++ b/gnommo/cli.py @@ -2920,7 +2920,11 @@ def cmd_trim( seg_force = force or source_changed existing = raw_data.get(seg_id, {}) - has_explicit = "skip" in existing or "take" in existing + # Any manual trim point counts as "already trimmed" — including the + # user-friendly begin/end/start aliases. Without this, a segment with + # only a manual `begin` would be re-trimmed and have auto-detected + # skip/take written over it, silently clobbering the intended trim. + has_explicit = any(k in existing for k in ("skip", "take", "begin", "end", "start")) if has_explicit and not seg_force: print(f" {seg_id}: already trimmed, skipping (use --force to redo)") _trim_fps[seg_id] = current_fp # adopt/keep current fingerprint @@ -3466,15 +3470,47 @@ def cmd_stitch( # Get segment IDs in natural order (Segment2 before Segment10) segment_ids = sorted(narration.keys(), key=lambda s: [int(t) if t.isdigit() else t.lower() for t in re.split(r'(\d+)', s)]) - # Show what we're stitching + # Show what we're stitching, and — importantly — where each segment's trim + # points came from: the user-friendly begin/end aliases, an explicit + # skip/take (e.g. written by 'trim'), or the project-level defaults. This + # makes the start/end determination visible instead of a bare number. + default_begin = config.default_begin if config else 0.0 + default_end_trim = config.default_end_trim if config else 0.0 + try: + _raw_narr = _read_json(_local_narration_json) if _local_narration_json.exists() else {} + except (OSError, json.JSONDecodeError): + _raw_narr = {} + print(f"\n Segments ({len(segment_ids)}):") for segment_id in segment_ids: seg = narration[segment_id] - skip_str = f"skip={seg.skip:.1f}s" if seg.skip else "" - take_str = f"take={seg.take:.1f}s" if seg.take else "" - trim_info = ", ".join(filter(None, [skip_str, take_str])) - trim_str = f" ({trim_info})" if trim_info else "" - print(f" - {segment_id}{trim_str}") + entry = _raw_narr.get(segment_id) or _raw_narr.get(segment_id.lower()) or {} + + if entry.get("begin"): + skip_from = f"from begin={entry['begin']}" + elif entry.get("start"): + skip_from = f"from start={entry['start']}" + elif "skip" in entry: + skip_from = "explicit skip" + elif default_begin: + skip_from = f"from default_begin={default_begin:g}s" + else: + skip_from = None + + if entry.get("end"): + take_from = f"from end={entry['end']}" + elif "take" in entry: + take_from = "explicit take" + elif seg.take is not None and default_end_trim: + take_from = f"from default_end_trim={default_end_trim:g}s" + else: + take_from = None + + skip_disp = f"skip={seg.skip:.1f}s" + (f" ({skip_from})" if skip_from else "") + take_disp = f"take={seg.take:.1f}s" if seg.take is not None else "take=to end" + if take_from: + take_disp += f" ({take_from})" + print(f" - {segment_id}: {skip_disp} · {take_disp}") stitch_output = videos_dir_out / "narration_combined.mov" diff --git a/gnommo/transfer.py b/gnommo/transfer.py index 0e9a79f..f412015 100644 --- a/gnommo/transfer.py +++ b/gnommo/transfer.py @@ -8,9 +8,12 @@ Workflow: Design: - commit appends a timestamped entry to commits.log - up checks server commits.log for newer entry (aborts if found), - then rsyncs a manifest of only the files needed to render - - down rsyncs everything the server has back to local - (server is already clean — it only holds what was pushed) + then rsyncs a manifest of only the *inputs* needed to render + (manuscript, slides, narration, audio, videos, keynote). It does + NOT push the rendered output (out/*.mp4/.srt) — that is produced + on the rig, so pushing a stale local copy would clobber it. + - down rsyncs everything the server has back to local, including the + freshly rendered out/*.mp4. Rendered output flows one way: rig → local. """ import json @@ -61,12 +64,10 @@ def _build_manifest(project_path: Path) -> list[str]: for key_file in project_path.glob("*.key"): files.add(key_file.name) - # Rendered output (mp4 + srt — not low-res previews or transcripts) - out_dir = project_path / "out" - if out_dir.is_dir(): - for f in out_dir.iterdir(): - if f.is_file() and f.suffix in (".mp4", ".srt"): - files.add(str(f.relative_to(project_path))) + # NOTE: out/ (rendered mp4/srt) is deliberately NOT pushed. The rendering + # rig produces those; pushing the local (older) copy up would overwrite the + # rig's fresh render, which 'down' would then pull back — clobbering the new + # result. Rendered output flows one way only: rig → local via 'down'. # Manuscript (may be at a non-standard path) manuscript_rel = project.get("manuscript", "manuscript.txt")