main
Caleb Version 0.3.0: new copper reader, dome via filter, every layer read from the photos, net evidence, measured confidence;… e874d7d
import subprocess,json,re,concurrent.futures as cf,imageio_ffmpeg
from script import SCENES
F=imageio_ffmpeg.get_ffmpeg_exe()
def go(s):
    n,title,pairs=s;lines=[a for a,_ in pairs];txt=' '.join(lines)
    subprocess.run(['adom-tts','say','--out',f'vo/{n}.mp3','--voice','en-US-AndrewNeural',txt],check=True,capture_output=True)
    subprocess.run([F,'-y','-v','error','-i',f'vo/{n}.mp3','-ar','48000','-ac','1',f'vo/{n}.wav'],check=True)
    e=subprocess.run([F,'-i',f'vo/{n}.wav','-af','silencedetect=noise=-40dB:d=0.12','-f','null','-'],capture_output=True,text=True).stderr
    st=[float(x) for x in re.findall(r'silence_start: ([\d.]+)',e)];en=[float(x) for x in re.findall(r'silence_end: ([\d.]+)',e)]
    D=re.search(r'Duration: (\d+):(\d+):([\d.]+)',e);dur=int(D[1])*3600+int(D[2])*60+float(D[3])
    t0=en[0] if st and st[0]<0.05 else 0.0
    gaps=[(a,b) for a,b in zip(st,en) if a>t0+.05]
    t1=dur
    if st and len(st)>len(en): t1=st[-1]
    elif gaps and gaps[-1][1]>=dur-.02: t1=gaps[-1][0];gaps=gaps[:-1]
    chars=[len(l) for l in lines];tot=sum(chars);cum=0;starts=[t0]
    for c in chars[:-1]:
        cum+=c;want=t0+(t1-t0)*cum/tot
        g=min(gaps,key=lambda g:abs((g[0]+g[1])/2-want)) if gaps else None
        starts.append(((g[0]+g[1])/2) if g and abs((g[0]+g[1])/2-want)<1.6 else want)
    return dict(id=n,title=title,lines=lines,written=[b or a for a,b in pairs],starts=starts,speech=[t0,t1],dur=dur,gaps=len(gaps))
with cf.ThreadPoolExecutor(6) as x:R=list(x.map(go,SCENES))
json.dump(R,open('vo/timing.json','w'),indent=1)
for r in R:print(r['id'],round(r['dur'],1),[round(s,1) for s in r['starts']])
print('total',sum(r['dur'] for r in R))