help quicky produce instagram reels and youtube shorts
80
93%
Does it follow best practices?
Impact
47%
1.95xAverage score across 1 eval scenario
Passed
No known issues
#!/usr/bin/env python3
"""Pipeline state machine for a reel project — status, next step, gate log.
Run from the project folder. Stage completion is detected from artifacts on
disk (never from memory), so status can't drift from reality; only things
disk can't know live in state.json: gate decisions, target duration, and the
waiting-on-NLE flag. Staleness chains through file mtimes — edit
plan/cut_plan.json and the preview, master, and exports all flip to STALE
automatically.
Commands:
pipeline.py init --target-duration 30 scaffold folders + project.md + state.json
pipeline.py status full stage table, gates, warnings
pipeline.py next the single next action (command or gate)
pipeline.py gate 1 presented log that plan+preview went to the human
pipeline.py gate 1 approved refuses if the preview is stale
pipeline.py gate 1 nle-wait pipeline paused: human editing in NLE —
cut_plan.json must NOT be touched
pipeline.py gate 1 reopen back to pending (also: after nle import)
pipeline.py gate 2 presented|approved|reopen
The agent ritual: `status` at session start (with project.md), `next` when
unsure, `gate` at every human decision. Creative judgment (what to reject,
hook picks, pacing feedback) is yours, not this script's.
"""
import argparse, json, shutil, sys, time
from pathlib import Path
VIDEO_EXT = {".mp4", ".mov", ".m4v", ".mts", ".avi", ".mkv"}
AUDIO_EXT = {".mp3", ".m4a", ".wav", ".aac", ".flac", ".ogg"}
STATE = Path("state.json")
# ---------- state ----------
def load_state():
if STATE.exists():
return json.loads(STATE.read_text())
return {"target_duration": 30, "gates": {}}
def save_state(st):
tmp = STATE.with_suffix(".json.tmp")
tmp.write_text(json.dumps(st, indent=2))
tmp.rename(STATE)
# ---------- artifact probes ----------
def mtime(p):
return p.stat().st_mtime
def newest(pattern):
files = [p for p in Path().glob(pattern) if p.is_file()]
return max(files, key=mtime) if files else None
def raw_clips():
raw = Path("raw")
if not raw.exists():
return []
return sorted(p for p in raw.rglob("*") if p.suffix.lower() in VIDEO_EXT)
def probed_clips():
cj = Path("work/clips.json")
if not cj.exists():
return None
try:
clips = json.loads(cj.read_text())["clips"]
except (json.JSONDecodeError, KeyError):
return None
return [c for c in clips if "error" not in c]
def music_file():
m = Path("music")
if not m.exists():
return None
files = sorted(p for p in m.iterdir() if p.suffix.lower() in AUDIO_EXT)
return files[0] if files else None
# ---------- stages ----------
# each check returns (status, detail, action)
# status: "done" | "stale" | "ready" | "blocked"
def check_probe(st):
raws = raw_clips()
cj = Path("work/clips.json")
cmd = (f"python3 scripts/probe_clips.py raw/ --target-duration "
f"{st['target_duration']:g} --out work/clips.json")
if not raws:
return ("blocked", "no clips in raw/ — get footage from the user "
"(or symlink: ln -s /Volumes/… raw)", None)
if not cj.exists() or probed_clips() is None:
return ("ready", f"{len(raws)} raw clips await probing", cmd)
if max(mtime(p) for p in raws) > mtime(cj):
return ("stale", "raw/ changed after last probe — re-probe", cmd)
clips = probed_clips()
warn = sum(1 for c in clips if "warning" in c)
d = f"{len(clips)} usable clips"
if warn:
d += f", {warn} duration outlier(s) — extract segments before normalizing"
return ("done", d, None)
def check_inspect(st):
clips = probed_clips()
cmd = "python3 scripts/contact_sheet.py work/clips.json --outdir work/sheets"
if not clips:
return ("blocked", "needs probe", None)
missing = [c for c in clips
if not Path(f"work/sheets/{Path(c['file']).stem}_sheet.png").exists()]
if missing:
return ("ready", f"{len(missing)}/{len(clips)} sheets missing — "
"generate, then VIEW every sheet before planning", cmd)
return ("done", f"{len(clips)}/{len(clips)} sheets (did you look at them?)", None)
def check_normalize(st):
clips = probed_clips()
cmd = "python3 scripts/normalize_clips.py work/clips.json --outdir work/mezz"
if not clips:
return ("blocked", "needs probe", None)
missing, stale = [], []
for c in clips:
mz = Path(f"work/mezz/{Path(c['file']).stem}.mp4")
if not mz.exists():
missing.append(c)
elif Path(c["file"]).exists() and mtime(Path(c["file"])) > mtime(mz):
stale.append(c)
if missing:
return ("ready", f"{len(missing)}/{len(clips)} mezzanine files to render "
"(wait for process exit, not files)", cmd)
if stale:
return ("stale", f"{len(stale)} source clip(s) newer than mezzanine", cmd)
return ("done", f"{len(clips)} mezzanine files", None)
def check_beats(st):
m = music_file()
bj = Path("work/beats.json")
if not m:
return ("blocked", "no music in music/ — ASK THE USER: drop a file, "
"YouTube via scripts/fetch_music.py, or --bpm fixed grid", None)
cmd = f"python3 scripts/detect_beats.py '{m}' --out work/beats.json"
if not bj.exists():
return ("ready", f"track: {m.name}", cmd)
if mtime(m) > mtime(bj):
return ("stale", "music changed after beat detection", cmd)
return ("done", f"{m.name}", None)
def check_plan(st):
plan = Path("plan/cut_plan.json")
cmd = (f"python3 scripts/build_cut_plan.py work/clips.json work/beats.json "
f"--target-duration {st['target_duration']:g} --out plan/cut_plan.json "
"# add --hook/--close from contact-sheet picks")
deps = [Path("work/clips.json"), Path("work/beats.json")]
if not all(d.exists() for d in deps):
return ("blocked", "needs probe + beats", None)
if not plan.exists():
return ("ready", "propose the cut plan (use --hook/--close)", cmd)
return ("done", "plan/cut_plan.json (human-editable)", None)
def check_preview(st):
plan = Path("plan/cut_plan.json")
pv = newest("preview/draft_v*.mp4")
n = int(pv.stem.split("_v")[-1]) + 1 if pv else 1
cmd = (f"python3 scripts/render_reel.py plan/cut_plan.json --preview "
f"--out preview/draft_v{n}.mp4 && python3 scripts/check_cuts.py "
f"plan/cut_plan.json preview/draft_v{n}.mp4 --out work/qc_v{n}")
if not plan.exists():
return ("blocked", "needs cut plan", None)
if not pv:
return ("ready", "render draft + QC before showing the human", cmd)
if mtime(plan) > mtime(pv):
return ("stale", f"cut plan edited after {pv.name} — re-render + QC", cmd)
qc = newest("work/qc_v*.json")
d = pv.name
if qc and mtime(qc) >= mtime(pv):
problems = json.loads(qc.read_text()).get("problems", [])
d += f", QC: {len(problems)} problem(s)" if problems else ", QC clean"
else:
d += ", QC MISSING — run check_cuts.py"
return ("done", d, None)
def check_master(st):
plan = Path("plan/cut_plan.json")
mst = Path("work/master.mp4")
cmd = ("python3 scripts/render_reel.py plan/cut_plan.json --grade gritty "
"--out work/master.mp4 && python3 scripts/check_cuts.py "
"plan/cut_plan.json work/master.mp4 --out work/qc_master")
if gate_state(st, 1) != "approved":
return ("blocked", "needs Gate 1 approval", None)
if not mst.exists():
return ("ready", "final render with grade + QC", cmd)
if plan.exists() and mtime(plan) > mtime(mst):
return ("stale", "cut plan edited after master render", cmd)
return ("done", "work/master.mp4", None)
def check_export(st):
mst = Path("work/master.mp4")
cmd = ("python3 scripts/export_variants.py work/master.mp4 "
"--outdir exports/ --thumb-at 1.5")
if gate_state(st, 2) != "approved":
return ("blocked", "needs Gate 2 approval", None)
outs = [Path(f"exports/{n}.mp4") for n in ("tiktok", "ig_reels", "yt_shorts")]
if not all(p.exists() for p in outs):
return ("ready", "export platform variants", cmd)
if mst.exists() and any(mtime(mst) > mtime(p) for p in outs):
return ("stale", "master newer than exports", cmd)
styled = len(list(Path("exports").glob("thumbnail-*.jpg")))
d = "tiktok / ig_reels / yt_shorts + thumbnail"
if styled:
d += f" (+{styled} styled — human picks the cover)"
return ("done", d, None)
STAGES = [("probe", check_probe), ("inspect", check_inspect),
("normalize", check_normalize), ("beats", check_beats),
("plan", check_plan), ("preview", check_preview),
("master", check_master), ("export", check_export)]
# ---------- gates ----------
def gate_state(st, n):
return st.get("gates", {}).get(str(n), {}).get("state", "pending")
def gate_ready(st, n):
"""Is the gate's input fresh enough to present/approve?"""
if n == 1:
s, _, _ = check_preview(st)
return s == "done", "preview missing or stale — re-render + QC first"
s, _, _ = check_master(st)
return s == "done", "master missing or stale — re-render + QC first"
def cmd_gate(st, n, action):
gates = st.setdefault("gates", {})
g = gates.setdefault(str(n), {})
cur = g.get("state", "pending")
if action in ("presented", "approved"):
ok, why = gate_ready(st, n)
if not ok:
sys.exit(f"Refusing gate {n} '{action}': {why}")
if action == "nle-wait":
if n != 1:
sys.exit("NLE round trip is a Gate 1 flow")
plan = Path("plan/cut_plan.json")
g["plan_mtime_at_wait"] = mtime(plan) if plan.exists() else 0
if action == "reopen":
action = "pending"
g["state"] = action if action != "presented" else "presented"
g["at"] = time.strftime("%Y-%m-%d %H:%M:%S")
save_state(st)
print(f"Gate {n}: {cur} -> {g['state']}")
if g["state"] == "nle-wait":
print("PIPELINE PAUSED. Do not render, re-plan, or touch "
"plan/cut_plan.json until the edited fcpxml returns "
"(then: import_timeline.py + `gate 1 reopen`).")
if g["state"] == "approved" and n == 1:
print("Gate 1 approved — next: final render (see `pipeline.py next`).")
if g["state"] == "approved" and n == 2:
print("Gate 2 approved — next: export variants.")
# ---------- commands ----------
def warnings(st):
w = []
g1 = st.get("gates", {}).get("1", {})
if g1.get("state") == "nle-wait":
plan = Path("plan/cut_plan.json")
if plan.exists() and mtime(plan) > g1.get("plan_mtime_at_wait", 0) + 1:
w.append("cut_plan.json CHANGED while waiting on the NLE edit — "
"the human's edit may be clobbered on import. Reconcile "
"before proceeding.")
else:
w.append("waiting on NLE edit — hands off plan/cut_plan.json "
"(import_timeline.py when the fcpxml comes back, "
"then `gate 1 reopen`)")
for n in (1, 2):
if gate_state(st, n) == "approved":
ok, _ = gate_ready(st, n)
if not ok:
w.append(f"Gate {n} was approved but its input went stale — "
f"re-render and re-confirm (`gate {n} reopen`)")
return w
def next_action(st):
if st.get("gates", {}).get("1", {}).get("state") == "nle-wait":
return ("WAITING on the human's NLE edit. When the fcpxml returns: "
"python3 scripts/import_timeline.py <edited.fcpxml> --out "
"plan/cut_plan.json --base-plan plan/cut_plan.json, "
"then `pipeline.py gate 1 reopen`")
for name, check in STAGES:
s, detail, cmd = check(st)
if s in ("ready", "stale"):
return f"[{name}] {detail}\n {cmd}" if cmd else f"[{name}] {detail}"
if s == "blocked" and name in ("probe", "beats"):
return f"[{name}] {detail}" # human-input blockers surface immediately
if name == "preview" and s == "done":
gs = gate_state(st, 1)
if gs == "pending":
return ("GATE 1: present plan + preview together to the human "
"(then `pipeline.py gate 1 presented`)")
if gs == "presented":
return ("GATE 1: awaiting the human — approve / edits / NLE "
"(`gate 1 approved` | edit plan | `gate 1 nle-wait`)")
if name == "master" and s == "done" and gate_state(st, 2) in ("pending", "presented"):
gs = gate_state(st, 2)
return ("GATE 2: show the master to the human "
"(`pipeline.py gate 2 presented`)" if gs == "pending" else
"GATE 2: awaiting the human (`gate 2 approved` or loop back)")
return "All stages done — deliver exports/ to the user and update project.md."
MARK = {"done": ".", "ready": ">", "stale": "!", "blocked": "x"}
def cmd_status(st):
print(f"Reel pipeline — {Path.cwd().name} "
f"(target {st['target_duration']:g}s)\n")
for name, check in STAGES:
s, detail, _ = check(st)
print(f" {MARK[s]} {name:<10} {s:<8} {detail}")
print(f"\n Gate 1 (plan+preview): {gate_state(st, 1)} "
f"Gate 2 (master): {gate_state(st, 2)}")
for w in warnings(st):
print(f"\n WARNING: {w}")
print(f"\nNext: {next_action(st)}")
def cmd_init(args):
if STATE.exists():
sys.exit("state.json already exists — this is already a reel project "
"(use `pipeline.py status`)")
for d in ("raw", "music", "work", "plan", "preview", "exports"):
Path(d).mkdir(exist_ok=True)
tmpl = Path(__file__).resolve().parent.parent / "assets/project_template.md"
if not Path("project.md").exists() and tmpl.exists():
shutil.copy(tmpl, "project.md")
save_state({"target_duration": args.target_duration, "gates": {},
"created": time.strftime("%Y-%m-%d %H:%M:%S")})
print(f"Project scaffolded (target {args.target_duration:g}s). "
"Fill project.md, drop footage in raw/ (or symlink), music in music/.")
def main():
ap = argparse.ArgumentParser()
sub = ap.add_subparsers(dest="cmd", required=True)
p_init = sub.add_parser("init")
p_init.add_argument("--target-duration", type=float, default=30)
sub.add_parser("status")
sub.add_parser("next")
p_gate = sub.add_parser("gate")
p_gate.add_argument("n", type=int, choices=(1, 2))
p_gate.add_argument("action", choices=("presented", "approved",
"nle-wait", "reopen"))
args = ap.parse_args()
if args.cmd == "init":
return cmd_init(args)
st = load_state()
if args.cmd == "status":
cmd_status(st)
elif args.cmd == "next":
print(next_action(st))
elif args.cmd == "gate":
cmd_gate(st, args.n, args.action)
if __name__ == "__main__":
main()