import { buildCommand, type Command, type BaseCommand, parseCommand } from "./commandBuilder.ts"; type GetSong = () => string | null; export type Song = { name: string; uri: string; artists: string[]; album: { name: string; coverUrl: string | undefined; }; }; export class SpotifyWS { public ws: WebSocket; private queryList: { [x: string]: (value: Song[] | PromiseLike) => void; } = {}; constructor(ws: WebSocket) { this.ws = ws; this.onMessage = this.onMessage.bind(this); this.onClose = this.onClose.bind(this); this.ws.addEventListener("message", this.onMessage); this.ws.addEventListener("close", this.onClose); } public search(query: string): Promise { return new Promise((resolve, reject) => { if (this.ws.readyState != this.ws.OPEN) reject("WS Isn't connected."); const id = crypto.randomUUID(); this.send(buildCommand("search", { query, id })); this.queryList[id] = resolve; setTimeout(() => { reject("Timeout."); delete this.queryList[id]; }, 3000); }); } public sendSong(uri?: string) { const song = uri ?? playerManager.getNext()?.uri; if (!song) return; this.send(buildCommand("next_song", { song })); } private send(data: string) { if (this.ws.readyState != this.ws.OPEN) return; this.ws.send(data); } private onClose() { globalThis.spotify = undefined; } private onMessage(msg: MessageEvent) { const text = msg.data; const parsed = parseCommand(text); if (parsed === null) return; switch (parsed.c) { case "next_song": this.sendSong(); break; case "search": { if (!parsed.d || !parsed.d.id || !parsed.d.results) break; const cmd = parsed as Command<{id: string, results: Song[]}> const { id, results } = cmd.d; if (!(id in this.queryList)) break; this.queryList[id](results); break; } } } }