#!/usr/bin/env python3 """Dialogue audit for talking-head cuts — the four defects TJ had to catch by hand on 2026-09-05. usage: dialogue_audit.py [--parts DIR] [--fix-out cuts_fixed.json] cuts.json : list of [section, id, in, out] in seconds on the SAME clock as words.json (in may be a string "a–b + c–d" for already-spliced parts; those are audited by their rendered part only) words.json : list of [start, end, word] — word-level whisper output, one continuous clock --parts : directory of rendered parts .mp4 — transcribes the first and last 2.5 s of each and checks the first/last expected word is present (catches clipped words AND bloopers at the head) Checks 1 OUT clips the last word : out < last word end + 0.15 2 IN inside a word : a word starts before `in` and ends after it 3 mid-sentence pause : consecutive word STARTS > 1.5 s apart with no sentence punctuation before (whisper word END times stretch across pauses — never use them for gaps) 4 blooper at the head : the first two words recur within 4 s of the in-point (a restart) 5 head/tail transcription : with --parts, whisper-small on the first/last 2.5 s of the rendered part With --fix-out, writes corrected in/out points: out = last word end + 0.25 (capped 0.06 before the next word), in = first word start − 0.10 (or the midpoint if the previous word abuts). Pauses and bloopers are reported, not auto-fixed — cutting them is a jump cut and TJ decides. """ import json, sys, re, subprocess, os, tempfile def norm(s): return re.sub(r"[^a-z0-9]","",s.lower()) def main(): a=sys.argv[1:]; cuts=json.load(open(a[0])); words=[tuple(w) for w in json.load(open(a[1]))]; words.sort() parts=a[a.index("--parts")+1] if "--parts" in a else None; fixout=a[a.index("--fix-out")+1] if "--fix-out" in a else None fixed=[]; nflag=0 for c in cuts: sec,id_,ci,co=c[:4] if isinstance(ci,str) or not isinstance(co,(int,float)): fixed.append(c); continue ci=float(ci); co=float(co); flags=[] ws=[w for w in words if ci-0.05<=w[0]=co]; nxt=nxt[0] if nxt else None if co1.5 and not w1[2].endswith((".","?","!"))) or g>2.4: flags.append(f"PAUSE {g:.1f}s '{w1[2]}'→'{w2[2]}' at {w1[0]:.2f}") head=[norm(w[2]) for w in ws if w[0]=4: bg=head[0]+head[1] for k in range(1,len(head)-1): if head[k]+head[k+1]==bg: flags.append(f"BLOOPER: '{ws[0][2]} {ws[1][2]}' repeats at {ws[k][0]:.2f} — start there") nb=min(last[1]+0.25, nxt[0]-0.06) if nxt else last[1]+0.25; nb=max(nb,co) prev=[w for w in words if w[1]<=first[0]]; prev=prev[-1] if prev else None na=first[0]-0.10 if prev and na