master
John Lauer Preserve validated CRR thermal routing recipe and narrated demonstration 92d2870 1mo ago
from pathlib import Path
import json,subprocess,os,hashlib
P=Path(__file__).resolve().parent/'edit';cues=json.loads((P/'script.json').read_text());dur=json.loads((P/'edit.json').read_text())['duration'];samples=round(dur*48000)
args=[];filters=[];timings=[]
for i,c in enumerate(cues):
 d=float(subprocess.check_output(['ffprobe','-v','error','-show_entries','format=duration','-of','default=nw=1:nk=1',c['file']]));tempo=max(1,d/(c['end']-c['start']-.15));assert tempo<1.13,(i,d,tempo)
 args+=['-i',c['file']];filters.append(f'[{i}:a]atempo={tempo},aresample=48000,adelay={round(c["start"]*1000)}:all=1[a{i}]');timings.append({**c,'duration':d,'tempo':tempo,'actualEnd':c['start']+d/tempo})
filters.append(''.join(f'[a{i}]' for i in range(len(cues)))+f'amix=inputs={len(cues)}:normalize=0,apad=whole_len={samples},atrim=end_sample={samples},asetpts=N/SR/TB[mix]')
raw=P/'narration.wav';subprocess.run(['ffmpeg','-v','error',*args,'-filter_complex',';'.join(filters),'-map','[mix]','-c:a','pcm_s24le','-y',str(raw)],check=True)
r=subprocess.run(['ffmpeg','-hide_banner','-i',str(raw),'-af','loudnorm=I=-16:TP=-1.5:LRA=7:print_format=json','-f','null','-'],capture_output=True,text=True,check=True);m=json.loads(r.stderr[r.stderr.rfind('{'):]);flt=f'loudnorm=I=-16:TP=-1.5:LRA=7:measured_I={m["input_i"]}:measured_TP={m["input_tp"]}:measured_LRA={m["input_lra"]}:measured_thresh={m["input_thresh"]}:offset={m["target_offset"]}:linear=true'
out=P/'fusion-bq25792-astra-thermal-narrated.mp4';subprocess.run(['ffmpeg','-v','error','-i',str(P/'fusion-bq25792-astra-thermal-silent.mp4'),'-i',str(raw),'-map','0:v','-map','1:a','-c:v','copy','-af',flt,'-c:a','aac','-b:a','192k','-ar','48000','-ac','2','-movflags','+faststart','-y',str(out)],check=True)
subprocess.run(['ffmpeg','-v','error','-i',str(out),'-f','null','-'],check=True)
r=subprocess.run(['ffmpeg','-hide_banner','-i',str(out),'-vn','-af','loudnorm=I=-16:TP=-1.5:LRA=7:print_format=json','-f','null','-'],capture_output=True,text=True,check=True);m=json.loads(r.stderr[r.stderr.rfind('{'):]);assert float(m['input_tp'])<0
probe=json.loads(subprocess.check_output(['ffprobe','-v','error','-show_streams','-show_format','-of','json',str(out)]));(P/'verification.json').write_text(json.dumps(dict(cues=timings,loudness=m,probe=probe,fullDecode='passed',sha256=hashlib.sha256(out.read_bytes()).hexdigest()),indent=2));print(out,m)