false, 'error' => 'POST only']); exit; } $payload = json_decode(file_get_contents('php://input'), true); if (!$payload) { http_response_code(400); echo json_encode(['ok' => false, 'error' => 'Invalid JSON']); exit; } $event = $payload['eventType'] ?? ''; if ($event === 'Test') { echo json_encode(['ok' => true, 'message' => 'Webhook connected — configure On Upgrade to use this URL']); exit; } // Sonarr/Radarr/Lidarr fire eventType "Download" with isUpgrade=true for upgrades. // A plain Download (no upgrade) is not our concern — the original quality is fine. $is_upgrade = $payload['isUpgrade'] ?? false; if ($event !== 'Download' || !$is_upgrade) { echo json_encode(['ok' => true, 'skipped' => $event]); exit; } // Detect arr type and item folder from payload structure if (isset($payload['series']['path'])) { $arr_type = 'sonarr'; $path = $payload['series']['path']; } elseif (isset($payload['movie']['folderPath'])) { $arr_type = 'radarr'; $path = $payload['movie']['folderPath']; } elseif (isset($payload['artist']['path'])) { $arr_type = 'lidarr'; $path = $payload['artist']['path']; } else { http_response_code(400); echo json_encode(['ok' => false, 'error' => 'Unrecognised payload structure']); exit; } if (!$path) { http_response_code(400); echo json_encode(['ok' => false, 'error' => 'Empty path in payload']); exit; } // Basic path sanity — must be absolute, no traversal if (!str_starts_with($path, '/') || str_contains($path, '..') || preg_match('/[\x00\n\r]/', $path)) { http_response_code(400); echo json_encode(['ok' => false, 'error' => 'Unsafe path']); exit; } $handler = SCRIPTS_DIR . '/Media/upgrade_webhook_handler.sh'; if (!file_exists($handler)) { http_response_code(500); echo json_encode(['ok' => false, 'error' => 'upgrade_webhook_handler.sh not found']); exit; } exec('bash ' . escapeshellarg($handler) . ' ' . escapeshellarg($arr_type) . ' ' . escapeshellarg($path) . ' >> /var/log/varaverk/upgrade_webhook.log 2>&1 &'); echo json_encode(['ok' => true, 'arr' => $arr_type, 'path' => $path]);