diff --git a/gnommo/cli.py b/gnommo/cli.py index aeee7b4..3104b3f 100644 --- a/gnommo/cli.py +++ b/gnommo/cli.py @@ -172,8 +172,15 @@ Examples: parser.add_argument( "--realign", action="store_true", - help="For build/render: discard existing events.json times and re-align " - "manuscript markers to the transcript from scratch (e.g. after re-recording)", + help="(build re-aligns by default now) Explicitly re-align manuscript markers " + "to the transcript, recomputing narration_time. Your `adjustment` offsets are " + "always carried forward.", + ) + parser.add_argument( + "--no-realign", + action="store_true", + help="For build: DON'T re-align — keep events.json's stored times verbatim " + "(freeze the timing layer, e.g. to preserve direct narration_time edits).", ) parser.add_argument( "--slides", @@ -397,7 +404,9 @@ Examples: args.slides, args.res, args.force, - realign=args.realign, + # build re-aligns by default (keeping your adjustments); --no-realign + # freezes events.json's stored times instead. + realign=not args.no_realign, ) elif action == "render": return cmd_render( @@ -5050,7 +5059,12 @@ def _cmd_render_impl( # all the rig ever needs and the output can't silently diverge from the build. from . import scaffold as _scaffold - _existing_events = None if realign else _scaffold.read_events(project_path) + # Always read the previous events.json: even when re-aligning we carry each + # event's manual `adjustment` (a relative nudge) forward. `_existing_events` + # (the timing OVERRIDE — use stored times instead of aligning) is only set when + # NOT re-aligning, so render / `all` are unaffected by the always-read. + _old_events = _scaffold.read_events(project_path) + _existing_events = None if realign else _old_events if not plan_only: if _existing_events is None or _scaffold.read_scaffold(project_path) is None: print( @@ -5143,21 +5157,23 @@ def _cmd_render_impl( # only consumes the files — so the timing layer is written exactly once, by build. if plan_only and slide_range is None and _output_path_override is None: _events = _scaffold.derive_events(marker_timings, slides, videos, audio) - # merge restores each id's human `adjustment` (the relative tweak). - _events = _scaffold.merge_events(_events, _existing_events) - if _existing_events: - # Override path: the marker_timings the plan used were EFFECTIVE - # narration (narration_time + adjustment). narration_time itself is - # owned by events.json — restore it so the effective value doesn't leak - # into the stored narration_time. Pair by ordinal (not id) so a marker - # reused several times keeps each occurrence's own narration_time - # instead of collapsing onto the last occurrence's value. - for e, _old in _scaffold.pair_events_by_ordinal(_events, _existing_events): + # Carry each id's human `adjustment` (a relative nudge) forward from the + # previous events.json — matched by (id, ordinal) — so re-aligning keeps your + # offsets even though narration_time is recomputed from the transcript. + _events = _scaffold.merge_events(_events, _old_events) + if realign: + # Re-align (default): narration_time comes fresh from the transcript; + # recompute the interpolated markers between the aligned anchors. + _scaffold.reinterpolate_events(_events) + elif _old_events: + # Freeze (--no-realign): the marker_timings the plan used were EFFECTIVE + # narration (narration_time + adjustment). narration_time itself is owned + # by events.json — restore it so the effective value doesn't leak into the + # stored narration_time. Pair by ordinal (not id) so a marker reused + # several times keeps each occurrence's own narration_time. + for e, _old in _scaffold.pair_events_by_ordinal(_events, _old_events): if _old is not None and _old.get("narration_time") is not None: e["narration_time"] = _old["narration_time"] - else: - # Fresh align: recompute interpolated narration times between anchors. - _scaffold.reinterpolate_events(_events) # final_time = (narration + adjustment) shifted by preceding pauses. _scaffold.compute_final_times(_events) # events.json carries representation-only narration-track events so a GUI