[ 'label' => 'Eurocopter EC135', 'scene' => "Subject: Eurocopter EC135 light twin-engine medical rescue helicopter shown as a clean side-view profile filling the frame edge-to-edge (NOT inside any circle or vignette). CRITICAL detail: the tail must end in a FENESTRON — a thick circular DISC-SHAPED HOUSING built into the tail fin, completely hiding the rotor blades behind a smooth flat disk surface. Absolutely NO open spinning tail rotor blades visible — the tail looks like a flat round wheel. Compact body, retractable skid landing gear, large sliding side door with red cross marking. Livery: bright Notarzt-Heli yellow (#e8c547) body with bold red (#c85c4a) horizontal accent stripe along the lower fuselage. Main rotor blades visible above the cabin.", ], 'heli-h145' => [ 'label' => 'Airbus H145', 'scene' => "Subject: Airbus H145 medium twin-engine medical rescue helicopter shown as a clean side-view profile filling the frame edge-to-edge (NOT inside any circle or vignette — straight rectangular composition). CRITICAL detail: the tail ends in a FENESTRON — a thick circular DISC-SHAPED HOUSING built into the tail fin, completely hiding the rotor blades behind a smooth flat disk. Absolutely NO open spinning tail rotor visible. Larger fuselage than the EC135, prominent rear clamshell doors at the back, skid landing gear, large side sliding door with red cross marking. Five-blade main rotor visible above the cabin. Livery: bright Notarzt-Heli yellow (#e8c547) body with bold red (#c85c4a) horizontal accent stripe along the lower fuselage.", ], 'heli-aw169' => [ 'label' => 'Leonardo AW169', 'scene' => "Subject: Leonardo AW169 medium twin-engine helicopter in side-view profile, in Austrian Police 'Libelle' configuration. Distinctive features: sleek modern fuselage, OPEN classical tail rotor (NOT a fenestron — clearly visible separate small rotor on tail boom), retractable wheeled landing gear (not skids), large windows. Five-blade main rotor visible above the cabin. Livery: dark navy blue (#1f3a5a) main body with bright yellow-mustard (#e8c547) high-visibility stripes along the lower side and tail.", ], 'heli-ab212' => [ 'label' => 'Agusta-Bell AB212', 'scene' => "Subject: Agusta-Bell AB212 utility twin-engine military helicopter in side-view profile, classic 1970s design. Distinctive features: long narrow tail boom, OPEN two-blade tail rotor on the boom (NOT a fenestron), classic two-blade main rotor with semi-rigid teetering hub, skid landing gear, large sliding side door, slightly bulky engine cowling on top. Livery: matte olive-green military camouflage (#4a5a3a) with small Austrian Bundesheer red-white-red roundel insignia visible.", ], ]; $outDir = __DIR__ . '/../sims/heli/assets/helis-new'; if (!is_dir($outDir)) mkdir($outDir, 0777, true); $ids = isset($_GET['ids']) ? array_filter(array_map('trim', explode(',', $_GET['ids']))) : array_keys($HELIS); $force = !empty($_GET['force']); echo "Helikopter-Typ-Generator (DALL-E 3, 1024x1024)\n"; echo "Modus: " . ($force ? 'FORCE' : 'skip if exists') . "\n"; echo str_repeat('-', 64) . "\n"; @ob_flush(); @flush(); $ok = 0; $skip = 0; $fail = 0; foreach ($ids as $id) { if (!isset($HELIS[$id])) { echo "x $id: unbekannt\n"; $fail++; continue; } $out = $outDir . '/' . $id . '.png'; if (file_exists($out) && !$force) { echo "skip $id.png (vorhanden)\n"; $skip++; continue; } $prompt = $STYLE_BASE . ' ' . $HELIS[$id]['scene']; $body = json_encode([ 'model' => 'dall-e-3', 'prompt' => $prompt, 'n' => 1, 'size' => '1024x1024', 'quality' => 'standard', ]); echo "-> $id (" . $HELIS[$id]['label'] . ") generiere ... "; @ob_flush(); @flush(); $ch = curl_init('https://api.openai.com/v1/images/generations'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer ' . $OPENAI_KEY, 'Content-Type: application/json', ], CURLOPT_POSTFIELDS => $body, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 180, ]); $resp = curl_exec($ch); $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($code !== 200) { echo "FAIL ($code) — " . substr($resp, 0, 200) . "\n"; $fail++; continue; } $data = json_decode($resp, true); $url = $data['data'][0]['url'] ?? null; if (!$url) { echo "FAIL keine URL\n"; $fail++; continue; } $img = @file_get_contents($url); if (!$img || strlen($img) < 1000) { echo "FAIL Download\n"; $fail++; continue; } file_put_contents($out, $img); $ok++; echo "ok (" . round(strlen($img) / 1024) . " kB)\n"; @ob_flush(); @flush(); sleep(2); } echo str_repeat('-', 64) . "\n"; echo "Zusammenfassung: ok=$ok, skip=$skip, fail=$fail\n"; echo "Ordner: $outDir\n";