Concat multi-shot source clips and auto-regen match_report.html
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>
This commit is contained in:
@@ -93,20 +93,39 @@ 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,html} after each cache write so they stay in sync."""
|
||||
"""Re-render CUTTER_REPORT.{md,html} and output/report/match_report.html.
|
||||
|
||||
Called from every match-style command after the cache is written so all
|
||||
cutter-facing artefacts stay in sync with the current `match_results.json`.
|
||||
Failures are logged but never abort the run — the cache is the source of
|
||||
truth, the reports can always be re-rendered manually later.
|
||||
"""
|
||||
try:
|
||||
from scripts.generate_cutter_report import render_report
|
||||
except Exception as exc:
|
||||
logging.getLogger(__name__).warning("Cutter report regen skipped: %s", exc)
|
||||
return
|
||||
else:
|
||||
try:
|
||||
project_root = cfg.paths.cache_dir.parent
|
||||
md, html = render_report(project_root, with_stills=True, with_clips=True)
|
||||
(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)
|
||||
|
||||
# Also keep the legacy output/report/match_report.html in sync. It uses
|
||||
# its own preview-clip pipeline (frame-locked compare videos) and is the
|
||||
# heavier of the two reports — kept up-to-date so the cutter can choose
|
||||
# whichever view they prefer.
|
||||
try:
|
||||
project_root = cfg.paths.cache_dir.parent
|
||||
md, html = render_report(project_root, with_stills=True, with_clips=True)
|
||||
(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)")
|
||||
from src.pipeline.reporter import generate_report
|
||||
all_beats = _load_beats(cfg)
|
||||
all_results = _normalize_cached_results(all_beats, _load_results(cfg), cfg)
|
||||
generate_report(all_beats, all_results, cfg)
|
||||
logging.getLogger(__name__).info("Match report regenerated → output/report/match_report.html")
|
||||
except Exception as exc:
|
||||
logging.getLogger(__name__).warning("Cutter report regen failed: %s", exc)
|
||||
logging.getLogger(__name__).warning("Match report regen failed: %s", exc)
|
||||
|
||||
|
||||
def _load_results(cfg: "AppConfig") -> list: # type: ignore[name-defined]
|
||||
|
||||
Reference in New Issue
Block a user