145 lines
4.7 KiB
PHP
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();
|
|
}
|