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

probe_clips.pyskills/reel-builder/scripts/

#!/usr/bin/env python3
"""Inventory raw clips: resolution, fps, codec, rotation, color profile.

Detects footage that needs special handling:
  - iPhone HDR (HLG / arib-std-b67 transfer) -> needs tonemap to SDR
  - DJI D-Log / flat profiles (heuristic: bt709 primaries + low-contrast tag or dlog in metadata)
  - Vertical vs landscape orientation (rotation metadata aware)

Flags duration outliers: a clip much longer than the reel target (default
>10x) is probably a screen recording / full event, not b-roll — extract the
useful segment before normalizing instead of transcoding the whole thing.

Usage: probe_clips.py RAW_DIR --out work/clips.json [--target-duration 30]
"""
import argparse, json, subprocess, sys
from pathlib import Path

VIDEO_EXT = {".mp4", ".mov", ".m4v", ".mts", ".avi", ".mkv"}


def ffprobe(path):
    cmd = ["ffprobe", "-v", "quiet", "-print_format", "json",
           "-show_format", "-show_streams", str(path)]
    r = subprocess.run(cmd, capture_output=True, text=True)
    if r.returncode != 0:
        return None
    return json.loads(r.stdout)


def analyze(path):
    data = ffprobe(path)
    if not data:
        return {"file": str(path), "error": "ffprobe failed (corrupt or unsupported)"}
    v = next((s for s in data.get("streams", []) if s.get("codec_type") == "video"), None)
    a = next((s for s in data.get("streams", []) if s.get("codec_type") == "audio"), None)
    if not v:
        return {"file": str(path), "error": "no video stream"}

    # rotation: side_data or tags
    rotation = 0
    for sd in v.get("side_data_list", []):
        if "rotation" in sd:
            rotation = int(sd["rotation"])
    rotation = int(v.get("tags", {}).get("rotate", rotation))

    w, h = v.get("width", 0), v.get("height", 0)
    if abs(rotation) in (90, 270):
        w, h = h, w

    fps = 0.0
    if v.get("avg_frame_rate") and v["avg_frame_rate"] != "0/0":
        num, den = v["avg_frame_rate"].split("/")
        fps = round(float(num) / float(den), 3) if float(den) else 0.0

    transfer = v.get("color_transfer", "")
    primaries = v.get("color_primaries", "")
    is_hdr = transfer in ("arib-std-b67", "smpte2084")  # HLG / PQ
    fmt_tags = {k.lower(): str(val).lower() for k, val in data.get("format", {}).get("tags", {}).items()}
    is_dlog = any("d-log" in val or "dlog" in val for val in fmt_tags.values())

    profile = "sdr"
    if is_hdr:
        profile = "hlg" if transfer == "arib-std-b67" else "pq"
    elif is_dlog:
        profile = "dlog"

    return {
        "file": str(path),
        "duration": round(float(data["format"].get("duration", 0)), 2),
        "width": w, "height": h,
        "orientation": "vertical" if h > w else ("square" if h == w else "landscape"),
        "fps": fps,
        "vcodec": v.get("codec_name"),
        "pix_fmt": v.get("pix_fmt"),
        "color_transfer": transfer, "color_primaries": primaries,
        "profile": profile,               # sdr | hlg | pq | dlog
        "rotation": rotation,
        "has_audio": a is not None,
        "size_mb": round(int(data["format"].get("size", 0)) / 1e6, 1),
        "pan": "center",                  # user-editable: left|center|right crop for landscape
    }


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("raw_dir")
    ap.add_argument("--out", default="work/clips.json")
    ap.add_argument("--target-duration", type=float, default=30,
                    help="Planned reel length (s); clips >10x this get flagged "
                         "as duration outliers (default 30)")
    args = ap.parse_args()

    raw = Path(args.raw_dir)
    files = sorted(p for p in raw.rglob("*") if p.suffix.lower() in VIDEO_EXT)
    if not files:
        sys.exit(f"No video files found in {raw}")

    clips = [analyze(p) for p in files]
    outlier_at = args.target_duration * 10
    for c in clips:
        if "error" not in c and c["duration"] > outlier_at:
            c["warning"] = (f"duration outlier: {c['duration']:.0f}s is "
                            f">{outlier_at:.0f}s (10x the {args.target_duration:.0f}s target) — "
                            "likely a full event/talk, not b-roll. Extract the usable "
                            "segment before normalizing, e.g.: ffmpeg -ss START -to END "
                            f"-i '{c['file']}' -c copy work/extracts/NAME.mp4, then re-probe.")
    out = Path(args.out)
    out.parent.mkdir(parents=True, exist_ok=True)
    out.write_text(json.dumps({"clips": clips}, indent=2))

    ok = [c for c in clips if "error" not in c]
    bad = [c for c in clips if "error" in c]
    total = sum(c["duration"] for c in ok)
    print(f"Probed {len(files)} files -> {out}")
    print(f"  usable: {len(ok)}  total runtime: {total:.1f}s")
    for c in ok:
        flag = "" if c["profile"] == "sdr" else f"  [{c['profile'].upper()} - will tonemap]"
        print(f"  {Path(c['file']).name}: {c['width']}x{c['height']} {c['fps']}fps "
              f"{c['duration']}s {c['orientation']}{flag}")
    for c in ok:
        if "warning" in c:
            print(f"  WARNING {Path(c['file']).name}: {c['warning']}")
    for c in bad:
        print(f"  PROBLEM {Path(c['file']).name}: {c['error']}")


if __name__ == "__main__":
    main()

tile.json