#!/usr/bin/env python3 import argparse import base64 import datetime as _dt import json import os import random import re import sys import time import urllib.error import urllib.request def _stamp() -> str: return _dt.datetime.now().strftime("%Y-%m-%d-%H%M%S") def _slug(text: str, max_len: int = 60) -> str: s = text.lower() s = re.sub(r"[^a-z0-9]+", "-", s).strip("-") return (s[:max_len] or "image").strip("-") def _default_out_dir() -> str: projects_tmp = os.path.expanduser("~/Projects/tmp") if os.path.isdir(projects_tmp): return os.path.join(projects_tmp, f"openai-image-gen-{_stamp()}") return os.path.join(os.getcwd(), "tmp", f"openai-image-gen-{_stamp()}") def _api_url() -> str: base = ( os.environ.get("OPENAI_BASE_URL") or os.environ.get("OPENAI_API_BASE") or "https://api.openai.com" ).rstrip("/") if base.endswith("/v1"): return f"{base}/images/generations" return f"{base}/v1/images/generations" def _random_prompts(count: int) -> list[str]: subjects = [ "a lobster piloting a vintage scooter", "a raccoon librarian in a tiny art-deco library", "a glass whale floating above a desert", "a moss-covered robot tending a bonsai garden", "a candlelit map room with impossible staircases", "a retro-futurist diner on the moon at dusk", "a hummingbird made of stained glass", "a porcelain teapot city in the clouds", "a midnight train station built inside a giant clock", "a tiny submarine exploring a glowing kelp forest", "a baroque observatory with brass telescopes and fog", "a koi pond shaped like a circuit board", ] styles = [ "ultra-detailed studio photo", "35mm film still", "risograph poster", "oil painting on linen", "watercolor with ink linework", "isometric diorama", "mid-century editorial illustration", "high-end product shot", ] lighting = [ "softbox lighting", "golden hour", "neon rim light", "overcast diffuse light", "candlelight with deep shadows", "dramatic chiaroscuro", ] palettes = [ "copper + teal + cream", "cobalt + vermilion + bone", "sage + sand + charcoal", "magenta + midnight blue + silver", ] random.shuffle(subjects) prompts: list[str] = [] for i in range(count): subj = subjects[i % len(subjects)] prompts.append( f"{random.choice(styles)} of {subj}. " f"Lighting: {random.choice(lighting)}. " f"Palette: {random.choice(palettes)}. " "Crisp, no text, no watermark." ) return prompts def _post_json(url: str, api_key: str, payload: dict, timeout_s: int) -> dict: body = json.dumps(payload).encode("utf-8") req = urllib.request.Request( url, data=body, headers={ "Authorization": f"Bearer {api_key}", "Content-Type": "application/json", }, method="POST", ) try: with urllib.request.urlopen(req, timeout=timeout_s) as resp: raw = resp.read() except urllib.error.HTTPError as e: raw = e.read() try: data = json.loads(raw.decode("utf-8", errors="replace")) except Exception: raise SystemExit(f"OpenAI HTTP {e.code}: {raw[:300]!r}") raise SystemExit(f"OpenAI HTTP {e.code}: {json.dumps(data, indent=2)[:1200]}") except Exception as e: raise SystemExit(f"request failed: {e}") try: return json.loads(raw) except Exception: raise SystemExit(f"invalid JSON response: {raw[:300]!r}") def _write_index(out_dir: str, items: list[dict]) -> None: html = [ "", "", "", "