Files
aj-portfolio/includes/spotify.php
2025-12-23 13:18:58 +02:00

145 lines
4.7 KiB
PHP

<?php
declare(strict_types=1);
function spotify_cfg(string $key, string $default = ''): string {
$v = getenv($key);
if ($v !== false && $v !== '') return $v;
return $default;
}
function spotify_client_id(): string { return spotify_cfg('SPOTIFY_CLIENT_ID'); }
function spotify_client_secret(): string { return spotify_cfg('SPOTIFY_CLIENT_SECRET'); }
function spotify_redirect_uri(): string { return spotify_cfg('SPOTIFY_REDIRECT_URI'); }
function spotify_http_post(string $url, array $fields, array $headers = []): array {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query($fields),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 12,
CURLOPT_HTTPHEADER => array_merge([
'Content-Type: application/x-www-form-urlencoded'
], $headers),
]);
$raw = curl_exec($ch);
$code = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
$err = curl_error($ch);
curl_close($ch);
if ($raw === false) return ['ok'=>false,'code'=>$code,'error'=>$err ?: 'curl_error','data'=>null];
$data = json_decode($raw, true);
return ['ok'=>($code >= 200 && $code < 300),'code'=>$code,'error'=>$err ?: null,'data'=>$data];
}
function spotify_http_get(string $url, string $accessToken): array {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $accessToken,
'Accept: application/json',
],
]);
$raw = curl_exec($ch);
$code = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
$err = curl_error($ch);
curl_close($ch);
if ($raw === false) return ['ok'=>false,'code'=>$code,'error'=>$err ?: 'curl_error','data'=>null];
$data = ($raw !== '' ? json_decode($raw, true) : null);
return ['ok'=>($code >= 200 && $code < 300),'code'=>$code,'error'=>$err ?: null,'data'=>$data];
}
function spotify_save_tokens(?string $refresh, ?string $access, int $expiresIn): void {
$expiresAt = time() + max(0, $expiresIn) - 30; // safety margin
$stmt = pdo()->prepare("
UPDATE spotify_tokens
SET refresh_token = COALESCE(?, refresh_token),
access_token = ?,
access_expires = ?,
updated_at = ?
WHERE id = 1
");
$stmt->execute([$refresh, $access, $expiresAt, time()]);
}
function spotify_get_stored_row(): ?array {
try {
$row = pdo()->query("SELECT refresh_token, access_token, access_expires FROM spotify_tokens WHERE id=1")->fetch();
return $row ?: null;
} catch (Throwable $e) {
return null;
}
}
function spotify_exchange_code_for_tokens(string $code): bool {
$cid = spotify_client_id();
$sec = spotify_client_secret();
$redir = spotify_redirect_uri();
if ($cid === '' || $sec === '' || $redir === '') return false;
$basic = base64_encode($cid . ':' . $sec);
$res = spotify_http_post(
'https://accounts.spotify.com/api/token',
[
'grant_type' => 'authorization_code',
'code' => $code,
'redirect_uri' => $redir,
],
['Authorization: Basic ' . $basic]
);
if (!$res['ok'] || !is_array($res['data'])) return false;
$access = (string)($res['data']['access_token'] ?? '');
$refresh = (string)($res['data']['refresh_token'] ?? '');
$expires = (int)($res['data']['expires_in'] ?? 0);
if ($access === '' || $refresh === '' || $expires <= 0) return false;
spotify_save_tokens($refresh, $access, $expires);
return true;
}
function spotify_refresh_access_token(): ?string {
$row = spotify_get_stored_row();
$refresh = (string)($row['refresh_token'] ?? '');
if ($refresh === '') return null;
$cid = spotify_client_id();
$sec = spotify_client_secret();
if ($cid === '' || $sec === '') return null;
$basic = base64_encode($cid . ':' . $sec);
$res = spotify_http_post(
'https://accounts.spotify.com/api/token',
[
'grant_type' => 'refresh_token',
'refresh_token' => $refresh,
],
['Authorization: Basic ' . $basic]
);
if (!$res['ok'] || !is_array($res['data'])) return null;
$access = (string)($res['data']['access_token'] ?? '');
$expires = (int)($res['data']['expires_in'] ?? 0);
if ($access === '' || $expires <= 0) return null;
// refresh token usually not returned here; keep existing one
spotify_save_tokens(null, $access, $expires);
return $access;
}
function spotify_get_access_token(): ?string {
$row = spotify_get_stored_row();
if (!$row) return null;
$access = (string)($row['access_token'] ?? '');
$exp = (int)($row['access_expires'] ?? 0);
if ($access !== '' && $exp > time() + 30) return $access;
return spotify_refresh_access_token();
}