import sys,json,argparse,hashlib
from pathlib import Path
import torch,torchaudio
p=argparse.ArgumentParser();p.add_argument('--model-dir',required=True);p.add_argument('--repo-dir',required=True);p.add_argument('--only',nargs='+',help='manually select job IDs to generate again');a=p.parse_args();repo=Path(a.repo_dir).resolve();sys.path.insert(0,str(repo));sys.path.append(str(repo/'third_party/Matcha-TTS'))
from cosyvoice.cli.cosyvoice import AutoModel
root=Path(__file__).resolve().parent;cv=AutoModel(model_dir=a.model_dir);data=json.loads((root/'jobs.json').read_text());outputs=[];existing=json.loads((root/'generated.json').read_text()).get('outputs',[]) if (root/'generated.json').exists() else [];reftext=(root/'reference.txt').read_text().strip()
known={job['id'] for job in data['jobs']};selected=set(a.only or known);assert selected<=known,'unknown selected job ID'
for job in data['jobs']:
 if job['id'] not in selected:continue
 if data['mode']=='instruct2':stream=cv.inference_instruct2(job['text'],job['instruct'],str(root/'reference.wav'),stream=False)
 else:stream=cv.inference_zero_shot(job['text'],'You are a helpful assistant.<|endofprompt|>'+reftext,str(root/'reference.wav'),stream=False)
 pieces=[x['tts_speech'] for x in stream];assert pieces,'empty model output';speech=torch.cat(pieces,dim=1);name=job['id']+'.wav';torchaudio.save(str(root/name),speech,cv.sample_rate)
 outputs.append({'id':job['id'],'file':name,'sampleRate':cv.sample_rate,'sha256':hashlib.sha256((root/name).read_bytes()).hexdigest(),'text':job['text'],'inputSha256':hashlib.sha256((root/'reference.wav').read_bytes()+reftext.encode()+job['text'].encode()+job.get('instruct','').encode()).hexdigest(),'status':'generated_pending_review','runtime':'actual CosyVoice output; needs listening'})
 merged={item['id']:item for item in existing};merged.update({item['id']:item for item in outputs});ordered=[merged[job['id']] for job in data['jobs'] if job['id'] in merged]
 (root/'generated.json').write_text(json.dumps({'outputs':ordered,'selection':'manual --only; no automatic skip or resume'},ensure_ascii=False,indent=2))
