ensureCliAccess(); $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 !== $this->artistImportToken) { $this->sendJsonResponse('error', 'Unauthorized access', 401); } if (! $artistId) { $this->sendJsonResponse('error', 'Artist ID is required', 400); } try { $artistData = $this->fetchNavidromeArtist($artistId); $albumData = $this->fetchNavidromeAlbums($artistId); $genre = $albumData[0]['genre'] ?? ($albumData[0]['genres'][0]['name'] ?? ''); $artistExists = $this->processArtist($artistData, $genre); if ($artistExists) { $this->processAlbums($artistId, $artistData->name, $albumData); } $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): object { $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()); } 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, string $genreName = ''): bool { $artistName = $artistData->name ?? ''; if (! $artistName) { throw new \Exception('Artist name is missing.'); } $existingArtist = $this->getArtistByName($artistName); if ($existingArtist) { return true; } $artistKey = sanitizeMediaString($artistName); $slug = "/music/artists/{$artistKey}"; $description = strip_tags($artistData->biography ?? ''); $genre = $this->resolveGenreId(strtolower($genreName)); $starred = $artistData->starred ?? false; $artistPayload = [ 'name_string' => $artistName, 'slug' => $slug, 'description' => $description, 'tentative' => true, 'art' => $this->placeholderImageId, 'mbid' => '', 'favorite' => $starred, 'genres' => $genre, ]; $this->makeRequest('POST', 'artists', ['json' => $artistPayload]); return true; } private function processAlbums(string $artistId, string $artistName, array $albumData): void { $artist = $this->getArtistByName($artistName); if (! $artist) { throw new \Exception('Artist not found after insert.'); } $existingAlbums = $this->getExistingAlbums($artist['id']); $existingAlbumKeys = array_column($existingAlbums, 'key'); foreach ($albumData as $album) { $albumName = $album['name'] ?? ''; $releaseYearRaw = $album['date'] ?? null; $releaseYear = null; if ($releaseYearRaw && preg_match('/^\d{4}/', $releaseYearRaw, $matches)) { $releaseYear = (int) $matches[0]; } $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->makeRequest('POST', 'albums', ['json' => $albumPayload]); } catch (\Exception $e) { error_log("Error adding album '{$albumName}': ".$e->getMessage()); } } } private function getArtistByName(string $nameString): ?array { $response = $this->fetchFromApi('artists', 'name_string=eq.'.urlencode($nameString)); return $response[0] ?? null; } private function getExistingAlbums(string $artistId): array { return $this->fetchFromApi('albums', 'artist=eq.'.urlencode($artistId)); } private function resolveGenreId(string $genreName): ?string { $genres = $this->fetchFromApi('genres', 'name=eq.'.urlencode(strtolower($genreName))); return $genres[0]['id'] ?? null; } } $handler = new ArtistImportHandler(); $handler->handleRequest();