#!/usr/bin/env bash
set -euo pipefail
R=/data/video-pipeline/GacMaiAudio/project/gacmai_20260718_091744
A="$R/artifacts"; D="$R/assets/audio"; M="$A/tts_manifest.json"
python3 - <<'PY'
import json,hashlib,wave
from pathlib import Path
R=Path('/data/video-pipeline/GacMaiAudio/project/gacmai_20260718_091744');A=R/'artifacts';D=R/'assets/audio';m=json.load(open(A/'tts_manifest.json'))
assert m['status']=='passed' and m['completed_count']==m['planned_count']==343 and len(m['chunks'])==343
out=D/'narration_raw.wav';frames=0
first=Path(m['chunks'][0]['path'])
with wave.open(str(first),'rb') as wi:
 assert (wi.getnchannels(),wi.getsampwidth(),wi.getframerate(),wi.getcomptype())==(1,2,48000,'NONE')
with wave.open(str(out),'wb') as wout:
 wout.setnchannels(1);wout.setsampwidth(2);wout.setframerate(48000)
 for i,row in enumerate(m['chunks']):
  p=Path(row['path']);assert hashlib.sha256(p.read_bytes()).hexdigest()==row['audio_sha256']
  with wave.open(str(p),'rb') as wi:
   cur=(wi.getnchannels(),wi.getsampwidth(),wi.getframerate(),wi.getcomptype())
   assert cur==(1,2,48000,'NONE'),(i,cur)
   data=wi.readframes(wi.getnframes());wout.writeframes(data);frames+=wi.getnframes()
print(frames)
PY
ffmpeg -y -hide_banner -loglevel error -i "$D/narration_raw.wav" -af loudnorm=I=-16:TP=-1.5:LRA=11:linear=false -c:a pcm_s16le -ar 48000 -ac 1 "$D/narration_final.wav"
python3 - <<'PY'
from pathlib import Path
import subprocess,json,hashlib,re,datetime
R=Path('/data/video-pipeline/GacMaiAudio/project/gacmai_20260718_091744');A=R/'artifacts';p=R/'assets/audio/narration_final.wav';m=A/'tts_manifest.json'
probe=json.loads(subprocess.check_output(['ffprobe','-v','error','-show_streams','-show_format','-of','json',str(p)],text=True));dec=subprocess.run(['ffmpeg','-v','error','-i',str(p),'-f','null','-']);cp=subprocess.run(['ffmpeg','-hide_banner','-nostats','-i',str(p),'-af','loudnorm=I=-16:TP=-1.5:LRA=11:print_format=json','-f','null','-'],capture_output=True,text=True);mm=re.findall(r'\{\s*"input_i".*?\}',cp.stderr,re.S);l=json.loads(mm[-1]);st=next(x for x in probe['streams'] if x['codec_type']=='audio');errs=[]
if st['codec_name']!='pcm_s16le' or st['sample_rate']!='48000' or st['channels']!=1:errs.append('format')
if dec.returncode:errs.append('decode')
I=float(l['input_i']);TP=float(l['input_tp'])
if abs(I+16)>0.3:errs.append('loudness')
if TP>-1.4:errs.append('true_peak')
out={'status':'passed' if not errs else 'failed','story_sha256':'fd016b65cfd699e9d4e97c5e491894135117a12d56c2b467204ab68efd68be98','tts_manifest_sha256':hashlib.sha256(m.read_bytes()).hexdigest(),'audio_sha256':hashlib.sha256(p.read_bytes()).hexdigest(),'duration':float(probe['format']['duration']),'codec':st['codec_name'],'sample_rate':st['sample_rate'],'channels':st['channels'],'integrated_lufs':I,'true_peak_dbfs':TP,'full_decode':'passed' if not dec.returncode else 'failed','errors':errs,'checked_at':datetime.datetime.now(datetime.timezone.utc).isoformat()};(A/'audio_master_qc.json').write_text(json.dumps(out,indent=2)+'\n');print(json.dumps(out));raise SystemExit(1 if errs else 0)
PY
