from pathlib import Path
import urllib.request,json,uuid,subprocess,hashlib,datetime,os
root=Path(__file__).resolve().parents[1]; base='http://192.168.1.104:8021'; output=root/'output/intro/intro.mp4'
def sha(p):
 h=hashlib.sha256()
 with p.open('rb') as f:
  for b in iter(lambda:f.read(8*1024*1024),b''):h.update(b)
 return h.hexdigest()
def atomic(p,d):
 q=Path(str(p)+'.tmp');q.write_text(json.dumps(d,ensure_ascii=False,indent=2)+'\n');os.replace(q,p)
poster=root/'image/intro-poster-1920x1080.png';audio=root/'audio/intro-full.wav'; ar=json.loads((root/'log/intro-audio.json').read_text()); assert sha(audio)==ar['intro_full_sha256']; assert not output.exists()
health=json.load(urllib.request.urlopen(base+'/health',timeout=15)); assert health['status']=='ok' and health['nvenc_available'] is True and health['video_root']=='/data/video-pipeline'
payload={'job_id':'gac-mai-007-intro-'+uuid.uuid4().hex[:12],'scenes':[{'image_path':str(poster),'duration':ar['duration_seconds']}],'audio_path':str(audio),'subtitle_path':None,'output_path':str(output),'width':1920,'height':1080,'fps':30,'crf':20,'preset':'p4','burn_subtitles':False,'ken_burns':False,'motion_preset':'none','transition':'cut','transition_duration':0.0,'fade_in':0.0,'fade_out':0.0,'slice_audio':False,'slice_subtitles':False}
intent={'status':'creating','base_url':base,'path':'/stickman-render-gpu','job_id':payload['job_id'],'request_sha256':hashlib.sha256(json.dumps(payload,sort_keys=True,separators=(',',':')).encode()).hexdigest(),'output_path':str(output),'created_at':datetime.datetime.now(datetime.timezone.utc).isoformat()};atomic(root/'log/intro-intent.json',intent)
req=urllib.request.Request(base+'/stickman-render-gpu',data=json.dumps(payload).encode(),headers={'Content-Type':'application/json'},method='POST')
with urllib.request.urlopen(req,timeout=600) as x: response=json.load(x)
probe=json.loads(subprocess.check_output(['ffprobe','-v','error','-show_entries','format=duration,size','-show_entries','stream=codec_type,codec_name,width,height,sample_rate,channels','-of','json',str(output)])); vids=[s for s in probe['streams'] if s['codec_type']=='video'];auds=[s for s in probe['streams'] if s['codec_type']=='audio']; dur=float(probe['format']['duration'])
checks={'terminal':response.get('status') in (None,'completed','success','ok'),'one_h264':len(vids)==1 and vids[0]['codec_name']=='h264','one_audio':len(auds)==1,'resolution':(vids[0]['width'],vids[0]['height'])==(1920,1080),'duration':abs(dur-ar['duration_seconds'])<=1/30+.01}; assert all(checks.values()),checks
rec={'status':'completed','verified':True,'base_url':base,'create_endpoint':'/stickman-render-gpu','job_id':payload['job_id'],'poster_path':str(poster),'poster_sha256':sha(poster),'intro_audio_path':str(audio),'intro_audio_sha256':sha(audio),'output_path':str(output),'artifact_sha256':sha(output),'artifact_bytes':output.stat().st_size,'duration_seconds':dur,'request':payload,'terminal_response':response,'probe':probe,'checks':checks,'created_at':datetime.datetime.now(datetime.timezone.utc).isoformat()};atomic(root/'log/intro.json',rec);print(json.dumps(rec,ensure_ascii=False))
