Basic functionality is here with a crappy ai generated frontend.
This commit is contained in:
commit
e9fdc05c2d
14 changed files with 1600 additions and 0 deletions
82
src/spotify.ts
Normal file
82
src/spotify.ts
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
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<Song[]>) => 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<Song[]> {
|
||||
return new Promise<Song[]>((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<string>) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue