docs: NanoBanana Skill — echte API-Response-Felder (successFlag, resultImageUrl) + Cloudflare-Workaround
Quality Gate / 🛡️ Lint + Type Check + Tests (push) Failing after 13m35s
Quality Gate / 🛡️ Lint + Type Check + Tests (push) Failing after 13m35s
This commit is contained in:
+56
-22
@@ -45,15 +45,22 @@ metadata:
|
|||||||
{
|
{
|
||||||
"code": 200,
|
"code": 200,
|
||||||
"data": {
|
"data": {
|
||||||
"status": "SUCCESS",
|
"taskId": "abc123",
|
||||||
"imageUrls": ["https://cdn.nanobananaapi.ai/result/abc123_0.jpg"],
|
"successFlag": 1,
|
||||||
"prompt": "...",
|
"completeTime": "2026-06-23 04:26:02",
|
||||||
"taskId": "abc123"
|
"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)
|
### POST /api/v1/nanobanana/generate (Basis-Modell)
|
||||||
|
|
||||||
@@ -129,32 +136,57 @@ def normalize_bsn_image(path: str) -> str:
|
|||||||
### 4. Polling bis zum Ergebnis (Python)
|
### 4. Polling bis zum Ergebnis (Python)
|
||||||
|
|
||||||
```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()
|
API_KEY=subpro..."], capture_output=True, text=True).stdout.strip()
|
||||||
HEADERS = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}
|
HEADERS = {
|
||||||
|
"Authorization": f"Bearer {API_KEY}",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"User-Agent": "Mozilla/5.0" # ⚠️ Pflicht! Cloudflare blockt ohne Browser-UA
|
||||||
|
}
|
||||||
|
|
||||||
# Task starten
|
# Task starten
|
||||||
resp = requests.post("https://api.nanobananaapi.ai/api/v1/nanobanana/generate-2", headers=HEADERS, json={
|
resp = requests.post(
|
||||||
|
"https://api.nanobananaapi.ai/api/v1/nanobanana/generate-2",
|
||||||
|
headers=HEADERS,
|
||||||
|
json={
|
||||||
"prompt": prompt,
|
"prompt": prompt,
|
||||||
"imageUrls": image_urls if image_urls else [],
|
"imageUrls": image_urls if image_urls else [],
|
||||||
"aspectRatio": aspect_ratio,
|
"aspectRatio": aspect_ratio,
|
||||||
"resolution": resolution,
|
"resolution": resolution,
|
||||||
"outputFormat": output_format,
|
"outputFormat": output_format,
|
||||||
}).json()
|
},
|
||||||
|
timeout=30
|
||||||
|
).json()
|
||||||
|
|
||||||
|
if resp.get("code") != 200:
|
||||||
|
raise Exception(f"API-Fehler: {resp}")
|
||||||
|
|
||||||
task_id = resp["data"]["taskId"]
|
task_id = resp["data"]["taskId"]
|
||||||
|
print(f"Task gestartet: {task_id}")
|
||||||
|
|
||||||
# Pollen
|
# Pollen
|
||||||
for _ in range(30): # max 5 Minuten
|
for i in range(30): # max 5 Minuten
|
||||||
time.sleep(10)
|
time.sleep(10)
|
||||||
r = requests.get(f"https://api.nanobananaapi.ai/api/v1/nanobanana/record-info?taskId={task_id}", headers=HEADERS).json()
|
r = requests.get(
|
||||||
if r["data"]["status"] == "SUCCESS":
|
f"https://api.nanobananaapi.ai/api/v1/nanobanana/record-info?taskId={task_id}",
|
||||||
return r["data"]["imageUrls"]
|
headers=HEADERS,
|
||||||
elif r["data"]["status"] == "FAILED":
|
timeout=15
|
||||||
raise Exception("Generation failed")
|
).json()
|
||||||
|
|
||||||
raise TimeoutError("Timeout after 5 minutes")
|
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 nach 5 Minuten")
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -207,11 +239,13 @@ curl -s "https://api.nanobananaapi.ai/api/v1/common/account-credits" \
|
|||||||
|
|
||||||
## Pitfalls
|
## 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
|
- **NICHT blockierend warten** — Pollen mit 10s Intervallen, max 30 Versuche
|
||||||
- **`imageUrls: []` für Text-to-Image** — Leeres Array, nicht weglassen
|
- **`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
|
- **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
|
||||||
- **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** — `PIL.Image.save(dpi=(72,72))` behebt das
|
||||||
- **DPI wird von API nicht gesetzt** — JPGs aus der API haben oft keine DPI-Metadaten. `PIL.Image.save(dpi=(72,72))` behebt das
|
|
||||||
|
|||||||
Reference in New Issue
Block a user