Embed cutter-report stills inline + add HTML report with video previews

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>
This commit is contained in:
Melbar
2026-05-04 13:33:07 +02:00
parent 5a6ae2175c
commit a405df0ddb
4 changed files with 633 additions and 359 deletions
+5 -4
View File
@@ -93,7 +93,7 @@ def _save_results(results: list, cfg: "AppConfig") -> None: # type: ignore[name
def _regenerate_cutter_report(cfg: "AppConfig") -> None: # type: ignore[name-defined]
"""Re-render CUTTER_REPORT.md after each cache write so it stays in sync."""
"""Re-render CUTTER_REPORT.{md,html} after each cache write so they stay in sync."""
try:
from scripts.generate_cutter_report import render_report
except Exception as exc:
@@ -101,9 +101,10 @@ def _regenerate_cutter_report(cfg: "AppConfig") -> None: # type: ignore[name-de
return
try:
project_root = cfg.paths.cache_dir.parent
out = project_root / "CUTTER_REPORT.md"
out.write_text(render_report(project_root), encoding="utf-8")
logging.getLogger(__name__).info("Cutter report regenerated → %s", out)
md, html = render_report(project_root, with_stills=True, with_clips=False)
(project_root / "CUTTER_REPORT.md").write_text(md, encoding="utf-8")
(project_root / "CUTTER_REPORT.html").write_text(html, encoding="utf-8")
logging.getLogger(__name__).info("Cutter report regenerated (md + html)")
except Exception as exc:
logging.getLogger(__name__).warning("Cutter report regen failed: %s", exc)