'error', 'message' => $message]); exit(); } /** * Get nested value from array using dot notation * @param array $array The array to search * @param string $path Dot-separated path like 'output.0.url' * @return mixed|null The value or null if not found */ function getNestedValue($array, $path) { $keys = explode('.', $path); $current = $array; foreach ($keys as $key) { if (is_array($current) && isset($current[$key])) { $current = $current[$key]; } else { return null; } } return $current; } // Ensure the request method is GET if ($_SERVER['REQUEST_METHOD'] !== 'GET') { sendErrorResponse('Method Not Allowed', 405); } $jobId = $_GET['job_id'] ?? null; if (empty($jobId)) { sendErrorResponse('Job ID is required', 400); } try { // Check if API key is available if (!defined('RUNWAY_API_KEY') || empty(RUNWAY_API_KEY)) { throw new Exception('Runway API key not configured. Please check your .env file.'); } // Validate job ID format (should be a UUID) if (!preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i', $jobId)) { throw new Exception('Invalid job ID format'); } error_log("Checking status for job ID: " . $jobId); // Check job status with Runway API $runwayStatusEndpoint = "https://api.dev.runwayml.com/v1/tasks/{$jobId}"; $ch = curl_init($runwayStatusEndpoint); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPGET, true); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Authorization: Bearer ' . RUNWAY_API_KEY, 'X-Runway-Version: 2024-11-06' ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $curlError = curl_error($ch); curl_close($ch); if ($response === false) { throw new Exception("cURL error checking job status: " . $curlError); } error_log("Status check response HTTP code: " . $httpCode); error_log("Status check response body: " . $response); $result = json_decode($response, true); if ($httpCode >= 400) { $errorMessage = 'Unknown error checking job status'; if (isset($result['error']['message'])) { $errorMessage = $result['error']['message']; } elseif (isset($result['message'])) { $errorMessage = $result['message']; } throw new Exception("Error checking job status ($httpCode): " . $errorMessage . ". Response: " . $response); } // Parse the task status response $status = $result['status'] ?? 'unknown'; $videoUrl = null; // Check if task is completed and has output if ($status === 'SUCCEEDED' || $status === 'completed') { // Look for video URL in various possible locations based on Runway API docs // Handle the actual Runway response format from logs if (isset($result['output']) && is_array($result['output']) && !empty($result['output'])) { // Runway returns output as array of URLs, take the first one $videoUrl = $result['output'][0]; } // Fallback to other possible locations if not found if (!$videoUrl) { $possiblePaths = [ 'output.url', 'artifacts.0.url', 'artifacts.url', 'video.url', 'video_url', 'url', 'result.url', 'result.video_url' ]; foreach ($possiblePaths as $path) { $videoUrl = getNestedValue($result, $path); if ($videoUrl) { break; } } } // Log what we found for debugging error_log("Task completed with status: $status"); error_log("Video URL found: " . ($videoUrl ?: 'NONE')); error_log("Full response keys: " . implode(', ', array_keys($result))); } // Return status to frontend echo json_encode([ 'job_id' => $jobId, 'status' => $status, 'video_url' => $videoUrl, 'progress' => $result['progress'] ?? null, 'message' => $result['message'] ?? null, 'raw_response' => $result // For debugging ]); } catch (Exception $e) { error_log("Status check error: " . $e->getMessage()); sendErrorResponse("Failed to check status: " . $e->getMessage(), 500); }