spotify controls

This commit is contained in:
yohlo
2025-09-12 11:08:21 -05:00
parent 9d92a8a510
commit 0169468114
15 changed files with 1655 additions and 28 deletions

74
src/lib/spotify/auth.ts Normal file
View File

@@ -0,0 +1,74 @@
import type { PKCEState, SpotifyTokenResponse } from './types';
const SPOTIFY_AUTH_BASE = 'https://accounts.spotify.com';
const SPOTIFY_SCOPES = [
'user-read-playback-state',
'user-modify-playback-state',
'user-read-currently-playing',
'streaming',
].join(' ');
function generateRandomString(length: number): string {
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const values = crypto.getRandomValues(new Uint8Array(length));
return values.reduce((acc, x) => acc + possible[x % possible.length], '');
}
export class SpotifyAuth {
private clientId: string;
private redirectUri: string;
constructor(clientId: string, redirectUri: string) {
this.clientId = clientId;
this.redirectUri = redirectUri;
}
async startAuthFlow(returnPath: string = window.location.pathname): Promise<void> {
const randomState = generateRandomString(16);
const stateWithPath = btoa(JSON.stringify({
state: randomState,
returnPath: returnPath
}));
sessionStorage.setItem('spotify_state', randomState);
const params = new URLSearchParams({
response_type: 'code',
client_id: this.clientId,
scope: SPOTIFY_SCOPES,
redirect_uri: this.redirectUri,
state: stateWithPath,
});
const authUrl = `${SPOTIFY_AUTH_BASE}/authorize?${params.toString()}`;
window.location.href = authUrl;
}
async refreshAccessToken(refreshToken: string): Promise<SpotifyTokenResponse> {
const response = await fetch('/api/spotify/token', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
refresh_token: refreshToken,
}),
});
if (!response.ok) {
const error = await response.json();
throw new Error(`Token refresh failed: ${error.error || 'Unknown error'}`);
}
return response.json();
}
getReturnPath(): string {
return '/';
}
clearStoredData(): void {
sessionStorage.removeItem('spotify_state');
}
}