import os,sys,json,hashlib,subprocess
from pathlib import Path

def load_env(path):
 for line in Path(path).read_text(errors='ignore').splitlines():
  line=line.strip()
  if line and not line.startswith('#') and '=' in line:
   k,v=line.split('=',1);os.environ.setdefault(k.strip(),v.strip().strip('"\''))
load_env('/opt/OpenMontage/.env');sys.path.insert(0,'/opt/OpenMontage')
from tools.graphics.cx_image import CXImage
P='gacmai_20260718_091744';R=Path('/data/video-pipeline/GacMaiAudio/project')/P;A=R/'artifacts';out=R/'assets/images';out.mkdir(parents=True,exist_ok=True)
contract=A/'visual_campaign_contract_rev1.json';csha=hashlib.sha256(contract.read_bytes()).hexdigest();assert csha=='3d345b87df3fa3d0c67458a6930b2cbf41a82f80ac7de3c4e7b9e5e920a16f9b'
base='Premium cinematic semi-realistic HAND-PAINTED GOUACHE-AND-OIL theatrical poster campaign, unmistakable expressive brush strokes on faces, clothes and props, simplified graphic tonal planes, hand-painted edges, subtle paper tooth, tactile but intentionally non-photographic materials, Vietnamese faces and hands, muted dark teal table-tennis hall with old tungsten amber accents, cream paper details, premium print grain. CLEARLY ILLUSTRATED POSTER ART. NOT photography, NOT film still, NOT photoreal, NOT CGI concept render, NOT plastic 3D, NOT flat vector, NOT anime. No readable text, no numbers, no logo, no watermark, no brand, no courtroom symbols, no accusation, no villain, no luxury sports arena. '
prompts={
'intro_source_rev4.png':('1920x1080',base+'NATIVE LANDSCAPE 16:9 hand-painted gouache/oil key art. EXACTLY one identifiable person: Nguyễn Hoài Lan, Vietnamese woman 42, low-tied black hair, blue-gray rolled-sleeve shirt, charcoal trousers, worn ivory canvas shoes. She waits behind a painted floor line holding a completely BLANK cream ticket. In her other hand she carries a faded olive-brown OPEN-TOP canvas TABLE-TENNIS RACKET BAG. A clearly recognizable BARE WOODEN TABLE-TENNIS PADDLE HANDLE projects 20-25 centimeters above the bag opening: distinct oval wood grip, narrow neck, worn varnish and visible wood grain, physically separate from all straps and trim; no ambiguity that it is a paddle handle. Empty scratched dark-teal table and cloth net lead to an enamel board containing ONLY solid abstract color rectangles and an analog wall clock containing ONLY twelve plain tick marks and two hands. ABSOLUTELY ZERO letters, numerals, pseudo-writing, glyphs, labels, brands or scribbles on any surface; table apron plain solid dark teal. One face only, no other person. Restrained tension, no accusation or triumph. Visible expressive brush strokes, simplified graphic tonal planes, premium theatrical illustrated poster, clearly not photograph/film still/CGI. Safe inset for Lan, ticket, complete bag and exposed handle, board and clock.'),
}
provider=CXImage();results=[]
for name,(size,prompt) in prompts.items():
 path=out/name
 r=provider.execute({'prompt':prompt,'model':'cx/gpt-5.5-image','size':size,'quality':'high','background':'auto','image_detail':'high','output_format':'png','n':1,'output_path':str(path)})
 if not r.success:raise SystemExit(f'{name}: {r.error}')
 probe=subprocess.check_output(['ffprobe','-v','error','-select_streams','v:0','-show_entries','stream=width,height','-of','json',str(path)],text=True)
 stream=json.loads(probe)['streams'][0];w=int(stream['width']);h=int(stream['height'])
 results.append({'file':str(path),'sha256':hashlib.sha256(path.read_bytes()).hexdigest(),'requested_size':size,'measured_width':w,'measured_height':h,'measured_aspect':w/h,'provider':r.data.get('provider'),'model':r.model})
(A/'visual_generation_manifest_rev0.json').write_text(json.dumps({'status':'generated_pending_qa','contract_sha256':csha,'results':results},ensure_ascii=False,indent=2)+'\n')
print(json.dumps({'status':'generated_pending_qa','results':results},ensure_ascii=False))
