Initial commit
This commit is contained in:
144
includes/spotify.php
Normal file
144
includes/spotify.php
Normal file
@@ -0,0 +1,144 @@
|
||||
<?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();
|
||||
}
|
||||
Reference in New Issue
Block a user