ensureCliAccess(); $this->loadEnvironment(); } private function loadEnvironment(): void { $this->postgrestUrl = getenv("POSTGREST_URL"); $this->postgrestApiKey = getenv("POSTGREST_API_KEY"); $this->artistImportToken = getenv("ARTIST_IMPORT_TOKEN"); $this->navidromeApiUrl = getenv("NAVIDROME_API_URL"); $this->navidromeAuthToken = getenv("NAVIDROME_API_TOKEN"); } public function handleRequest(): void { $input = json_decode(file_get_contents("php://input"), true); if (!$input) $this->sendJsonResponse("error", "Invalid or missing JSON body", 400); $providedToken = $input["token"] ?? null; $artistId = $input["artistId"] ?? null; if (!$providedToken || $providedToken !== $this->artistImportToken) { $this->sendJsonResponse("error", "Unauthorized access", 401); } if (!$artistId) $this->sendJsonResponse("error", "Artist ID is required", 400); try { $artistData = $this->fetchNavidromeArtist($artistId); $artistExists = $this->processArtist($artistData); if ($artistExists) $this->processAlbums($artistId, $artistData->name); $this->sendJsonResponse("message", "Artist and albums synced successfully", 200); } catch (Exception $e) { $this->sendJsonResponse("error", "Error: " . $e->getMessage(), 500); } } private function sendJsonResponse(string $key, string $message, int $statusCode): void { http_response_code($statusCode); header("Content-Type: application/json"); echo json_encode([$key => $message]); exit(); } private function fetchNavidromeArtist(string $artistId) { $client = new Client(); $response = $client->get("{$this->navidromeApiUrl}/api/artist/{$artistId}", [ "headers" => [ "x-nd-authorization" => "Bearer {$this->navidromeAuthToken}", "Accept" => "application/json" ] ]); return json_decode($response->getBody(), false); } private function fetchNavidromeAlbums(string $artistId): array { $client = new Client(); $response = $client->get("{$this->navidromeApiUrl}/api/album", [ "query" => [ "_end" => 0, "_order" => "ASC", "_sort" => "max_year", "_start" => 0, "artist_id" => $artistId ], "headers" => [ "x-nd-authorization" => "Bearer {$this->navidromeAuthToken}", "Accept" => "application/json" ] ]); return json_decode($response->getBody(), true); } private function processArtist(object $artistData): bool { $artistName = $artistData->name ?? ""; if (!$artistName) throw new Exception("Artist name is missing from Navidrome data."); $existingArtist = $this->getArtistByName($artistName); if ($existingArtist) return true; $artistKey = sanitizeMediaString($artistName); $slug = "/music/artists/{$artistKey}"; $description = strip_tags($artistData->biography) ?? ""; $genre = $this->resolveGenreId($artistData->genres[0]->name ?? ""); $starred = $artistData->starred ?? false; $artistPayload = [ "name_string" => $artistName, "slug" => $slug, "description" => $description, "tentative" => true, "art" => $this->placeholderImageId, "mbid" => "", "favorite" => $starred, "genres" => $genre, ]; $this->saveArtist($artistPayload); return true; } private function processAlbums(string $artistId, string $artistName): void { $artist = $this->getArtistByName($artistName); if (!$artist) throw new Exception("Artist not found in the database."); $existingAlbums = $this->getExistingAlbums($artist["id"]); $existingAlbumKeys = array_column($existingAlbums, "key"); $navidromeAlbums = $this->fetchNavidromeAlbums($artistId); foreach ($navidromeAlbums as $album) { $albumName = $album["name"]; $releaseYear = $album["date"]; $artistKey = sanitizeMediaString($artistName); $albumKey = $artistKey . "-" . sanitizeMediaString($albumName); if (in_array($albumKey, $existingAlbumKeys)) { error_log("Skipping existing album: " . $albumName); continue; } try { $albumPayload = [ "name" => $albumName, "key" => $albumKey, "release_year" => $releaseYear, "artist" => $artist["id"], "artist_name" => $artistName, "art" => $this->placeholderImageId, "tentative" => true, ]; $this->saveAlbum($albumPayload); } catch (Exception $e) { error_log("Error adding album '{$albumName}': " . $e->getMessage()); } } } private function getArtistByName(string $nameString): ?array { $query = "name_string=eq." . urlencode($nameString); $response = $this->fetchFromPostgREST("artists", $query, "GET"); return $response[0] ?? null; } private function saveArtist(array $artistPayload): void { $this->fetchFromPostgREST("artists", "", "POST", $artistPayload); } private function saveAlbum(array $albumPayload): void { $this->fetchFromPostgREST("albums", "", "POST", $albumPayload); } private function resolveGenreId(string $genreName): ?string { $genres = $this->fetchFromPostgREST("genres", "name=eq." . urlencode(strtolower($genreName)), "GET"); if (!empty($genres)) return $genres[0]["id"]; return null; } private function getExistingAlbums(string $artistId): array { return $this->fetchFromPostgREST("albums", "artist=eq." . urlencode($artistId), "GET"); } } $handler = new ArtistImportHandler(); $handler->handleRequest();