#!/usr/bin/env python3
from pathlib import Path
import hashlib,json,subprocess,sys,datetime,os
ROOT=Path('/data/video-pipeline/HaTramAudio/project/023-Buc-Thu-Khong-Gui-O-Ngan-Ban-Cuoi');RUN='run-20260721T183334Z-cc725d7b';WORK=ROOT/'work/artwork';GEN=Path.home()/'.hermes/skills/content-creation/tao-anh/scripts/generate_image.py';CANON='e4abc410d33fdde939459c170dccfc1c7b86bf8341d4f6a0cc1f3770d4c9566b';TITLE='Bức Thư Không Gửi Ở Ngăn Bàn Cuối'
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 phim thanh xuân Trung Quốc hư cấu, digital illustration bán hiện thực điện ảnh cao cấp, Hàng Châu trong mưa, trường trung học cuối cấp, xanh lam mực và hổ phách ấm, tối giản, một focal point, chiều sâu rõ, không collage/card/slide/mockup, không mô phỏng người thật. Tống Vãn Ninh 18 tuổi, nữ sinh châu Á hư cấu, tóc đen ngang vai, đồng phục sơ mi trắng và nơ xanh, ánh mắt tỉnh táo kiên định. Lục Trạch Ngôn 18 tuổi, nam sinh châu Á hư cấu, tóc đen gọn, đồng phục trắng xanh, điềm tĩnh và tôn trọng khoảng cách. Motif công khai: phong thư màu lam chưa mở, ngăn bàn cuối lớp, micrô phát thanh, cửa sổ mưa và tiếng chuông gợi bằng ánh sáng. Không lộ thủ phạm, nội dung thư, metadata máy quét, kết luận hội đồng hoặc cảnh hai người xác nhận tình cảm. Mọi giấy, bảng, sách, màn hình, huy hiệu, đồng hồ và đạo cụ nền phải trơn, không chữ, không số, không pseudo-text, logo hay watermark. Provider phải render hình và typography hoàn chỉnh trong cùng raster; không chừa chỗ hậu kỳ.\n'''
prompts={
'intro_poster':shared+f'''Tạo poster ngang chính xác 16:9, master 1920x1080. Vãn Ninh là focal point cạnh chiếc bàn cuối lớp, cầm phong thư màu lam còn niêm, Trạch Ngôn đứng lớp sau gần cửa phòng phát thanh, mưa ngoài cửa sổ và micrô ở foreground. Cảm giác bí ẩn thanh xuân nhưng không kinh dị. Provider tự quyết typography. RENDER CHÍNH XÁC BA KHỐI CHỮ TIẾNG VIỆT SAU, 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 từng chữ và dấu; dễ đọc, không cắt hoặc che.''',
'right_panel':shared+f'''Tạo key art dọc tỷ lệ chính xác 656:1080, master 1080x1778. Vãn Ninh là focal point cạnh ngăn bàn cuối, phong thư lam trong tay; Trạch Ngôn ở lớp sau cạnh cửa sổ mưa, micrô phát thanh nhỏ ở foreground. Không crop chủ thể quan trọng. Provider tự quyết typography. RENDER CHÍNH XÁC BA KHỐI CHỮ TIẾNG VIỆT SAU, 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 từng chữ và dấu; dễ đọc, không cắt hoặc che.''',
'left_panel':'''Tạo branding panel điện ảnh dọc tỷ lệ chính xác 656:1080, master 1080x1778. Trường học Hàng Châu trong mưa, xanh lam mực và hổ phách ấm, ngăn bàn và phong thư lam trừu tượng không chữ, micrô phát thanh tối giản; không nhân vật, không tên truyện, không dòng độc quyền, không aperture hoặc layout giả. Mọi đạo cụ nền trơn, không chữ/số/logo/watermark. Provider render typography hoàn chỉnh và tự quyết bố cục. RENDER CHÍNH XÁC HAI KHỐI CHỮ TIẾNG VIỆT SAU, KHÔNG THÊM CHỮ KHÁC:\n“Hạ Trâm Audio”\n“Like • Chia sẻ • Đăng ký”\nGiữ nguyên chính tả và dấu; dễ đọc, không cắt hoặc che.'''}
WORK.mkdir(parents=True,exist_ok=True);bundle={'schema_version':1,'project_id':'023','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':'023','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))
