generate_cutter_report.py:
- Frame-Locked Compare video (trailer left / source right, single MP4) per
beat replaces two separate side-by-side clips; rendered via accurate
double-seek + black-fill segmented source reconstruction
- Generation timestamp now includes HH:MM:SS (Uhrzeit-Angabe)
- Per-beat segment list for multi-shot beats (TC, duration, offset, scene,
score per segment)
- Score warning badge (yellow) if score < 0.65
- python cli.py rematch --beat N command hint in every card
- Overview table links to each beat card via #anchor
- Cleaner dark/light CSS using design tokens (--fg/--bg/--card/--bd)
- --no-clips flag (replaces --with-clips; default is now with clips)
cli.py:
- _auto_commit_push_reports(): after every report regeneration, stages the
report output files (CUTTER_REPORT.*, output/cutter_clips/, output/report/)
and auto-commits + pushes to origin/main so remote is always current
- Removed the legacy match_report.html call from _regenerate_cutter_report
(CUTTER_REPORT now supersedes it)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- config.toml: revert scoreable_luma/contrast thresholds to 24/58/24 (lowering
them let cross-fade blend frames contaminate content-validation templates,
dropping scores below provisional_content_threshold)
- src/cv/global_scan.py: _is_dark_reference_frame now requires contrast<30 so
genuine dark silhouette frames are not rejected as scoreable; two-path
_is_scoreable_reference_frame separates standard vs fade-content scoring
- cli.py: _keeps_cached_match() guard prevents a weaker single-span rematch
from overwriting a better multi-segment provisional cache entry
- cli.py: _fade_content_shots() restricted to between-island gaps only—
pre-island black leaders were incorrectly emitted as matchable shots
- cli.py: island[0] of _match_unmatched_visual_segments() now uses no
continuity seed so an insert cut at the start of a multi-shot beat is not
forced toward the previous beat's scene
- scripts/generate_cutter_report.py: fix ffmpeg concat demuxer on Windows—
use part.absolute().as_posix() so paths in the concat txt are absolute and
not double-resolved relative to the concat file's directory
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1. Cutter-report source clip for multi-segment beats was using only the
primary in/out, which equals the FIRST segment's range. Beat 10 with
3 shots therefore showed only ~0.88 s of source instead of all 3.32 s.
Added extract_concat_clip(): renders each segment as its own MP4 and
concatenates them via ffmpeg's concat demuxer into one continuous
source clip the same length as the trailer beat.
Per-segment intermediate clips (beat_NN_source_seg00.mp4 etc.) are
kept too so individual shots stay inspectable.
2. _regenerate_cutter_report now also regenerates the legacy
output/report/match_report.html via src.pipeline.reporter.generate_report.
Both reports stay in sync after every match command.
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>
- 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>
- 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>
- Phase realign for matched results: drop the "long scene" gate (>1.6x
segment, >=6s) in favor of "scene has any meaningful slack beyond the
segment". Already-confirmed segments in tight scenes are still skipped to
protect strong matches. A repair is only committed if the new score is
not meaningfully worse than the original (>=score-0.02).
- Recovery stage for unmatched beats: vibe-check (CV) feeds top-K candidate
scenes into the semantic action-window search; CV alignment + vision phase
validate gate decide whether the candidate becomes a provisional match.
Beats without scoreable visual content (logos, title cards, full fades)
remain unmatched by design.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
For segmented beats, the repair stage now searches for the source action
window using the segment's own description first; the full beat context is
used only as a fallback or when it scores noticeably higher. The trailer-
offset shift is applied only when the beat context is actually chosen.
Also harden vision-call retries to catch read-side network errors
(TimeoutError, socket.timeout, ConnectionError, OSError) and wrap the
filter/repair loop so a transient vision failure preserves the previously
cached match instead of dropping it.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>