#!/usr/bin/env python3 # /// script # requires-python = ">=3.10" # dependencies = [ # "google-genai>=1.0.0", # "pillow>=10.0.0", # ] # /// """ Batch-generate infographic slides using Nano Banana Pro (Gemini 3 Pro Image). Usage: uv run batch_generate.py --prompts prompts.json --output /tmp/slides/ [--resolution 1K] uv run batch_generate.py --prompt "single prompt" --output /tmp/slides/ --slide-num 1 prompts.json format: [ {"slide": 1, "prompt": "Dark infographic poster..."}, {"slide": 2, "prompt": "Dark infographic poster..."} ] """ import argparse import json import os import sys import time from io import BytesIO from pathlib import Path RESOLUTIONS = { "1K": (1024, 1280), # 4:5 vertical "2K": (2048, 2560), "4K": (4096, 5120), } def get_api_key(provided_key: str | None) -> str | None: if provided_key: return provided_key return os.environ.get("GEMINI_API_KEY") def generate_single(client, prompt: str, output_path: Path, resolution: str) -> bool: """Generate a single slide image. Returns True on success.""" from google.genai import types as genai_types width, height = RESOLUTIONS.get(resolution, RESOLUTIONS["1K"]) config = genai_types.GenerateContentConfig( response_modalities=["image", "text"], generate_images=genai_types.ImageGenerationConfig( number_of_images=1, aspect_ratio="3:4", output_image_format="png", ), ) try: response = client.models.generate_content( model="gemini-2.0-flash-preview-image-generation", contents=prompt, config=config, ) from PIL import Image as PILImage for part in response.candidates[0].content.parts: if part.inline_data and part.inline_data.mime_type.startswith("image/"): image_data = part.inline_data.data if isinstance(image_data, str): import base64 image_data = base64.b64decode(image_data) image = PILImage.open(BytesIO(image_data)) if image.mode == 'RGBA': rgb = PILImage.new('RGB', image.size, (255, 255, 255)) rgb.paste(image, mask=image.split()[3]) rgb.save(str(output_path), 'PNG') elif image.mode == 'RGB': image.save(str(output_path), 'PNG') else: image.convert('RGB').save(str(output_path), 'PNG') return True print(f" ✗ No image in response", file=sys.stderr) return False except Exception as e: print(f" ✗ Error: {e}", file=sys.stderr) return False def main(): parser = argparse.ArgumentParser(description="Batch-generate infographic slides") parser.add_argument("--prompts", "-p", help="Path to prompts.json file") parser.add_argument("--prompt", help="Single prompt (use with --slide-num)") parser.add_argument("--slide-num", type=int, default=1, help="Slide number for single prompt") parser.add_argument("--output", "-o", required=True, help="Output directory") parser.add_argument("--resolution", "-r", default="1K", choices=["1K", "2K", "4K"]) parser.add_argument("--api-key", "-k", help="Gemini API key") parser.add_argument("--delay", type=float, default=1.0, help="Delay between requests (seconds)") args = parser.parse_args() api_key = get_api_key(args.api_key) if not api_key: print("Error: No API key. Set GEMINI_API_KEY or use --api-key.", file=sys.stderr) sys.exit(1) from google import genai client = genai.Client(api_key=api_key) output_dir = Path(args.output) output_dir.mkdir(parents=True, exist_ok=True) # Build slide list slides = [] if args.prompts: with open(args.prompts) as f: slides = json.load(f) elif args.prompt: slides = [{"slide": args.slide_num, "prompt": args.prompt}] else: print("Error: Provide --prompts or --prompt.", file=sys.stderr) sys.exit(1) total = len(slides) success = 0 print(f"Generating {total} slides at {args.resolution} resolution...\n") for i, entry in enumerate(slides): num = entry.get("slide", i + 1) prompt = entry["prompt"] filename = f"slide-{num:02d}.png" output_path = output_dir / filename print(f"[{i+1}/{total}] Generating slide {num}...") start = time.time() if generate_single(client, prompt, output_path, args.resolution): elapsed = time.time() - start print(f" ✓ Saved: {output_path} ({elapsed:.1f}s)") print(f"MEDIA: {output_path.resolve()}") success += 1 else: print(f" ✗ Failed: slide {num}") if i < total - 1: time.sleep(args.delay) print(f"\nDone: {success}/{total} slides generated in {output_dir}") if __name__ == "__main__": main()