true, CURLOPT_TIMEOUT=>5, CURLOPT_NOBODY=>true, CURLOPT_FOLLOWLOCATION=>true, CURLOPT_SSL_VERIFYPEER=>false]); curl_exec($ch_test); $http_code = curl_getinfo($ch_test, CURLINFO_HTTP_CODE); $total_time = round(curl_getinfo($ch_test, CURLINFO_TOTAL_TIME) * 1000); curl_close($ch_test); // If 200 OK and it's an API endpoint: render the JSON response as code if ($http_code >= 200 && $http_code < 400 && $is_api) { // Let Playwright handle it (will render the page) } else if ($http_code >= 400 || $http_code === 0) { // Generate SVG status card → PNG via Playwright minimal render $short_url = preg_replace('#^https?://[^/]+#', '', $url); $status_color = ($http_code >= 500) ? '#ef4444' : (($http_code >= 400) ? '#f59e0b' : '#6b7280'); $server_label = $is_s95 ? 'S95 Arsenal' : 'S204'; $svg = '' . '' . '' . '' . $http_code . '' . '' . htmlspecialchars($short_url) . '' . '' . $server_label . ' | ' . $total_time . 'ms' . 'Screenshot unavailable — endpoint returned ' . $http_code . '' . ''; // OPUS Phase5 — Serve SVG DIRECTEMENT (zéro Playwright, zéro latence) // Cache le SVG aussi pour éviter de régénérer @file_put_contents("$cache_dir/$key.svg", $svg); header("Content-Type: image/svg+xml"); header("X-Cache: SVG-DIRECT"); header("X-Status: " . $http_code); echo $svg; exit; } } // Generate via Playwright Python $py = " import sys, asyncio from playwright.async_api import async_playwright async def cap(): async with async_playwright() as p: browser = await p.chromium.launch(headless=True, args=[\"--no-sandbox\",\"--disable-dev-shm-usage\"]) ctx = await browser.new_context(viewport={\"width\":1024,\"height\":640}, ignore_https_errors=True) page = await ctx.new_page() try: await page.goto(\"$url\", timeout=8000, wait_until=\"domcontentloaded\") await page.wait_for_timeout(800) await page.screenshot(path=\"$cache\", clip={\"x\":0,\"y\":0,\"width\":1024,\"height\":640}) except Exception as e: # Fallback: render error message as image await page.set_content(f\"SCREENSHOT FAIL

{type(e).__name__}
{str(e)[:200]}

URL: $url\") await page.screenshot(path=\"$cache\") await browser.close() asyncio.run(cap()) "; $tmp = tempnam("/tmp", "ss_"); file_put_contents($tmp, $py); $out = shell_exec("timeout 12 python3 $tmp 2>&1"); @unlink($tmp); if (file_exists($cache) && filesize($cache) > 100) { header("Content-Type: image/png"); header("X-Cache: MISS"); header("X-Generator: playwright"); readfile($cache); } else { // OPUS Phase5 — Fallback SVG générique au lieu de 503 brut $short = preg_replace("#^https?://[^/]+#", "", $url); $fallback_svg = '' . '' . '' . '' . '' . htmlspecialchars($short) . '' . 'Screenshot en cours de génération' . ''; header("Content-Type: image/svg+xml"); header("X-Cache: PLACEHOLDER"); echo $fallback_svg; }