Template scan of entire source found the knife/letter-opener close-up at
t=130-133s in the film (scene 17, 127.76-133.04s). Previous wrong match was
pointing at scene 309 (the coffee/window scene) for both shots because the
strong continuity seed from beat 14 overwhelmed the global search.
Two-segment provisional match written to cache manually:
seg[0] knife: scene 17 in=130.32s dur=2.80s score=0.72 (confirmed)
seg[1] coffee: scene 309 in=2615.52s dur=1.28s score=0.38 (provisional)
Regenerated CUTTER_REPORT and match_report with correct beat 15 clips/stills.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Beat 15 (4.40 s, two women + coffee mugs + snowy window) was matched as a
single 4-second clip at scene 309 in=2626.93 s, score 0.531. The trailer
beat actually contains two shots split by an internal cut at offset 3.12 s,
and the previous match approximated both with the same continuous span,
landing at the wrong phase for the second sub-shot.
After `match --beat 15 --vision` with the per-shot pipeline:
Shot 1 (offset 0.32, dur 2.80 s): scene 309 in=2626.929 score 0.531
Shot 2 (offset 3.12, dur 1.20 s): scene 309 in=2607.254 score 0.608
Same scene, but the second shot is now placed earlier in the source where
the leopard-print woman enters the frame — the right phase for that sub-
shot. Other beats unchanged (0 diffs verified against bak).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1. Action-group classifier conflated object-touches and person-touches.
"man touches the red door with a small object" was being tagged as
forehead_touch because "touch" was in the forehead_touch needles set.
That made the realign pass yank Beat 16 from scene 451 (correct: man
painting red door, IV stand) over to scene 623 (woman/man in bed) —
a totally wrong shot at score 0.344.
Fix: removed generic "touch*" verbs from forehead_touch's needle set.
forehead_touch is now added in _semantic_action_groups() only when a
touch verb is paired with an explicit body-part target (forehead,
face, cheek, head, hand, ...) and not paired with an object target
(door, handle, brush, tool, lock, ...).
Effect on Beat 16 after `match --beat 16 --vision`:
scene 623 in=5476.28 score=0.344 -> scene 451 in=3912.48 score=0.626.
2. Cutter-report stills/clips were keyed by source-video mtime, so a
match-position change without a video change served stale frames from
the previous match. Dropped the mtime cache; both extractors now
render fresh every time. Slower (~minute per full regen) but correct.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two issues fixed:
1. Beats with internal hard cuts (e.g. man-shot then back to woman) were
being approximated by a single source clip because the multi-segment
path only triggered for fade-bounded multi-island beats. Added
_reference_shot_segments(), which returns the shot ranges by splitting
each visible island at detected internal cuts. The multi-island gate in
cmd_match and the per-island loop in _match_unmatched_visual_segments
now use shots, so any beat with cuts > 0 produces one MatchSegment per
shot. Each shot is matched independently against the source movie.
Effect on Beat 10: 1 segment (3.32 s in scene 558) -> 3 segments
covering shots 0-0.88 s, 0.88-2.64 s, 2.64-3.32 s in scenes 554, 559,
556 respectively, with the previously missing "back to woman" cut now
correctly placed in scene 556.
2. Targeted --beat N runs were silently dropping cache entries for other
beats whose old scores no longer pass current quality gates
(_normalize_cached_results runs at load time and removes them). The
save path now re-loads the raw cache from disk and writes back every
non-targeted beat verbatim, so a per-beat run can never regress
another beat's stored match.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Refreshed CUTTER_REPORT.{md,html} plus stills and clips from the latest
match cache. Notable changes vs prior cache:
- 6 confirmed (was 5): newly confirmed beats 2, 9, 12, 17 (beat 8 lost
confirmed status, beat 18 lost match entirely; both will be addressed
per-beat).
- Beat 2: previously unmatched -> scene 3 in=35.190s score 0.761 (OK).
- Beat 20: scene 613 in=5284.706s score 0.663 (OK), correct phase via
recovery.
- Beats 21, 23: previously unmatched -> now provisional via recovery.
- Beat 18: regression, currently MAN. (was confirmed before).
- Beat 24: still MAN. (end credits, expected).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- cli.py auto-regen now produces video clips on every match (no opt-in
flag). Best presentation comes first; speed cost (~minutes per match)
is accepted.
- output/cutter_stills/ and output/cutter_clips/ are no longer gitignored.
All 45 stills and 45 short MP4 previews are committed alongside the
CUTTER_REPORT.{md,html} so the remote repo always shows the current
state — even when the report files are inspected without running the
generator.
- Other output/ contents (FCPXML, EDL, debug folders, HTML report) stay
ignored.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two issues fixed:
1. Source frame rate was wrong. The script trusted ffprobe, which on this
re-wrapped proxy reports 25 fps. The real number for the EDL/FCPXML and
for what the cutter sees in the NLE comes from config.toml's
edl_frame_rate (here 23.976). Source fps now reads that value first;
ffprobe is only a fallback. Trailer fps still probes ffprobe (correct
for the trailer file) with optional config override.
2. Stills in CUTTER_REPORT.md showed as broken links because output/ is
gitignored, so the git server can't serve them. Stills are now embedded
as base64 data URIs directly in the markdown. The file is therefore
self-contained and renders in any markdown viewer including the git
server's web preview.
3. New CUTTER_REPORT.html alongside the markdown: same data, proper card
layout, side-by-side trailer/source columns per beat, base64-embedded
stills, and (with --with-clips) base64-embedded 3 s MP4 video previews
so the cutter can sight-check phase agreement directly in a browser.
The auto-regen on each match writes both files; --with-clips is opt-in
from the CLI for slower full renders.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
README: 550 -> 308 lines. The dense algorithm prose was moved verbatim to
docs/ALGORITHM.md and replaced in the README with a compact "Wenn ein Match
falsch wirkt" troubleshooting table and a link. The cutter-facing intro
points at the new in-report stills instead of the old HTML report.
Cutter report:
- Per-side frame rates: trailer timecodes use the trailer file's fps
(typically 25), source timecodes use the source file's fps. ffprobe is
used to detect each side; falls back to edl_frame_rate if unavailable.
- Side-by-side trailer/source preview stills extracted via ffmpeg, taken
~30% into the beat / match window. Stored under output/cutter_stills/
(gitignored). Re-rendered only when the underlying video is newer than
the cached jpg.
- Compact table at the top, detailed per-beat sections below with the
stills inline so the cutter can sight-check phase agreement directly.
- New --no-stills flag for fast text-only regeneration.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- New CUTTER_REPORT.md: per-beat hand-off table for the video editor doing
the manual recut. Per beat: trailer SMPTE in/out, source SMPTE in/out,
scene id, score, status (OK / ? / MAN.), and a one-line phase
description from the cached vision text.
- New scripts/generate_cutter_report.py: pure renderer that reads the
current cache (match_results.json + trailer_beats.json + optional
vision_descriptions.json) and writes CUTTER_REPORT.md. No side effects on
the cache.
- cli.py: after every successful match the cutter report is regenerated
automatically (best-effort; failures are logged and do not abort).
- README.md: new top-section "Fuer den Cutter" describing exactly what the
editor needs (which two files to look at, how the status flag works,
the recommended NLE workflow). The technical algorithm description
follows below.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>