"no id"]); exit; } $id = preg_replace('/[^a-z0-9]/i', '', $id); $frame = $dir . "f_{$id}.dat"; $meta = $dir . "m_{$id}.json"; // ── CLIENTE sube frame ── if ($_SERVER['REQUEST_METHOD'] === 'POST') { $body = file_get_contents("php://input"); if (!$body) { echo json_encode(["error" => "empty"]); exit; } $ts = microtime(true); file_put_contents($frame, $body, LOCK_EX); file_put_contents($meta, json_encode(["ts" => $ts, "size" => strlen($body)]), LOCK_EX); echo json_encode(["status" => "ok", "ts" => $ts]); exit; } // ── PANEL descarga frame (long-poll: espera hasta 1s si no hay frame nuevo) ── $since = floatval($_GET['since'] ?? 0); $waited = 0; while ($waited < 1000) { if (file_exists($meta)) { $info = json_decode(file_get_contents($meta), true); $ts = floatval($info['ts'] ?? 0); if ($ts > $since) { $b64 = base64_encode(file_get_contents($frame)); echo json_encode(["ts" => $ts, "b64" => $b64]); exit; } } usleep(30000); // 30ms $waited += 30; } // Timeout — no hay frame nuevo echo json_encode(["ts" => $since, "b64" => null]);