coryd.dev/api/artist-import.php

208 lines
6.5 KiB
PHP

<?php
require_once __DIR__.'/../bootstrap.php';
use App\Classes\ApiHandler;
use GuzzleHttp\Client;
class ArtistImportHandler extends ApiHandler
{
private string $artistImportToken;
private string $placeholderImageId = '4cef75db-831f-4f5d-9333-79eaa5bb55ee';
private string $navidromeApiUrl;
private string $navidromeAuthToken;
public function __construct()
{
parent::__construct();
$this->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();