Write talking-head scripts and produce Instagram reels and YouTube shorts
90
97%
Does it follow best practices?
Impact
64%
1.77xAverage score across 3 eval scenarios
Low
Low-risk findings worth noting
#!/usr/bin/env python3
"""Preflight: verify every tool this pipeline needs, before touching media.
Run this FIRST, every session. A missing dependency found at step 6 has already
cost a normalize pass; found here it costs nothing.
Exits non-zero when a REQUIRED tool is missing, so it can gate a workflow.
Optional tools are reported with the exact capability that is lost — never
silently substituted, because a fallback the user did not hear about is
indistinguishable from a bug.
Usage:
check_deps.py # music-video requirements (default)
check_deps.py --mode talking-head # VO-spine requirements
check_deps.py --json # machine-readable, for gating
"""
import argparse, json, os, shutil, subprocess, sys
from pathlib import Path
VENV_PY = Path(".venv/bin/python3")
def which_version(exe, args, first_line=True):
path = shutil.which(exe)
if not path:
return None, None
try:
r = subprocess.run([path] + args, capture_output=True, text=True, timeout=20)
out = (r.stdout or r.stderr).strip()
return path, (out.splitlines()[0][:60] if first_line and out else "")
except (subprocess.SubprocessError, OSError):
return path, ""
def python_module(mod):
"""Check a module inside the project venv, falling back to this python.
Reports which interpreter satisfied it — a module present only in system
python is NOT usable by the pipeline, which invokes .venv/bin/python3.
"""
interp = str(VENV_PY) if VENV_PY.exists() else sys.executable
r = subprocess.run(
[interp, "-c", f"import {mod}; print(getattr({mod}, '__version__', ''))"],
capture_output=True, text=True)
if r.returncode != 0:
return None, interp
return (r.stdout.strip() or "present"), interp
# ffmpeg is one row, but the pipeline leans on specific FILTERS and ENCODERS
# inside it, and a build can ship without them (Homebrew bottles and CI
# runners drop libass, libzimg, libfreetype). Ask the build, once, and read
# every listing three ways: available, missing, or unknown when the listing
# itself could not be parsed. Unknown is never folded into missing (a filter
# that exists is not reported absent) nor into available (a failed detection
# is not a pass). Adapted from kajisho5/ffmpeg-skill's doctor (MIT).
FILTERS_REQUIRED = {
"scale": "every normalize and render", "crop": "aspect crops and pans",
"fps": "CFR conform", "format": "yuv420p output", "eq": "all grades",
"colorbalance": "gritty/rapha grades", "curves": "cine grade",
"lut3d": "LUT looks and D-Log conversion", "tile": "contact sheets and audits",
"select": "tighten_vo.py cuts", "aselect": "tighten_vo.py cuts",
"setpts": "tighten_vo.py cuts", "asetpts": "tighten_vo.py cuts",
"loudnorm": "export loudness", "volume": "music/VO mix",
"afade": "segment audio joins", "amix": "VO over b-roll",
"silencedetect": "tighten_vo.py pause detection",
"zoompan": "Ken Burns on stills", "overlay": "styled captions",
"noise": "gritty grade grain",
}
FILTERS_OPTIONAL = {
"subtitles": "render_reel.py --srt burns nothing — plain captions unavailable, styled ones (gen_captions.py) still work",
"zscale": "HDR (HLG/PQ) footage cannot be tone-mapped — normalize_clips.py leaves it washed out",
"tonemap": "HDR (HLG/PQ) footage cannot be tone-mapped — normalize_clips.py leaves it washed out",
"afftdn": "clean_audio.py has no default denoiser",
"arnndn": "clean_audio.py --engine arnndn unavailable",
}
ENCODERS_REQUIRED = {"libx264": "no H.264 output — nothing exports",
"aac": "no AAC audio — nothing exports"}
def parse_listing(text):
"""Names from `ffmpeg -filters` / `-encoders` output, or an unknown status.
Both listings print a legend, a ` ------` separator, then one row per
entry: flags, name, then (filters only) an io spec like `V->V`. The name is
keyed off that shape, not the legend, which has changed across releases.
Anything without the separator did not come from ffmpeg and is `unknown`.
"""
lines = (text or "").splitlines()
try:
start = next(i for i, l in enumerate(lines) if l.strip().startswith("------")) + 1
except StopIteration:
return {"status": "unknown", "names": set()}
names = set()
for line in lines[start:]:
parts = line.split()
if len(parts) >= 2 and not parts[0].startswith("-"):
names.add(parts[1])
return {"status": "parsed" if names else "unknown", "names": names}
def ffmpeg_listing(kind):
"""Parsed `ffmpeg -<kind>` output, or status missing/unknown."""
exe = shutil.which("ffmpeg")
if not exe:
return {"status": "missing", "names": set()}
try:
r = subprocess.run([exe, "-hide_banner", f"-{kind}"], capture_output=True,
text=True, timeout=20)
except (subprocess.TimeoutExpired, OSError):
return {"status": "unknown", "names": set()}
if r.returncode != 0:
return {"status": "unknown", "names": set()}
return parse_listing(r.stdout)
def capability_state(listing, name):
"""available | missing | unknown for one filter or encoder."""
if listing["status"] == "parsed":
return "available" if name in listing["names"] else "missing"
return listing["status"] if listing["status"] == "missing" else "unknown"
def check(mode):
rows = []
def add(name, required, found, detail, lost):
rows.append({"name": name, "required": required, "present": bool(found),
"detail": detail or "", "without_it": lost})
p, v = which_version("ffmpeg", ["-version"])
add("ffmpeg", True, p, v, "nothing runs — every stage shells out to it")
p, v = which_version("ffprobe", ["-version"])
add("ffprobe", True, p, v, "no probing, no duration validation")
filters, encoders = ffmpeg_listing("filters"), ffmpeg_listing("encoders")
def add_cap(kind, listing, name, required, lost):
state = capability_state(listing, name)
detail = "" if state == "available" else (
"listing unreadable" if state == "unknown" else "not in this build")
add(f"{kind}:{name}", required, state == "available", detail,
lost if state != "unknown" else
f"could not read ffmpeg -{kind}s — {lost}")
for name, use in FILTERS_REQUIRED.items():
add_cap("filter", filters, name, True, f"{use} fails")
for name, lost in FILTERS_OPTIONAL.items():
add_cap("filter", filters, name, False, lost)
for name, lost in ENCODERS_REQUIRED.items():
add_cap("encoder", encoders, name, True, lost)
venv_ok = VENV_PY.exists()
add(".venv", mode == "music-video", venv_ok,
str(VENV_PY) if venv_ok else "",
"macOS refuses system pip (PEP 668) — python deps cannot be installed")
for mod, needed_for in (("librosa", "music-video"), ("soundfile", "music-video")):
ver, interp = python_module(mod)
add(mod, mode == needed_for, ver,
f"{ver} via {interp}" if ver else "",
"no beat detection — cuts cannot be synced to the track")
# Optional: each names precisely the capability lost, so the agent can
# tell the user what degrades instead of quietly choosing a lesser path.
p, v = which_version("auto-editor", ["--version"])
add("auto-editor", False, p, v,
"tighten_vo.py uses its ffmpeg silencedetect path (measured equivalent)")
p, v = which_version("deepFilter", ["--help"])
add("deepFilter", False, p, "",
"clean_audio.py uses ffmpeg afftdn — audibly weaker on noisy rooms")
p, v = which_version("node", ["--version"])
add("node", False, p, v,
"make_cards.py cannot run — build cards as stills and use Ken Burns")
ver, interp = python_module("moviepy")
add("moviepy", False, ver, ver or "",
"render_moviepy.py unavailable — no engine A/B (ffmpeg still renders)")
p, v = which_version("yt-dlp", ["--version"])
add("yt-dlp", False, p, v, "fetch_music.py cannot pull audio from a link")
ver, interp = python_module("mlx_whisper")
add("mlx-whisper", False, ver, ver or "",
"no local transcription — captions must come from elsewhere, and "
"talking-head reels are watched muted" if mode == "talking-head"
else "no local transcription — captions must come from elsewhere")
ver, interp = python_module("Vision")
add("apple vision", False, ver, "" if not ver else "pyobjc",
"no subject detection — build_cut_plan --score-in-points and "
"detect_subjects.py are unavailable, in-points stay positional")
ver, interp = python_module("Quartz")
add("pyobjc quartz", False, ver, "" if not ver else "pyobjc",
"capture_window.py cannot enumerate windows — app b-roll must be "
"screen-recorded by hand")
osapp = "/Applications/Openscreen.app/Contents/MacOS/OpenScreen"
add("openscreen", False, osapp if os.path.exists(osapp) else "", "",
"no real-time screencasts — capture_window.py still records stepped UI "
"at 3-5 fps, which is right for agent-driven screens and a slideshow "
"for live motion")
p, v = which_version("magick", ["-version"])
add("imagemagick", False, p, v,
"gen_captions.py cannot render styled cards — only plain "
"render_reel.py --srt captions")
key = next((k for k in ("GEMINI_API_KEY", "GOOGLE_AI_API_KEY",
"GOOGLE_API_KEY") if os.environ.get(k)), None)
add("gemini api key", False, key, key or "",
"no hook-copy drafts or stylized thumbnails")
return rows
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--mode", choices=("music-video", "talking-head"),
default="music-video")
ap.add_argument("--json", action="store_true")
args = ap.parse_args()
rows = check(args.mode)
missing_required = [r for r in rows if r["required"] and not r["present"]]
missing_optional = [r for r in rows if not r["required"] and not r["present"]]
if args.json:
print(json.dumps({"mode": args.mode, "ok": not missing_required,
"missing_required": [r["name"] for r in missing_required],
"missing_optional": [r["name"] for r in missing_optional],
"checks": rows}, indent=2))
else:
print(f"Dependency check — {args.mode}\n")
caps_ok = [r["name"].split(":", 1)[1] for r in rows
if ":" in r["name"] and r["present"]]
if caps_ok:
print(f" OK ffmpeg build carries {len(caps_ok)} needed filters/encoders")
for r in rows:
if ":" in r["name"] and r["present"]:
continue
mark = "OK " if r["present"] else ("MISS" if r["required"] else "-- ")
tag = "required" if r["required"] else "optional"
print(f" {mark} {r['name']:<16} {tag:<9} {r['detail']}")
if missing_optional:
print("\nUnavailable, and what each costs:")
for r in missing_optional:
print(f" - {r['name']}: {r['without_it']}")
if missing_required:
print("\nMISSING REQUIRED — do not start the pipeline:")
for r in missing_required:
print(f" - {r['name']}: {r['without_it']}")
print("\n ffmpeg/ffprobe: brew install ffmpeg")
print(" .venv + deps: python3 -m venv .venv && "
".venv/bin/pip install librosa soundfile")
if missing_required:
sys.exit(1)
if __name__ == "__main__":
main().tessl-plugin
skills
reel-builder
assets
references
scripts
yap-writer