diff --git a/docs/nanobanana-skill.md b/docs/nanobanana-skill.md index f44264f..c6e87cb 100644 --- a/docs/nanobanana-skill.md +++ b/docs/nanobanana-skill.md @@ -45,15 +45,22 @@ metadata: { "code": 200, "data": { - "status": "SUCCESS", - "imageUrls": ["https://cdn.nanobananaapi.ai/result/abc123_0.jpg"], - "prompt": "...", - "taskId": "abc123" + "taskId": "abc123", + "successFlag": 1, + "completeTime": "2026-06-23 04:26:02", + "response": { + "resultImageUrl": "https://tempfile.aiquickdraw.com/workers/images/image_xxx.jpg", + "originImageUrl": null + }, + "operationType": "nano-banana-2_2k", + "createTime": "2026-06-23 04:25:26" } } ``` -**Status-Werte:** `PENDING` → warten, `SUCCESS` → fertig, `FAILED` → Fehler +**Status-Prüfung:** `data.successFlag`: `1` = fertig, `0`/`null` = noch in Arbeit, `2` = fehlgeschlagen. +**Bild-URL:** `data.response.resultImageUrl` — das fertige Bild. +**Original (bei Image-to-Image):** `data.response.originImageUrl`. ### POST /api/v1/nanobanana/generate (Basis-Modell) @@ -129,32 +136,57 @@ def normalize_bsn_image(path: str) -> str: ### 4. Polling bis zum Ergebnis (Python) ```python -import time, requests, json +import time, requests, json, subprocess -API_KEY = subprocess.run(["bsn-secrets","get","bsn/nanobanana-key"], capture_output=True, text=True).stdout.strip() -HEADERS = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"} +API_KEY=subpro..."], capture_output=True, text=True).stdout.strip() +HEADERS = { + "Authorization": f"Bearer {API_KEY}", + "Content-Type": "application/json", + "User-Agent": "Mozilla/5.0" # ⚠️ Pflicht! Cloudflare blockt ohne Browser-UA +} # Task starten -resp = requests.post("https://api.nanobananaapi.ai/api/v1/nanobanana/generate-2", headers=HEADERS, json={ - "prompt": prompt, - "imageUrls": image_urls if image_urls else [], - "aspectRatio": aspect_ratio, - "resolution": resolution, - "outputFormat": output_format, -}).json() +resp = requests.post( + "https://api.nanobananaapi.ai/api/v1/nanobanana/generate-2", + headers=HEADERS, + json={ + "prompt": prompt, + "imageUrls": image_urls if image_urls else [], + "aspectRatio": aspect_ratio, + "resolution": resolution, + "outputFormat": output_format, + }, + timeout=30 +).json() + +if resp.get("code") != 200: + raise Exception(f"API-Fehler: {resp}") task_id = resp["data"]["taskId"] +print(f"Task gestartet: {task_id}") # Pollen -for _ in range(30): # max 5 Minuten +for i in range(30): # max 5 Minuten time.sleep(10) - r = requests.get(f"https://api.nanobananaapi.ai/api/v1/nanobanana/record-info?taskId={task_id}", headers=HEADERS).json() - if r["data"]["status"] == "SUCCESS": - return r["data"]["imageUrls"] - elif r["data"]["status"] == "FAILED": - raise Exception("Generation failed") + r = requests.get( + f"https://api.nanobananaapi.ai/api/v1/nanobanana/record-info?taskId={task_id}", + headers=HEADERS, + timeout=15 + ).json() + + data = r.get("data", {}) + success = data.get("successFlag") + + if success == 1: + url = data["response"]["resultImageUrl"] + print(f"✅ Fertig nach {(i+1)*10}s: {url}") + return url, data["response"].get("originImageUrl") + elif success == 2: + raise Exception(f"Generation fehlgeschlagen: {data.get('errorMessage')}") + + print(f" Poll {i+1}: noch in Arbeit...") -raise TimeoutError("Timeout after 5 minutes") +raise TimeoutError("Timeout nach 5 Minuten") ``` --- @@ -207,11 +239,13 @@ curl -s "https://api.nanobananaapi.ai/api/v1/common/account-credits" \ ## Pitfalls -- **Task ist asynchron** — `POST` gibt sofort `taskId` zurück, Ergebnis erst nach 10-60s via GET +- **⛔ Cloudflare-Block:** API blockt Requests ohne `User-Agent: Mozilla/5.0` → IMMER Browser-UA mitschicken +- **Task ist asynchron** — `POST` gibt sofort `taskId` zurück, Ergebnis nach 30-60s via GET +- **Status-Feld heißt `successFlag`** (nicht `status`): `1` = fertig, `0`/`null` = pending, `2` = failed +- **Bild-URL:** `data.response.resultImageUrl` (nicht `data.imageUrls`) - **NICHT blockierend warten** — Pollen mit 10s Intervallen, max 30 Versuche - **`imageUrls: []` für Text-to-Image** — Leeres Array, nicht weglassen -- **Prompt-Länge max 20K** bei generate-2, weniger bei generate +- **Prompt-Länge max 20K** bei generate-2 - **Aspect-Ratio als String** `"16:9"` — nicht als Float -- **Status-Check:** Immer `r["data"]["status"]` prüfen — nicht `r["code"]` -- **Keine exakten Pixel-Garantien** — API liefert ~1920×1080 bei 2K+16:9. Immer mit `normalize_bsn_image()` nachbearbeiten für exakte 1920×1080 @ 72 DPI -- **DPI wird von API nicht gesetzt** — JPGs aus der API haben oft keine DPI-Metadaten. `PIL.Image.save(dpi=(72,72))` behebt das +- **Keine exakten Pixel-Garantien** — API liefert ~1920×1080 bei 2K+16:9. Immer mit `normalize_bsn_image()` nachbearbeiten +- **DPI wird von API nicht gesetzt** — `PIL.Image.save(dpi=(72,72))` behebt das