from pathlib import Path
import subprocess, json, glob

BASE=Path('/home/kevin/shared/video-packs/ia-tomo-control-V1-production')
V6=Path('/home/kevin/shared/video-packs/ia-tomo-control-V6-do-it-right')
WORK=Path('/home/kevin/shared/video-packs/ia-tomo-control-V7-lipsync-correct')
for d in ['segments','video','short','qa','logs','manifests']:
    (WORK/d).mkdir(parents=True, exist_ok=True)

def first(pat):
    xs=sorted(glob.glob(str(pat)))
    return Path(xs[0]) if xs else None

def dur(p):
    return float(subprocess.check_output(['ffprobe','-v','error','-show_entries','format=duration','-of','csv=p=0',str(p)], text=True).strip())

def run(cmd, log):
    with open(log,'w') as f:
        p=subprocess.run(cmd, stdout=f, stderr=f)
    if p.returncode:
        raise SystemExit(f'FAILED {log}: {cmd}')

AV=Path('/home/kevin/shared/video-packs/ia-tomo-control-V2-premium/assets/avatar-v2')
scenes={
 's01': dict(voice=first(BASE/'assets/runway-visual-voice/s01-voiceover-*.mp3'), avatar=AV/'s01-hook-avatar-v2.mp4', mode='fullscreen'),
 's02': dict(voice=first(BASE/'assets/runway-visual-voice/s02-voiceover-*.mp3'), avatar=AV/'s02-mobile-avatar-v2.mp4', mode='pip'),
 's03': dict(voice=None, avatar=None, mode='none'),
 's04': dict(voice=first(BASE/'assets/runway-visual-voice/s04-voiceover-*.mp3'), avatar=AV/'s04-defense-avatar-v2.mp4', mode='pip'),
 's05': dict(voice=None, avatar=None, mode='none'),
 's06': dict(voice=first(BASE/'assets/runway-visual-voice/s06-voiceover-*.mp3'), avatar=AV/'s06-cyber-avatar-v2.mp4', mode='pip'),
 's07': dict(voice=None, avatar=None, mode='none'),
 's08': dict(voice=None, avatar=None, mode='none'),
 's09': dict(voice=first(BASE/'assets/runway-visual-voice/s09-voiceover-*.mp3'), avatar=AV/'s09-close-avatar-v2.mp4', mode='fullscreen'),
}
manifest=[]; outsegs=[]
for sid in [f's{i:02d}' for i in range(1,10)]:
    v6seg=V6/'segments'/f'{sid}_v6.mp4'
    bg=V6/'chunks'/f'{sid}_bg.mp4'
    info=scenes[sid]
    if info['mode']=='none' or not info['avatar'] or not info['avatar'].exists():
        out=WORK/'segments'/f'{sid}_v7.mp4'
        run(['ffmpeg','-y','-i',str(v6seg),'-c:v','libx264','-preset','veryfast','-crf','18','-r','25','-c:a','aac','-b:a','192k','-ar','48000','-ac','2',str(out)], WORK/'logs'/f'{sid}_copy_reencode.log')
        manifest.append({'scene':sid,'source':'v6-no-avatar-or-no-change','segment':str(out)})
    else:
        voice=info['voice']; avatar=info['avatar']; ad=dur(voice); avd=min(dur(avatar), ad)
        intro=WORK/'segments'/f'{sid}_v7_avatar_intro.mp4'
        if info['mode']=='fullscreen':
            vf='scale=1920:1080:force_original_aspect_ratio=increase,crop=1920:1080,fps=25,format=yuv420p'
            run(['ffmpeg','-y','-i',str(avatar),'-t',f'{avd:.3f}','-vf',vf,'-map','0:v','-map','0:a','-c:v','libx264','-preset','veryfast','-crf','18','-r','25','-c:a','aac','-b:a','192k','-ar','48000','-ac','2',str(intro)], WORK/'logs'/f'{sid}_avatar_intro_fullscreen.log')
        else:
            # PIP clip uses the avatar clip's embedded audio, not the scene master audio. No loop, no offset.
            vf="[0:v]trim=0:%0.3f,setpts=PTS-STARTPTS,scale=1920:1080:force_original_aspect_ratio=increase,crop=1920:1080,fps=25,format=rgba[bg];[1:v]setpts=PTS-STARTPTS,scale=500:282:force_original_aspect_ratio=increase,crop=500:282,fps=25,format=rgba[pip];color=c=0x07101b:s=524x306:d=%0.3f,format=rgba[frame];[frame][pip]overlay=12:12[pipf];[bg][pipf]overlay=W-w-56:H-h-56:format=auto,format=yuv420p[v]"%(avd,avd)
            run(['ffmpeg','-y','-i',str(bg),'-i',str(avatar),'-filter_complex',vf,'-map','[v]','-map','1:a','-t',f'{avd:.3f}','-c:v','libx264','-preset','veryfast','-crf','18','-r','25','-c:a','aac','-b:a','192k','-ar','48000','-ac','2',str(intro)], WORK/'logs'/f'{sid}_avatar_intro_pip.log')
        rem=max(0.05, ad-avd)
        rest=WORK/'segments'/f'{sid}_v7_rest.mp4'
        run(['ffmpeg','-y','-ss',f'{avd:.3f}','-i',str(bg),'-ss',f'{avd:.3f}','-i',str(voice),'-t',f'{rem:.3f}','-map','0:v','-map','1:a','-c:v','libx264','-preset','veryfast','-crf','18','-r','25','-c:a','aac','-b:a','192k','-ar','48000','-ac','2','-shortest',str(rest)], WORK/'logs'/f'{sid}_rest_trimmed.log')
        lst=WORK/'segments'/f'{sid}_parts.txt'; lst.write_text(f"file '{intro}'\nfile '{rest}'\n")
        out=WORK/'segments'/f'{sid}_v7.mp4'
        run(['ffmpeg','-y','-f','concat','-safe','0','-i',str(lst),'-c','copy',str(out)], WORK/'logs'/f'{sid}_concat.log')
        manifest.append({'scene':sid,'mode':info['mode'],'avatar':str(avatar),'avatarDurationUsed':avd,'audioRule':'avatar intro uses embedded avatar audio; rest uses scene voice trimmed by same duration','segment':str(out)})
    outsegs.append(out)

concat=WORK/'segments/concat_v7.txt'; concat.write_text(''.join(f"file '{p}'\n" for p in outsegs))
raw=WORK/'video/la-ia-ya-tomo-el-control-V7-lipsync-correct-raw.mp4'
run(['ffmpeg','-y','-f','concat','-safe','0','-i',str(concat),'-c','copy',str(raw)], WORK/'logs/render_long_raw.log')
fixed=WORK/'video/la-ia-ya-tomo-el-control-V7-lipsync-correct-fixed.mp4'
run(['ffmpeg','-y','-fflags','+genpts','-i',str(raw),'-vf','fps=25,format=yuv420p','-af','aresample=async=1:first_pts=0','-c:v','libx264','-preset','veryfast','-crf','19','-c:a','aac','-b:a','192k','-ar','48000','-ac','2',str(fixed)], WORK/'logs/fix_long.log')
short=WORK/'short/la-ia-ya-tomo-el-control-V7-lipsync-correct-short-9x16.mp4'
run(['ffmpeg','-y','-i',str(fixed),'-t','59','-vf','scale=1920:1080:force_original_aspect_ratio=increase,crop=1920:1080,scale=1080:1920:force_original_aspect_ratio=increase,crop=1080:1920,setsar=1,format=yuv420p','-c:v','libx264','-preset','veryfast','-crf','19','-c:a','aac','-b:a','192k','-ar','48000','-ac','2',str(short)], WORK/'logs/render_short.log')
(WORK/'manifests/v7-lipsync-correct-manifest.json').write_text(json.dumps({'version':'V7 lipsync correct','invalidates':'V6 due to unsynced PIP','rule':'Avatar is shown only during its own embedded synchronized audio. No looping. No offset PIP over unrelated master audio. Remaining scene voice is trimmed by avatar duration to avoid duplicate narration.','long':str(fixed),'short':str(short),'scenes':manifest}, indent=2, ensure_ascii=False))
print(fixed); print(short)
