#!/usr/bin/env python3 """Generiert die 5 Tutorial-Step-Bilder für das Weltküche-Onboarding. Flat-Scandinavian-Stil, ersetzen die alten Emojis im Tutorial-Overlay. Output: App/sims/weltkueche/assets/img/onboarding/.webp (320x320)""" import json, os, subprocess, sys, tempfile, base64 from pathlib import Path KEY = None env_path = 'C:/xampp/htdocs/geograsim/App/.env.local' with open(env_path, 'r', encoding='utf-8') as f: for line in f: if line.startswith('OPENAI_API_KEY='): KEY = line.split('=', 1)[1].strip().strip('"').strip("'") break if not KEY: sys.exit('OPENAI_API_KEY nicht in .env.local') OUT_DIR = Path('C:/xampp/htdocs/geograsim/App/sims/weltkueche/assets/img/onboarding') ORIG_DIR = OUT_DIR / '_original' OUT_DIR.mkdir(parents=True, exist_ok=True) ORIG_DIR.mkdir(parents=True, exist_ok=True) PROMPT_BASE = ("CRITICAL STYLE: flat vector illustration, children's-book / Scandinavian poster art, " "NO photography, NO 3D rendering, NO realistic shading, NO photographic textures, NO gradients. " "Wide-shape silhouettes with flat solid color fills, clean cut edges, simple geometric forms. " "Strict palette: dark forest green #1f4b37, sage green #4a7c4e, yellow-mustard #e8c547, " "warm beige #e8e4d8, white highlights, optional small red-orange accent #c97b5a. " "COMPOSITION: a single centered iconographic motif (one strong shape), filling about 75% of the square frame, " "with a flat beige #e8e4d8 background. The motif is plakatativ-simple — recognizable from a thumbnail. " "FORBIDDEN: text, letters, numbers, watermarks, logos, borders, frames, photo-realistic lighting, " "lens-flare, people faces, hands, brand names, shadows.") STEPS = { 'woher': ( "SCENE: a centered round Earth globe in dark forest green and sage green, " "with simple geographic shapes (one continent shape with white tiny dots for cities). " "Around the globe, 5 small flat food icons evenly spaced like a clockface: " "a yellow-mustard banana shape (top), a red-orange tomato (right), a forest-green coffee bean (bottom-right), " "a sage-green pepper grain cluster (bottom-left), a beige bread slice (left). " "Soft dotted curved lines connect each food to the globe (like trade routes)." ), 'forschung': ( "SCENE: a centered flat-style world map (silhouette of continents in sage green on beige ocean) " "with a large dark forest green magnifying glass overlapping the right side. " "The map has 3-4 red-orange pin-drop markers at different locations. " "The magnifying glass handle points to the bottom-right." ), 'punkte': ( "SCENE: a centered tall trophy cup in yellow-mustard with dark forest green handles and base. " "Inside the cup, a 5-pointed sage-green star. " "Three small flat circular medals float around the cup — one with a star, one with a leaf, one with a globe symbol — " "all in white outlines on dark forest green circles." ), 'klima': ( "SCENE: a centered round Earth globe in sage green and dark forest green, with a young green sapling/seedling " "growing out of the top of the globe (two small leaves in sage green). " "Below the globe, a flat container ship silhouette in dark forest green on a beige horizontal sea line. " "A small white CO₂ cloud shape on the upper-right (made from 2-3 overlapping circles)." ), 'glossar': ( "SCENE: a centered open hardcover book viewed from above. The cover and edges are dark forest green, " "the pages are warm beige. On the left page: 3-4 horizontal sage-green text lines (abstract, no real letters). " "On the right page: a small dark forest green globe icon centered. " "A yellow-mustard bookmark ribbon hangs from the top center." ), } def generate(key, scene): out_png = ORIG_DIR / f'{key}.png' out_webp = OUT_DIR / f'{key}.webp' if out_webp.exists(): print(f' SKIP (existiert): {out_webp.name}') return True prompt = PROMPT_BASE + ' ' + scene body = json.dumps({'model': 'gpt-image-2', 'prompt': prompt, 'n': 1, 'size': '1024x1024'}, ensure_ascii=False) with tempfile.NamedTemporaryFile(mode='w', encoding='utf-8', suffix='.json', delete=False) as f: f.write(body); bpath = f.name r = subprocess.run(['curl', '-sS', '-X', 'POST', 'https://api.openai.com/v1/images/generations', '-H', 'Authorization: Bearer ' + KEY, '-H', 'Content-Type: application/json', '--data-binary', '@' + bpath], capture_output=True, text=True, timeout=300) os.unlink(bpath) try: data = json.loads(r.stdout) item = data['data'][0] if 'b64_json' in item: with open(out_png, 'wb') as f: f.write(base64.b64decode(item['b64_json'])) elif 'url' in item: subprocess.run(['curl', '-sS', '-o', str(out_png), item['url']], capture_output=True, text=True, timeout=60) else: return False except Exception as e: print(f' FAIL: {r.stdout[:200] if r.stdout else e}') return False if not out_png.exists() or out_png.stat().st_size < 10000: return False try: from PIL import Image img = Image.open(out_png) img = img.resize((320, 320), Image.LANCZOS) img.save(out_webp, 'webp', quality=85, method=6) except Exception as e: print(f' WebP-Fail: {e}') return False return True ids = sys.argv[1:] if len(sys.argv) > 1 else list(STEPS.keys()) for k in ids: if k not in STEPS: print(f'-> {k}: UNBEKANNT') continue print(f'-> {k} ...', flush=True) ok = generate(k, STEPS[k]) print(f' {"OK" if ok else "FAIL"}: {k}.webp')