/* * Keeps the active playlist in memory between player-state requests. * State, command and discovery requests share a queue so responses cannot * overwrite newer state or reuse tracks from a different playlist version. */ export class PlayerStateClient { #sendRequest; #snapshot = null; #pending = Promise.resolve(); // sendRequest performs the application's JSON GET/POST transport. constructor(sendRequest) { this.#sendRequest = sendRequest; } // Queues a state-producing request and returns a complete browser snapshot. // A failed request does not prevent subsequent requests from being sent. request(path, body) { const result = this.#pending.then(() => this.#read(path, body)); this.#pending = result.catch(() => {}); return result; } // Sends the cached version and restores omitted tracks from that exact version. async #read(path, body) { if (this.#snapshot) { path += `?playlistVersion=${encodeURIComponent(this.#snapshot.playlistVersion)}`; } const nextState = await this.#sendRequest(path, body); if (nextState.tracks === null) { if (!this.#snapshot || nextState.playlistVersion !== this.#snapshot.playlistVersion) { this.#snapshot = null; throw new Error("Playlist cache does not match the server version."); } nextState.tracks = this.#snapshot.tracks; } this.#snapshot = nextState; return nextState; } }