CtrlK
BlogDocsLog inGet started
Tessl Logo

gamussa/reels-producer-skill

Write talking-head scripts and produce Instagram reels and YouTube shorts

90

1.77x
Quality

97%

Does it follow best practices?

Impact

64%

1.77x

Average score across 3 eval scenarios

SecuritybySnyk

Low

Low-risk findings worth noting

Overview
Quality
Evals
Security
Files

check_export.pyskills/reel-builder/scripts/

#!/usr/bin/env python3
"""Check an exported file against the platform it is bound for.

export_variants.py encodes to a spec; nothing until now read the result back
and asked whether it actually meets one. This does. Each row is one property,
measured from the file, against the platform's requirement, with the fix.

Rows come in two kinds, and the split is the point:

  format     codec, pixel format, colour tags, HDR, true peak, VFR. Wrong is
             wrong; the fix is mechanical and changes nothing the viewer sees.
  judgement  duration, aspect, fps, loudness. Fixing these changes the
             content — a cut loses material, a crop loses edges, a gain
             boosts ambience — so a FAIL here is a decision to put to the
             human, not a thing to correct silently.

Platform caps are a SNAPSHOT (see PLATFORMS and SPECS_REVIEWED). They move;
when a cap is older than the reel, check the platform's own help page before
trusting a FAIL on duration.

Adapted from kajisho5/ffmpeg-skill's check.py (MIT, see CREDITS.md).

Usage:
  check_export.py exports/ig_reels.mp4 --platform ig_reels
  check_export.py exports/*.mp4                 # platform from the file name
  check_export.py exports/tiktok.mp4 --json --no-loudness
"""
import argparse, json, subprocess, sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent))
from ffsafe import measure_loudness      # noqa: E402

SPECS_REVIEWED = "2026-09-10"    # bump when PLATFORMS is re-checked

# max_s: None = no cap this check enforces. aspects: (w/h) ratios accepted.
PLATFORMS = {
    "tiktok":    {"max_s": 600,  "aspects": (9 / 16, 1.0),        "min_h": 1080, "fps_max": 60, "lufs": -14, "tol": 2.0, "tp": -1.0, "sdr_only": True},
    "ig_reels":  {"max_s": 180,  "aspects": (9 / 16, 4 / 5, 1.0), "min_h": 1080, "fps_max": 60, "lufs": -14, "tol": 2.0, "tp": -1.0, "sdr_only": True},
    "yt_shorts": {"max_s": 180,  "aspects": (9 / 16, 1.0),        "min_h": 1080, "fps_max": 60, "lufs": -14, "tol": 2.0, "tp": -1.0, "sdr_only": False},
    "youtube":   {"max_s": None, "aspects": (16 / 9, 9 / 16, 1.0, 4 / 3), "min_h": 720, "fps_max": 60, "lufs": -14, "tol": 2.0, "tp": -1.0, "sdr_only": False},
}
CODECS = ("h264", "hevc")
JUDGEMENT = ("duration", "aspect", "fps", "loudness")
HDR_TRANSFERS = ("smpte2084", "arib-std-b67")
ASPECT_TOL = 0.02


def probe(path):
    """The fields this check reads, from one ffprobe call. Exit on failure."""
    r = subprocess.run(
        ["ffprobe", "-v", "error", "-show_entries",
         "stream=codec_type,codec_name,width,height,pix_fmt,r_frame_rate,"
         "avg_frame_rate,color_transfer,color_primaries,color_space"
         ":format=duration", "-of", "json", str(path)],
        capture_output=True, text=True)
    if r.returncode != 0:
        sys.exit(f"ffprobe could not read {path}: {r.stderr.strip()[-300:]}")
    d = json.loads(r.stdout)
    v = next((s for s in d.get("streams", []) if s.get("codec_type") == "video"), None)
    if v is None:
        sys.exit(f"{path} has no video stream — this checks video deliverables.")
    return v, float(d["format"]["duration"])


def frac(s):
    try:
        n, d = s.split("/")
        return float(n) / float(d) if float(d) else 0.0
    except (ValueError, AttributeError):
        return 0.0


def rows_for(v, duration, spec, loud=None):
    """Every check row for one file. Pure: no ffmpeg here, so it is testable."""
    out = []

    def row(name, status, got, want, fix=""):
        out.append({"check": name, "status": status, "measured": got,
                    "expected": want, "fix": fix,
                    "kind": "judgement" if name in JUDGEMENT else "format"})

    if spec["max_s"]:
        row("duration", "PASS" if duration <= spec["max_s"] else "FAIL",
            f"{duration:.2f}s", f"<= {spec['max_s']}s",
            "decide with the human: a cut drops material, a speed-up changes motion")

    w, h = int(v.get("width", 0)), int(v.get("height", 0))
    ar = w / h if h else 0.0
    ok = any(abs(ar - a) <= ASPECT_TOL for a in spec["aspects"])
    row("aspect", "PASS" if ok else "FAIL", f"{w}x{h} ({ar:.3f})",
        " / ".join(f"{a:.3f}" for a in spec["aspects"]),
        "re-export from a master of the right shape; a crop here loses edges")

    row("height", "PASS" if h >= spec["min_h"] else "WARN", f"{h}",
        f">= {spec['min_h']}", "the platform will upscale; re-render at the mezzanine size")

    fps = frac(v.get("avg_frame_rate", "0/1")) or frac(v.get("r_frame_rate", "0/1"))
    row("fps", "PASS" if 0 < fps <= spec["fps_max"] else "FAIL",
        f"{fps:.3f}", f"<= {spec['fps_max']}", "render_reel.py conforms to 30fps")

    vfr = frac(v.get("r_frame_rate", "0/1")) - frac(v.get("avg_frame_rate", "0/1"))
    row("vfr", "PASS" if abs(vfr) < 0.5 else "WARN",
        "variable" if abs(vfr) >= 0.5 else "constant", "constant",
        "re-encode through render_reel.py, which forces a constant rate")

    codec = v.get("codec_name", "")
    row("video codec", "PASS" if codec in CODECS else "FAIL", codec, "/".join(CODECS),
        "export_variants.py writes h264")

    pf = v.get("pix_fmt", "")
    row("pixel format", "PASS" if pf == "yuv420p" else "FAIL", pf, "yuv420p",
        "a 4:2:2 or 10-bit file plays wrong or not at all on phones; re-export")

    trc = v.get("color_transfer", "")
    if trc in HDR_TRANSFERS:
        row("colour", "FAIL" if spec["sdr_only"] else "WARN", f"HDR ({trc})",
            "SDR bt709" if spec["sdr_only"] else "SDR bt709, or HDR the platform accepts",
            "normalize_clips.py tone-maps HDR to bt709; re-run from the mezzanine")
    else:
        tags = (v.get("color_primaries", ""), v.get("color_transfer", ""), v.get("color_space", ""))
        if all(t == "bt709" for t in tags):
            row("colour", "PASS", "bt709 tagged", "bt709 tagged")
        elif not any(tags):
            row("colour", "WARN", "untagged", "bt709 tagged",
                "players assume bt709; export_variants.py tags explicitly")
        else:
            row("colour", "WARN", "/".join(t or "-" for t in tags), "bt709 tagged",
                "tags disagree with bt709; re-export to retag")

    if loud is not None and spec["lufs"] is not None:
        lufs, tp = float(loud["input_i"]), float(loud["input_tp"])
        diff = abs(lufs - spec["lufs"])
        row("loudness", "PASS" if diff <= spec["tol"] else "FAIL",
            f"{lufs:.1f} LUFS", f"{spec['lufs']} ± {spec['tol']} LUFS",
            "export_variants.py normalises speech and music; leave near-silent ambience alone and say so")
        row("true peak", "PASS" if tp <= spec["tp"] else "FAIL",
            f"{tp:.1f} dBTP", f"<= {spec['tp']} dBTP",
            "re-export; the two-pass loudnorm caps true peak")
    return out


def platform_from_name(path):
    stem = Path(path).stem
    return stem if stem in PLATFORMS else None


def main():
    ap = argparse.ArgumentParser(
        description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
    ap.add_argument("files", nargs="+")
    ap.add_argument("--platform", choices=sorted(PLATFORMS),
                    help="Default: the file's stem, when it names a platform")
    ap.add_argument("--no-loudness", action="store_true",
                    help="Skip the loudness analysis pass (it decodes the whole file)")
    ap.add_argument("--json", action="store_true")
    a = ap.parse_args()

    reports, any_fail = [], False
    for f in a.files:
        plat = a.platform or platform_from_name(f)
        if plat is None:
            sys.exit(f"{f}: cannot tell the platform from the name — pass --platform.")
        v, dur = probe(f)
        loud = None if a.no_loudness else measure_loudness(f)
        if not a.no_loudness and loud is None:
            print(f"WARNING: {f}: loudness could not be measured — rows omitted",
                  file=sys.stderr)
        rows = rows_for(v, dur, PLATFORMS[plat], loud)
        fails = [r for r in rows if r["status"] == "FAIL"]
        any_fail |= bool(fails)
        reports.append({"file": f, "platform": plat, "specs_reviewed": SPECS_REVIEWED,
                        "rows": rows,
                        "defects": [r for r in fails if r["kind"] == "format"],
                        "decisions": [r for r in fails if r["kind"] == "judgement"],
                        "ok": not fails})

    if a.json:
        print(json.dumps(reports, indent=2))
    else:
        for rep in reports:
            print(f"{rep['file']} -> {rep['platform']} (caps reviewed {SPECS_REVIEWED})")
            for r in rep["rows"]:
                tag = "" if r["status"] == "PASS" else f"  [{r['kind']}]"
                print(f"  {r['status']:4} {r['check']:13} {r['measured']:<22} want {r['expected']}{tag}")
                if r["status"] != "PASS" and r["fix"]:
                    print(f"       -> {r['fix']}")
            if rep["decisions"]:
                print("  A judgement FAIL changes the content to fix. Put it to the "
                      "human; do not correct it silently.")
    sys.exit(1 if any_fail else 0)


if __name__ == "__main__":
    main()

.mcp.json

tile.json