Files
radio/api/artwork.php
T

118 lines
4.4 KiB
PHP

<?php
/**
* api/artwork.php
* ---------------
* Optional album-art lookup. The browser calls this only when the stream
* provider itself did not supply artwork and ARTWORK_SOURCE allows a lookup.
* Tries Genius first, then Last.fm (whichever have keys configured). Returns:
*
* { "art": "https://…" | "", "album": "…", "url": "…" }
*
* With nothing configured it returns empty fields and the front-end falls back
* to the placeholder image.
*/
require_once __DIR__ . '/_bootstrap.php';
$empty = ['art' => '', 'album' => '', 'url' => ''];
$source = cfg('artwork.source');
if ($source === 'none' || $source === 'provider') {
json_out($empty);
}
[$artist, $title] = request_track();
if ($artist === '' && $title === '') {
json_out($empty);
}
/* -- Operator-defined overrides (config/overrides.json) -------------------- */
$overridesFile = __DIR__ . '/../config/overrides.json';
if (is_file($overridesFile)) {
$ov = json_decode((string) file_get_contents($overridesFile), true);
foreach (($ov['artwork'] ?? []) as $rule) {
$ra = $rule['artist'] ?? null;
$rt = $rule['title'] ?? null;
$artistMatch = ($ra === null || strcasecmp($ra, $artist) === 0);
$titleMatch = ($rt === null || strcasecmp($rt, $title) === 0);
if ($artistMatch && $titleMatch && !empty($rule['art'])) {
json_out(['art' => $rule['art'], 'album' => ($rule['album'] ?? ''), 'url' => '']);
}
}
}
/* -- Genius --------------------------------------------------------------- */
function genius_artwork($artist, $title) {
$token = cfg('genius.token');
if ($token === '') return null;
$headers = ['Authorization: Bearer ' . $token];
$query = rawurlencode(trim($artist . ' - ' . $title, ' -'));
$search = http_get('https://api.genius.com/search?q=' . $query, 5, $headers);
if ($search === null) return null;
$hit = dot_get(json_decode($search, true), 'response.hits.0.result', null);
if (!$hit) return null;
$out = [
'art' => (string) ($hit['song_art_image_url'] ?? $hit['header_image_url'] ?? ''),
'album' => '',
'url' => (string) ($hit['url'] ?? ''),
];
if (!empty($hit['id'])) {
$songRaw = http_get('https://api.genius.com/songs/' . rawurlencode((string) $hit['id']), 5, $headers);
if ($songRaw !== null) {
$s = dot_get(json_decode($songRaw, true), 'response.song', null);
if ($s) {
$out['album'] = (string) dot_get($s, 'album.name', '');
$better = (string) ($s['song_art_image_url'] ?? $s['header_image_url'] ?? '');
if ($better !== '') $out['art'] = $better;
}
}
}
// Genius' generic default cover counts as "no art".
if (strpos($out['art'], 'default_cover_image') !== false) $out['art'] = '';
return ($out['art'] === '' && $out['album'] === '') ? null : $out;
}
/* -- Last.fm -------------------------------------------------------------- */
function lastfm_artwork($artist, $title) {
$key = cfg('artwork.lastfm_key');
if ($key === '' || $artist === '') return null;
$url = 'https://ws.audioscrobbler.com/2.0/?method=track.getInfo&format=json&autocorrect=1'
. '&api_key=' . rawurlencode($key)
. '&artist=' . rawurlencode($artist)
. '&track=' . rawurlencode($title);
$raw = http_get($url, 5);
if ($raw === null) return null;
$data = json_decode($raw, true);
$images = dot_get($data, 'track.album.image', []);
$art = '';
if (is_array($images)) {
foreach ($images as $img) { // last non-empty = largest
if (!empty($img['#text'])) $art = $img['#text'];
}
}
if ($art === '') return null;
return ['art' => $art, 'album' => (string) dot_get($data, 'track.album.title', ''), 'url' => ''];
}
/* -- Try providers in the configured order -------------------------------- */
$order = $source === 'lastfm' ? ['lastfm']
: ($source === 'genius' ? ['genius'] : ['genius', 'lastfm']);
$album = '';
foreach ($order as $provider) {
$res = ($provider === 'genius') ? genius_artwork($artist, $title) : lastfm_artwork($artist, $title);
if ($res === null) continue;
if ($album === '' && $res['album'] !== '') $album = $res['album'];
if ($res['art'] !== '') {
$res['album'] = $res['album'] ?: $album;
json_out($res);
}
}
json_out(['art' => '', 'album' => $album, 'url' => '']);