#!/usr/bin/env python3
"""Проверяет общую длительность, последний кадр и последний аудиопакет."""
from __future__ import annotations
import argparse, json, subprocess, tempfile
from pathlib import Path

def run(c): return subprocess.run(c,capture_output=True,text=True)
def main():
    ap=argparse.ArgumentParser(); ap.add_argument('video'); ap.add_argument('timeline'); ap.add_argument('--tolerance',type=float,default=0.10)
    ap.add_argument('--report',default='full_end_report.json'); args=ap.parse_args()
    t=json.loads(Path(args.timeline).read_text(encoding='utf-8')); expected=float(t['expected_duration']); v=Path(args.video); errors=[]
    p=run(['ffprobe','-v','error','-show_streams','-show_format','-of','json',str(v)])
    if p.returncode: raise SystemExit(p.stderr)
    meta=json.loads(p.stdout); duration=float(meta['format'].get('duration') or 0)
    if abs(duration-expected)>args.tolerance: errors.append(f'Длительность {duration:.3f}, ожидалось {expected:.3f}')
    vs=[s for s in meta['streams'] if s.get('codec_type')=='video']; au=[s for s in meta['streams'] if s.get('codec_type')=='audio']
    vd=float(vs[0].get('duration') or duration) if vs else 0; ad=float(au[0].get('duration') or duration) if au else 0
    if abs(vd-ad)>0.05: errors.append(f'Видео/аудио расходятся на {abs(vd-ad):.3f} с')
    # Декодируем последние 0.5 с и считаем кадры. Проба на -0.08 с ложно падала,
    # когда контейнер чуть длиннее видеопотока: точка поиска оказывалась за
    # последним кадром, и ffmpeg честно не выдавал ничего.
    with tempfile.TemporaryDirectory() as td:
        q=run(['ffmpeg','-v','error','-sseof','-0.5','-i',str(v),'-frames:v','15',
               '-f','image2',str(Path(td)/'last_%03d.png')])
        made=len(list(Path(td).glob('last_*.png')))
        if q.returncode or made==0:
            errors.append('Последние 0.5 с видео не декодируются')
    packets=run(['ffprobe','-v','error','-select_streams','a:0','-show_packets','-show_entries','packet=pts_time,duration_time','-of','json',str(v)])
    last_audio=None
    if packets.returncode==0:
        arr=json.loads(packets.stdout).get('packets',[])
        if arr:
            z=arr[-1]; last_audio=float(z.get('pts_time') or 0)+float(z.get('duration_time') or 0)
            if duration-last_audio>0.20: errors.append(f'Последний аудиопакет заканчивается за {duration-last_audio:.3f} с до конца')
    report={'duration':duration,'expected_duration':expected,'video_duration':vd,'audio_duration':ad,'last_audio_end':last_audio,
            'last_word_end':t.get('last_word_end'),'tail_after_speech':t.get('tail_after_speech'),'errors':errors,'status':'ok' if not errors else 'failed'}
    Path(args.report).write_text(json.dumps(report,ensure_ascii=False,indent=2),encoding='utf-8'); print(json.dumps(report,ensure_ascii=False,indent=2))
    if errors: raise SystemExit(2)
if __name__=='__main__': main()
