coryd.dev/api/artist-import.php

208 lines
6 KiB
PHP

<?php
require __DIR__ . "/Classes/ApiHandler.php";
require __DIR__ . "/Utils/init.php";
use App\Classes\ApiHandler;
use GuzzleHttp\Client;
class ArtistImportHandler extends ApiHandler
{
protected string $postgrestUrl;
protected string $postgrestApiKey;
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->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();