skill
Demo authoring skillpack
Public Made by Adomby adom
How Adom bridges give a guided, narrated, recorded demo on a user's desktop: real content, an instant remote, captions and TTS, windows forward once and sized into slots, every beat verified, motion that stays on the content, first-run hardening, recording with audio.
main
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
#!/usr/bin/env python3
"""Post-production for the KiCad demo recording.
usage: postprod.py <recording.webm|mp4> <rec_start_epoch_s> <timeline.json> <outdir>
timeline.json: {"prelude":[{"clip":path,"at":seconds_into_recording},...],
"beats":[{"step":..,"partAAt":epoch,"partBAt":epoch,"partBEndAt":epoch}],
"audio_dir":path, "cut_start":seconds}
Builds full.mp4 (trimmed at cut_start, all narration mixed at its true offsets) and
partb.mp4 (prelude + each beat's part B only, narration B aligned to each segment)."""
import json, subprocess, sys, os
rec, t0, tl, out = sys.argv[1], float(sys.argv[2]), json.load(open(sys.argv[3])), sys.argv[4]
REC_DUR = float(tl["rec_duration"]) # recorder-reported length; the pup webm carries no duration header
os.makedirs(out, exist_ok=True)
def dur(p): return float(subprocess.check_output(["ffprobe","-v","error","-show_entries","format=duration","-of","csv=p=0",p]).decode().strip())
A = tl["audio_dir"]; cut = float(tl.get("cut_start", 0))
# ---- full video: one filter graph, every clip delayed to its offset (relative to the cut)
inputs = ["-i", rec]; delays = []; n = 1
for c in tl["prelude"]:
inputs += ["-i", c["clip"]]; delays.append((n, c["at"] - cut)); n += 1
for b in tl["beats"]:
for part, key in (("a","partAAt"),("b","partBAt")):
clip = os.path.join(A, f"{b['step']}-{part}.mp3")
if b.get(key) and os.path.exists(clip):
inputs += ["-i", clip]; delays.append((n, b[key] - t0 - cut)); n += 1
fc = []; labels = []
for idx, (i, at) in enumerate(delays):
ms = max(0, int(at * 1000)); fc.append(f"[{i}:a]adelay={ms}|{ms}[a{idx}]"); labels.append(f"[a{idx}]")
fc.append("".join(labels) + f"amix=inputs={len(labels)}:normalize=0:dropout_transition=0,apad[aout]")
cmd = ["ffmpeg","-y","-v","error","-ss",str(cut)] + inputs + ["-filter_complex",";".join(fc),"-map","0:v","-map","[aout]",
"-vf","scale=1280:-2","-r","30","-fps_mode","cfr","-c:v","libx264","-preset","veryfast","-crf","26","-pix_fmt","yuv420p","-c:a","aac","-b:a","96k","-t",str(REC_DUR - cut),"-movflags","+faststart",os.path.join(out,"full.mp4")]
print("full:", subprocess.run(cmd, capture_output=True, text=True).stderr[-300:] or "ok")
# ---- part-B video: prelude segment + B segments
segs = [(cut, tl["prelude_end"] - cut if "prelude_end" in tl else None)]
pre_end = tl.get("prelude_end", (tl["beats"][0]["partAAt"] - t0) if tl["beats"] else cut)
segs = [(cut, pre_end)]
for b in tl["beats"]:
if b.get("partBAt") and b.get("partBEndAt"):
segs.append((b["partBAt"] - t0, b["partBEndAt"] - t0))
parts = []
for k, (s0, s1) in enumerate(segs):
p = os.path.join(out, f"seg{k}.mp4")
subprocess.run(["ffmpeg","-y","-v","error","-ss",str(s0),"-to",str(s1),"-i",rec,"-an","-vf","scale=1280:-2","-r","30","-fps_mode","cfr","-c:v","libx264","-preset","veryfast","-crf","26","-pix_fmt","yuv420p",p], check=False)
parts.append(p)
# audio for part-B video: prelude clips at (at - cut); beat B clips at each segment's start
offs = []; acc = 0.0
for k, (s0, s1) in enumerate(segs):
if k == 0:
for c in tl["prelude"]: offs.append((c["clip"], c["at"] - cut))
else:
b = tl["beats"][k-1]; clip = os.path.join(A, f"{b['step']}-b.mp3")
if os.path.exists(clip): offs.append((clip, acc))
acc += (s1 - s0)
lst = os.path.join(out, "segs.txt"); open(lst,"w").write("".join(f"file '{p}'\n" for p in parts))
vid = os.path.join(out, "partb-video.mp4"); subprocess.run(["ffmpeg","-y","-v","error","-f","concat","-safe","0","-i",lst,"-c","copy",vid], check=False)
inputs = ["-i", vid]; fc = []; labels = []
for idx, (clip, at) in enumerate(offs):
inputs += ["-i", clip]; ms = max(0, int(at*1000)); fc.append(f"[{idx+1}:a]adelay={ms}|{ms}[b{idx}]"); labels.append(f"[b{idx}]")
fc.append("".join(labels) + f"amix=inputs={len(labels)}:normalize=0:dropout_transition=0,apad[aout]")
cmd = ["ffmpeg","-y","-v","error"] + inputs + ["-filter_complex",";".join(fc),"-map","0:v","-map","[aout]","-c:v","copy","-c:a","aac","-b:a","96k","-t",str(acc),"-movflags","+faststart",os.path.join(out,"partb.mp4")]
print("partb:", subprocess.run(cmd, capture_output=True, text=True).stderr[-300:] or "ok")
for f in ("full.mp4","partb.mp4"):
p=os.path.join(out,f); print(f, os.path.getsize(p)//1024, "KB", round(dur(p),1), "s")