#!/usr/bin/env python3
"""Находит все исходные медиа, не выбирая самый большой файл молча."""
from __future__ import annotations
import argparse, hashlib, json, shutil, subprocess, tempfile, zipfile
from pathlib import Path

VIDEO_EXTS={'.mp4','.mov','.mkv','.m4v','.avi','.webm','.mts','.m2ts'}
AUDIO_EXTS={'.wav','.mp3','.m4a','.aac','.flac','.ogg'}
RENDER_TOKENS=('final','edit','cut','render','preview','master','export','готов','финал','монтаж')

def sha256(p:Path)->str:
    h=hashlib.sha256()
    with p.open('rb') as f:
        for b in iter(lambda:f.read(1024*1024),b''): h.update(b)
    return h.hexdigest()

def probe(p:Path)->dict:
    q=subprocess.run(['ffprobe','-v','error','-show_streams','-show_format','-of','json',str(p)],capture_output=True,text=True)
    if q.returncode: return {'probe_error':q.stderr.strip()}
    d=json.loads(q.stdout); fmt=d.get('format',{}); streams=d.get('streams',[])
    v=next((s for s in streams if s.get('codec_type')=='video'),{})
    a=next((s for s in streams if s.get('codec_type')=='audio'),{})
    return {
        'duration':float(fmt.get('duration') or 0), 'width':v.get('width'), 'height':v.get('height'),
        'video_codec':v.get('codec_name'), 'fps':v.get('avg_frame_rate'), 'has_audio':bool(a),
        'audio_codec':a.get('codec_name') if a else None, 'sample_rate':a.get('sample_rate') if a else None,
    }

def extract(inp:Path,tmp:Path):
    if inp.is_dir(): return inp
    if inp.suffix.lower()=='.zip':
        with zipfile.ZipFile(inp) as z: z.extractall(tmp)
        return tmp
    import shutil as _shutil
    seven=_shutil.which('7z') or _shutil.which('7zz') or _shutil.which('7za')
    if seven:
        q=subprocess.run([seven,'x','-y',f'-o{tmp}',str(inp)],capture_output=True,text=True)
        if q.returncode==0: return tmp
    if inp.suffix.lower()=='.rar':
        try:
            import rarfile
            with rarfile.RarFile(inp) as r: r.extractall(tmp)
            return tmp
        except Exception as e:
            raise SystemExit(f'Не удалось распаковать RAR: {e}. Установи 7-Zip/unrar или передай папку.')
    raise SystemExit('Не удалось распаковать архив. Установи 7-Zip/unrar или передай папку.')

def main():
    ap=argparse.ArgumentParser()
    ap.add_argument('input'); ap.add_argument('--project',default='video_project')
    ap.add_argument('--accept-all',action='store_true',help='Скопировать все чистые видео-кандидаты в source/')
    args=ap.parse_args(); inp=Path(args.input).resolve(); project=Path(args.project).resolve()
    project.mkdir(parents=True,exist_ok=True); (project/'source').mkdir(exist_ok=True)
    with tempfile.TemporaryDirectory() as td:
        base=extract(inp,Path(td))
        media=sorted(p for p in base.rglob('*') if p.is_file() and p.suffix.lower() in VIDEO_EXTS|AUDIO_EXTS)
        if not media: raise SystemExit('Медиафайлы не найдены.')
        items=[]
        for p in media:
            name=p.name.lower(); suspected=any(t in name for t in RENDER_TOKENS)
            item={'original_path':str(p.relative_to(base)),'name':p.name,'suffix':p.suffix.lower(),'bytes':p.stat().st_size,
                  'sha256':sha256(p),'suspected_render':suspected,'kind':'video' if p.suffix.lower() in VIDEO_EXTS else 'audio'}
            item.update(probe(p)); items.append(item)
        candidates=[(p,it) for p,it in zip(media,items) if it['kind']=='video' and not it['suspected_render']]
        if args.accept_all:
            for i,(p,it) in enumerate(candidates,1):
                out=project/'source'/f'source_{i:03d}{p.suffix.lower()}'
                shutil.copy2(p,out); it['project_path']=str(out.relative_to(project))
        report={'input':str(inp),'media_count':len(items),'source_candidates':len(candidates),'items':items,
                'decision':'all_candidates_copied' if args.accept_all else 'manual_selection_required'}
        out=project/'source_inventory.json'; out.write_text(json.dumps(report,ensure_ascii=False,indent=2),encoding='utf-8')
        print(json.dumps(report,ensure_ascii=False,indent=2))
        if len(candidates)>1 and not args.accept_all:
            raise SystemExit(3)

if __name__=='__main__': main()
