wtf
All checks were successful
CI - Build Tonehaus Docker image / tonehaus-ci-build (push) Successful in 2m0s

This commit is contained in:
2025-11-28 02:00:11 +00:00
parent 1c98a634c3
commit dae8f3d999
35 changed files with 1510 additions and 82 deletions

View File

@@ -1,8 +1,10 @@
<?php
namespace App\Service;
use App\Repository\SettingRepository;
use Symfony\Contracts\Cache\CacheInterface;
use Symfony\Contracts\Cache\ItemInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
/**
@@ -77,6 +79,60 @@ class SpotifyClient
}
}
/**
* Fetches album metadata plus the full tracklist.
*
* @return array<mixed>|null
*/
public function getAlbumWithTracks(string $albumId): ?array
{
$album = $this->getAlbum($albumId);
if ($album === null) {
return null;
}
$tracks = $this->getAlbumTracks($albumId);
if ($tracks !== []) {
$album['tracks'] = $album['tracks'] ?? [];
$album['tracks']['items'] = $tracks;
$album['tracks']['total'] = count($tracks);
$album['tracks']['limit'] = count($tracks);
$album['tracks']['offset'] = 0;
$album['tracks']['next'] = null;
$album['tracks']['previous'] = null;
}
return $album;
}
/**
* Retrieves the complete tracklist for an album.
*
* @return list<array<string,mixed>>
*/
public function getAlbumTracks(string $albumId): array
{
$accessToken = $this->getAccessToken();
if ($accessToken === null) {
return [];
}
$items = [];
$limit = 50;
$offset = 0;
do {
$page = $this->requestAlbumTracksPage($albumId, $accessToken, $limit, $offset);
if ($page === null) {
break;
}
$batch = (array) ($page['items'] ?? []);
$items = array_merge($items, $batch);
$offset += $limit;
$total = isset($page['total']) ? (int) $page['total'] : null;
$hasNext = isset($page['next']) && $page['next'] !== null;
} while ($hasNext && ($total === null || $offset < $total));
return $items;
}
/**
* Fetch multiple albums with one call.
*
@@ -132,18 +188,38 @@ class SpotifyClient
return $data;
}
/**
* @return array<mixed>|null
*/
private function requestAlbumTracksPage(string $albumId, string $accessToken, int $limit, int $offset): ?array
{
$url = sprintf('https://api.spotify.com/v1/albums/%s/tracks', urlencode($albumId));
$options = [
'headers' => [ 'Authorization' => 'Bearer ' . $accessToken ],
'query' => [ 'limit' => $limit, 'offset' => $offset ],
];
try {
return $this->sendRequest('GET', $url, $options, 1200);
} catch (\Throwable) {
return null;
}
}
/**
* Retrieves a cached access token or refreshes credentials when missing.
*/
private function getAccessToken(): ?string
{
return $this->cache->get('spotify_client_credentials_token', function ($item) {
// Default to 1 hour, will adjust based on response
$cacheKey = 'spotify_client_credentials_token';
$token = $this->cache->get($cacheKey, function (ItemInterface $item) {
// Default to ~1 hour, adjusted after Spotify response
$item->expiresAfter(3500);
$clientId = $this->settings->getValue('SPOTIFY_CLIENT_ID', $this->clientId ?? '');
$clientSecret = $this->settings->getValue('SPOTIFY_CLIENT_SECRET', $this->clientSecret ?? '');
$clientId = trim((string) $this->settings->getValue('SPOTIFY_CLIENT_ID', $this->clientId ?? ''));
$clientSecret = trim((string) $this->settings->getValue('SPOTIFY_CLIENT_SECRET', $this->clientSecret ?? ''));
if ($clientId === '' || $clientSecret === '') {
// surface the miss quickly so the cache can be recomputed on the next request
$item->expiresAfter(60);
return null;
}
@@ -158,6 +234,7 @@ class SpotifyClient
$data = $response->toArray(false);
if (!isset($data['access_token'])) {
$item->expiresAfter(60);
return null;
}
@@ -168,6 +245,13 @@ class SpotifyClient
return $data['access_token'];
});
if ($token === null) {
// Remove failed entries so the next request retries instead of serving cached nulls.
$this->cache->delete($cacheKey);
}
return $token;
}
}