spotify state resume/capture
This commit is contained in:
@@ -2,6 +2,7 @@ import type {
|
||||
SpotifyDevice,
|
||||
SpotifyDevicesResponse,
|
||||
SpotifyPlaybackState,
|
||||
SpotifyPlaybackSnapshot,
|
||||
SpotifyError,
|
||||
} from './types';
|
||||
|
||||
@@ -125,4 +126,74 @@ export class SpotifyWebApiClient {
|
||||
updateAccessToken(accessToken: string): void {
|
||||
this.accessToken = accessToken;
|
||||
}
|
||||
|
||||
async createPlaybackSnapshot(): Promise<SpotifyPlaybackSnapshot | null> {
|
||||
const state = await this.getPlaybackState();
|
||||
if (!state || !state.item) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const snapshot: SpotifyPlaybackSnapshot = {
|
||||
contextUri: state.context?.uri,
|
||||
trackUri: state.item.external_urls.spotify,
|
||||
trackId: state.item.id,
|
||||
positionMs: state.progress_ms || 0,
|
||||
shuffleState: state.shuffle_state,
|
||||
repeatState: state.repeat_state,
|
||||
volumePercent: state.device.volume_percent || undefined,
|
||||
deviceId: state.device.id,
|
||||
timestamp: Date.now(),
|
||||
isPlaying: state.is_playing,
|
||||
trackName: state.item.name,
|
||||
artistName: state.item.artists.map(a => a.name).join(', '),
|
||||
albumName: state.item.album.name,
|
||||
albumImageUrl: state.item.album.images[2]?.url,
|
||||
};
|
||||
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
async restorePlaybackSnapshot(snapshot: SpotifyPlaybackSnapshot): Promise<void> {
|
||||
if (snapshot.deviceId) {
|
||||
await this.transferPlayback(snapshot.deviceId, false);
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
}
|
||||
|
||||
const playbackRequest: any = {
|
||||
position_ms: snapshot.positionMs,
|
||||
};
|
||||
|
||||
if (snapshot.contextUri && snapshot.trackId) {
|
||||
playbackRequest.context_uri = snapshot.contextUri;
|
||||
playbackRequest.offset = { uri: `spotify:track:${snapshot.trackId}` };
|
||||
} else if (snapshot.trackId) {
|
||||
playbackRequest.uris = [`spotify:track:${snapshot.trackId}`];
|
||||
}
|
||||
|
||||
const endpoint = snapshot.deviceId
|
||||
? `/me/player/play?device_id=${snapshot.deviceId}`
|
||||
: '/me/player/play';
|
||||
|
||||
await this.request(endpoint, {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify(playbackRequest),
|
||||
});
|
||||
|
||||
await Promise.all([
|
||||
this.request(`/me/player/shuffle?state=${snapshot.shuffleState}`, {
|
||||
method: 'PUT',
|
||||
}),
|
||||
this.request(`/me/player/repeat?state=${snapshot.repeatState}`, {
|
||||
method: 'PUT',
|
||||
}),
|
||||
snapshot.volumePercent !== undefined
|
||||
? this.setVolume(snapshot.volumePercent)
|
||||
: Promise.resolve(),
|
||||
]);
|
||||
|
||||
if (!snapshot.isPlaying) {
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
await this.pause();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,6 +82,10 @@ export interface SpotifyContextType extends SpotifyAuthState {
|
||||
isLoading: boolean;
|
||||
error: string | null;
|
||||
|
||||
capturedState: SpotifyPlaybackSnapshot | null;
|
||||
isCaptureLoading: boolean;
|
||||
isResumeLoading: boolean;
|
||||
|
||||
login: () => void;
|
||||
logout: () => void;
|
||||
|
||||
@@ -95,6 +99,10 @@ export interface SpotifyContextType extends SpotifyAuthState {
|
||||
setActiveDevice: (deviceId: string) => Promise<void>;
|
||||
|
||||
refreshPlaybackState: () => Promise<void>;
|
||||
|
||||
capturePlaybackState: () => Promise<void>;
|
||||
resumePlaybackState: () => Promise<void>;
|
||||
clearCapturedState: () => void;
|
||||
}
|
||||
|
||||
export interface PKCEState {
|
||||
@@ -105,4 +113,21 @@ export interface PKCEState {
|
||||
|
||||
export interface SpotifyDevicesResponse {
|
||||
devices: SpotifyDevice[];
|
||||
}
|
||||
|
||||
export interface SpotifyPlaybackSnapshot {
|
||||
contextUri?: string;
|
||||
trackUri?: string;
|
||||
trackId?: string;
|
||||
positionMs: number;
|
||||
shuffleState: boolean;
|
||||
repeatState: 'off' | 'context' | 'track';
|
||||
volumePercent?: number;
|
||||
deviceId?: string;
|
||||
timestamp: number;
|
||||
isPlaying: boolean;
|
||||
trackName?: string;
|
||||
artistName?: string;
|
||||
albumName?: string;
|
||||
albumImageUrl?: string;
|
||||
}
|
||||
Reference in New Issue
Block a user