#!/usr/bin/env python3
from pathlib import Path
import hashlib,json,subprocess,sys,datetime,os
ROOT=Path('/data/video-pipeline/HaTramAudio/project/024-Ban-Ghi-Am-Bi-Cat-O-Giay-Thu-Ba-Muoi-Bay');RUN='run-20260721T232250Z-bcc7a598';WORK=ROOT/'work/artwork';GEN=Path.home()/'.hermes/skills/content-creation/tao-anh/scripts/generate_image.py';CANON='16f8ecd13c4815b37e8fe29d354297dd7a5b441fa221ecaeef249754a585e704';TITLE='Bản Ghi Âm Bị Cắt Ở Giây Thứ Ba Mươi Bảy'
def sha(p):
 h=hashlib.sha256()
 with p.open('rb') as f:
  for b in iter(lambda:f.read(8<<20),b''):h.update(b)
 return h.hexdigest()
def atomic(p,o):
 p.parent.mkdir(parents=True,exist_ok=True);q=p.with_suffix(p.suffix+'.tmp');q.write_text(json.dumps(o,ensure_ascii=False,indent=2)+'\n');os.replace(q,p)
def now():return datetime.datetime.now(datetime.timezone.utc).isoformat()
assert sha(ROOT/'story/story-canon.txt')==CANON and json.loads((ROOT/'log/story-qa.json').read_text())['verified']
shared=f'''Key art ngôn tình đô thị nghề nghiệp Trung Quốc hư cấu, digital illustration bán hiện thực điện ảnh cao cấp, Thượng Hải trong mưa đêm, phòng phục chế âm thanh với tường kính, máy băng từ và waveform trừu tượng không chữ, xanh than pha hổ phách, tối giản, một focal point, không collage/card/slide/mockup, không mô phỏng người thật. Thẩm Chi Âm 29 tuổi, phụ nữ châu Á hư cấu, tóc đen ngang vai, sơ mi kem và tai nghe studio, ánh mắt chính xác kiên định. Cố Hoài Thanh 32 tuổi, đàn ông châu Á hư cấu, tóc đen gọn, sơ mi xanh xám, điềm tĩnh, đứng lớp sau tôn trọng khoảng cách. Motif công khai: cuộn băng từ xám, waveform bị ngắt ở giây 37 biểu đạt bằng ánh sáng không số/chữ, tai nghe, kính phòng dựng và mưa thành phố. Không lộ thủ phạm, nội dung câu bị cắt, service token, clock drift, kết luận hội nghề hoặc cảnh hẹn hò. Mọi màn hình, giấy, nhãn, đồng hồ, thiết bị và đạo cụ nền phải trơn, không chữ, không số, không pseudo-text, logo hay watermark. Provider render hình và typography hoàn chỉnh cùng raster; cấm chừa chỗ hậu kỳ.\n'''
prompts={'intro_poster':shared+f'''Tạo poster ngang chính xác 16:9, master 1920x1080. Chi Âm là focal point tại bàn phục chế, cầm tai nghe bên cuộn băng; Hoài Thanh đứng sau lớp kính phòng máy. Một đường waveform ánh sáng bị ngắt tạo bí ẩn nhưng không chứa ký tự. Provider tự quyết typography. RENDER CHÍNH XÁC BA KHỐI CHỮ TIẾNG VIỆT, KHÔNG THÊM CHỮ KHÁC:\n“Hạ Trâm Audio”\n“{TITLE}”\n“Truyện được phát độc quyền tại Hạ Trâm Audio, nghiêm cấm sao chép dưới mọi hình thức”\nGiữ nguyên dấu, dễ đọc, không cắt/che.''','right_panel':shared+f'''Tạo key art dọc tỷ lệ chính xác 656:1080, master 1080x1778. Chi Âm focal point với tai nghe và cuộn băng tại bàn phục chế; Hoài Thanh ở lớp sau sau kính mưa, waveform trừu tượng. Không crop chủ thể. Provider tự quyết typography. RENDER CHÍNH XÁC BA KHỐI CHỮ TIẾNG VIỆT, KHÔNG THÊM CHỮ KHÁC:\n“Hạ Trâm Audio”\n“{TITLE}”\n“Truyện được phát độc quyền tại Hạ Trâm Audio, nghiêm cấm sao chép dưới mọi hình thức”\nGiữ nguyên dấu, dễ đọc, không cắt/che.''','left_panel':'''Tạo branding panel điện ảnh dọc tỷ lệ chính xác 656:1080, master 1080x1778. Phòng thu âm Thượng Hải đêm mưa, xanh than và hổ phách, microphone, tai nghe, cuộn băng từ trơn và waveform ánh sáng trừu tượng; không nhân vật, không tên truyện, không dòng độc quyền, không layout giả. Mọi đạo cụ không chữ/số/logo/watermark. Provider render typography hoàn chỉnh. RENDER CHÍNH XÁC HAI KHỐI CHỮ TIẾNG VIỆT, KHÔNG THÊM CHỮ KHÁC:\n“Hạ Trâm Audio”\n“Like • Chia sẻ • Đăng ký”\nGiữ nguyên dấu, dễ đọc, không cắt/che.'''}
WORK.mkdir(parents=True,exist_ok=True);bundle={'schema_version':1,'project_id':'024','run_id':RUN,'status':'locked','verified':True,'source_canon_sha256':CANON,'provider_skill':'tao-anh','provider_model':'cx/gpt-5.5-image','text_postprocessing_allowed':False,'items':{}}
for role,prompt in prompts.items():
 pp=WORK/f'{role}.prompt.txt';pp.write_text(prompt);bundle['items'][role]={'prompt_path':str(pp.relative_to(ROOT)),'prompt_sha256':sha(pp)}
atomic(ROOT/'script/image-prompts.json',bundle)
for role in ['intro_poster','left_panel','right_panel']:
 out=WORK/f'{role}-provider.png';rec=WORK/f'{role}.generation.json';item=bundle['items'][role];attempt={'schema_version':1,'project_id':'024','run_id':RUN,'role':role,'status':'requesting','verified':False,'source_canon_sha256':CANON,'prompt_sha256':item['prompt_sha256'],'output':str(out.relative_to(ROOT)),'provider_skill':'tao-anh','text_postprocessed':False,'created_at':now()};atomic(rec,attempt)
 r=subprocess.run([sys.executable,str(GEN),'--prompt',(ROOT/item['prompt_path']).read_text(),'--output',str(out),'--n','1','--size','auto','--quality','auto','--background','auto','--detail','high','--format','png','--timeout','600'],stdout=subprocess.PIPE,stderr=subprocess.PIPE,text=True,timeout=720)
 if r.returncode or not out.exists():attempt.update({'status':'failed','error':(r.stderr or r.stdout)[-1000:]});atomic(rec,attempt);raise RuntimeError(role)
 attempt.update({'status':'generated','technical_verified':True,'artifact_sha256':sha(out),'artifact_bytes':out.stat().st_size,'completed_at':now()});atomic(rec,attempt)
print(json.dumps({'status':'generated_awaiting_visual_qa','roles':list(prompts)},ensure_ascii=False))
