CtrlK
BlogDocsLog inGet started
Tessl Logo

gamussa/reels-producer-skill

help quicky produce instagram reels and youtube shorts

80

1.95x
Quality

93%

Does it follow best practices?

Impact

47%

1.95x

Average score across 1 eval scenario

SecuritybySnyk

Passed

No known issues

Overview
Quality
Evals
Security
Files

check_cuts.pyskills/reel-builder/scripts/

#!/usr/bin/env python3
"""QC a rendered reel before showing it to the human.

Checks every cut boundary in the plan:
  - black/near-black frames at a cut (bad trim or empty source region)
  - frozen cut (frame before == frame after -> the "cut" is invisible,
    usually two segments from the same clip region)
  - duration drift between plan and rendered file

Produces:
  - contact sheet PNG: frame pairs (last frame of seg N | first of seg N+1)
    for visual inspection by the agent BEFORE the human sees the preview
  - report JSON with per-cut verdicts

Usage: check_cuts.py plan/cut_plan.json preview/draft_v1.mp4 \
           --out work/qc_v1  (writes qc_v1.png + qc_v1.json)
"""
import argparse, json, subprocess, sys
from pathlib import Path


def run(cmd):
    r = subprocess.run(cmd, capture_output=True, text=True)
    if r.returncode != 0:
        sys.exit(f"ffmpeg failed:\n{r.stderr[-600:]}")
    return r


def frame_stats(video, t, out_png):
    """Grab frame at t; return (mean_luma, saved_path)."""
    run(["ffmpeg", "-y", "-v", "error", "-ss", f"{max(0, t):.3f}", "-i", str(video),
         "-frames:v", "1", "-vf", "scale=270:480", str(out_png)])
    r = subprocess.run(
        ["ffmpeg", "-i", str(out_png), "-vf", "signalstats,metadata=print",
         "-f", "null", "-"], capture_output=True, text=True)
    mean = None
    for line in r.stderr.splitlines():
        if "YAVG" in line:
            mean = float(line.split("=")[-1])
    return mean if mean is not None else -1


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("plan")
    ap.add_argument("video")
    ap.add_argument("--out", default="work/qc")
    ap.add_argument("--black-thresh", type=float, default=16.0,
                    help="mean luma below this = black frame")
    args = ap.parse_args()

    plan = json.loads(Path(args.plan).read_text())
    outbase = Path(args.out)
    outbase.parent.mkdir(parents=True, exist_ok=True)
    tmp = outbase.parent / "_qc_frames"
    tmp.mkdir(exist_ok=True)

    # rendered duration vs plan
    r = subprocess.run(["ffprobe", "-v", "quiet", "-show_entries", "format=duration",
                        "-of", "csv=p=0", args.video], capture_output=True, text=True)
    rendered = float(r.stdout.strip())
    drift = rendered - plan["total_duration"]

    # cut times = cumulative segment durations
    cuts, t = [], 0.0
    for seg in plan["segments"][:-1]:
        t += seg["duration"]
        cuts.append(round(t, 3))

    issues, pairs = [], []
    eps = 1 / 30 / 2  # half a frame
    for i, ct in enumerate(cuts):
        before = tmp / f"cut{i:02d}_before.png"
        after = tmp / f"cut{i:02d}_after.png"
        luma_b = frame_stats(args.video, ct - eps - 1/30, before)
        luma_a = frame_stats(args.video, ct + eps, after)
        verdict = "ok"
        if luma_b < args.black_thresh or luma_a < args.black_thresh:
            verdict = "BLACK_FRAME"
        pairs.append((before, after))
        # frozen-cut check: pixel difference between the two frames
        d = subprocess.run(
            ["ffmpeg", "-i", str(before), "-i", str(after),
             "-lavfi", "psnr", "-f", "null", "-"],
            capture_output=True, text=True)
        psnr = None
        for line in d.stderr.splitlines():
            if "average:" in line:
                try:
                    psnr = float(line.split("average:")[1].split()[0])
                except ValueError:
                    psnr = 99.0  # 'inf' -> identical
        if psnr is None or psnr > 45:
            verdict = "FROZEN_CUT" if verdict == "ok" else verdict + "+FROZEN"
        issues.append({"cut": i, "time": ct, "luma_before": round(luma_b, 1),
                       "luma_after": round(luma_a, 1),
                       "psnr_across_cut": psnr, "verdict": verdict})

    # contact sheet: stack all pairs into a grid (2 cols per cut)
    inputs, filters = [], []
    for i, (b, a) in enumerate(pairs):
        inputs += ["-i", str(b), "-i", str(a)]
        filters.append(f"[{2*i}][{2*i+1}]hstack[row{i}]")
    if pairs:
        graph = ";".join(filters)
        if len(pairs) > 1:
            graph += ";" + "".join(f"[row{i}]" for i in range(len(pairs))) + \
                     f"vstack=inputs={len(pairs)}[sheet]"
            outmap = "[sheet]"
        else:
            outmap = "[row0]"
        run(["ffmpeg", "-y", "-v", "error"] + inputs +
            ["-filter_complex", graph, "-map", outmap,
             "-frames:v", "1", f"{outbase}.png"])

    report = {"video": args.video, "planned_duration": plan["total_duration"],
              "rendered_duration": round(rendered, 2), "drift_s": round(drift, 2),
              "cuts": issues,
              "problems": [c for c in issues if c["verdict"] != "ok"]}
    Path(f"{outbase}.json").write_text(json.dumps(report, indent=2))

    n_bad = len(report["problems"])
    print(f"QC: {len(cuts)} cuts checked, {n_bad} problem(s), "
          f"duration drift {drift:+.2f}s")
    for c in report["problems"]:
        print(f"  cut {c['cut']} @ {c['time']}s: {c['verdict']}")
    print(f"Contact sheet: {outbase}.png  Report: {outbase}.json")
    if abs(drift) > 0.5:
        print(f"WARNING: rendered duration off by {drift:+.2f}s vs plan")


if __name__ == "__main__":
    main()

tile.json