#!/usr/bin/env python3
from pathlib import Path
import json,subprocess,wave
P=Path(__file__).resolve().parents[1]
def duration(path):
 return float(subprocess.check_output(['ffprobe','-v','error','-show_entries','format=duration','-of','default=nk=1:nw=1',str(path)]))
main=P/'video/output/main.mp4';outro=P/'video/outro.mp4';out=P/'video/output/main-with-outro.mp4'
with wave.open(str(P/'audio/full_narration.wav'),'rb') as w:main_audio=w.getnframes()/w.getframerate()
main_video=duration(main);outro_d=duration(outro);normalize=max(0.0,main_audio-main_video);video_pad=normalize+1.0;segment_end=main_audio+1.0
cmd=['ffmpeg','-hide_banner','-loglevel','error','-y','-i',str(main),'-i',str(outro),'-filter_complex',f'[0:v]fps=30,scale=1920:1080,setsar=1,tpad=stop_mode=clone:stop_duration={video_pad:.9f},trim=duration={segment_end:.9f},setpts=PTS-STARTPTS[v0];[0:a]aresample=48000,apad=pad_dur=1,atrim=duration={segment_end:.9f},asetpts=PTS-STARTPTS[a0];[1:v]fps=30,scale=1920:1080,setsar=1,setpts=PTS-STARTPTS[v1];[1:a]aresample=48000,asetpts=PTS-STARTPTS[a1];[v0][a0][v1][a1]concat=n=2:v=1:a=1[v][a]','-map','[v]','-map','[a]','-c:v','libx264','-preset','medium','-crf','18','-c:a','aac','-b:a','192k','-movflags','+faststart',str(out)]
(P/'logs/boundary_assembly_command.json').write_text(json.dumps({'command':cmd,'main_video_duration':main_video,'main_audio_duration':main_audio,'normalized_tail':normalize,'hold':1.0,'expected_total':segment_end+outro_d},indent=2)+'\n')
subprocess.run(cmd,check=True);actual=duration(out);gate={'status':'PASS' if abs(actual-(segment_end+outro_d))<0.15 else 'FAIL','main_video_duration':main_video,'main_audio_duration':main_audio,'normalized_tail':normalize,'hold':1.0,'outro_duration':outro_d,'expected_total':segment_end+outro_d,'actual_total':actual,'delta':actual-(segment_end+outro_d)};(P/'logs/boundary_duration_gate.json').write_text(json.dumps(gate,indent=2)+'\n');print(json.dumps(gate));raise SystemExit(0 if gate['status']=='PASS' else 1)
