diff --git a/extension/spotiqueue.js b/extension/spotiqueue.js index bad7d01..da999b1 100644 --- a/extension/spotiqueue.js +++ b/extension/spotiqueue.js @@ -1,5 +1,4 @@ -const DEFAULT_WS_URL = - localStorage.getItem("spotiqueueUrl") ?? "ws://localhost:8000/ws/spotify"; +const DEFAULT_WS_URL = "ws://localhost:8000/ws/spotify"; const DEFAULT_RECONNECT_MS = 1000; class SpotiQueue { @@ -73,16 +72,14 @@ class SpotiQueue { this.socket = null; } - if (this.button) this.button.style.color = "#e22134"; console.log("[SpotiQueue] Disconnecting from server, Bye!"); } send(objOrString) { if (!this.socket || this.socket.readyState !== WebSocket.OPEN) return; - const payload = - typeof objOrString === "string" - ? objOrString - : JSON.stringify(objOrString); + const payload = (typeof objOrString === "string") + ? objOrString + : JSON.stringify(objOrString); try { this.socket.send(payload); } catch (err) { @@ -114,19 +111,14 @@ class SpotiQueue { return; } - if (data.c === "next_song") { - if (data.d && data.d.song) { - try { - Spicetify.Player.playUri(data.d.song); - Spicetify.Player.setRepeat(false); - this.startedPlaying = true; - console.log("[SpotiQueue] New song received!"); - } catch (err) { - console.error("[SpotiQueue] Error playing received song:", err); - } - } else { - Spicetify.Player.play(); + if (data.c === "next_song" && data.d && data.d.song) { + try { + Spicetify.Player.playUri(data.d.song); + Spicetify.Player.setRepeat(false); this.startedPlaying = true; + console.log("[SpotiQueue] New song received!"); + } catch (err) { + console.error("[SpotiQueue] Error playing received song:", err); } } @@ -149,6 +141,7 @@ class SpotiQueue { includeAuthors: true, }); + console.log(data) songs = data.searchV2.tracksV2.items.reduce((o, c) => { const item = c.item.data; @@ -158,12 +151,14 @@ class SpotiQueue { o.push({ name: item.name, uri: item.uri, - artists: item.artists.items.map((v) => v.profile?.name), + artists: item.artists.items.map(v => v.profile?.name), album: { name: album.name, - coverUrl: album.coverArt.sources - .sort((a, b) => b.height - a.height) - .map((v) => v.url)[0], + coverUrl: album.coverArt.sources.sort((a, b) => + b.height - a.height + ).map((v) => + v.url + )[0], }, }); @@ -176,17 +171,16 @@ class SpotiQueue { console.log("[SpotiQueue] Found", songs); this.send({ - c: "search", - d: { - id: id, - results: songs, + "c": "search", + "d": { + "id": id, + "results": songs, }, }); } } - _onError(e) { + _onError() { try { - console.log(e); if (this.socket) this.socket.close(); } catch { // ignore @@ -199,7 +193,6 @@ class SpotiQueue { this.reconnectInterval, this.closing, ); - if (!this.reconnectInterval && !this.closing) { this.reconnectInterval = setInterval( () => this.connect(), @@ -207,68 +200,35 @@ class SpotiQueue { ); } - if (this.button && !this.closing) { - this.button.style.color = ""; - } - + if (this.button) this.button.style.color = ""; this.closing = false; } initUi() { - const btn = new Spicetify.Topbar.Button("SpotiQueue", "enhance", () => {}); + const btn = new Spicetify.Topbar.Button( + "SpotiQueue", + `

hi

`, // SVG icon or markup + () => { + if (!this.socket) { + if (Spicetify.GraphQL.Definitions.searchDesktop === undefined) { + Spicetify.showNotification("Please search something (e.g a song) before trying to use SpotiQueue. This will load required dependencies.") + return + } + this.connect(); + this.button.innerText = "bye"; + } else { + this.stop(); + this.button.innerText = "hi"; + } + }, + ); this.button = btn.button; - this.button.style.color = "#e22134"; - this.button.addEventListener("click", (e) => { - console.log(e.pointerId); - if (this.socket) { - this.stop(); - return; - } - - if (Spicetify.GraphQL.Definitions.searchDesktop === undefined) { - Spicetify.Platform.History.push("/search/"); - setTimeout(() => { - this.button.click(); - Spicetify.Platform.History.goBack(); - }, 200); - return; - } - - this.closing = false; - this.button.style.color = ""; - this.connect(); - }); - this.button.addEventListener("contextmenu", (e) => { - e.preventDefault(); - - const div = document.createElement("div"); - div.innerHTML = ` -
- - -
- `; - Spicetify.PopupModal.display({ title: "WebSocket URL", content: div }); - - div.querySelector("input").value = this.wsUrl - div.querySelector("button").onclick = () => { - const newUrl = div.querySelector("input").value.trim(); - if (!newUrl) return; - - localStorage.setItem("spotiqueueUrl", newUrl); - this.wsUrl = newUrl; - this.stop(); - Spicetify.PopupModal.hide(); - Spicetify.showNotification("Saved!"); - }; - }); Spicetify.Player.addEventListener("songchange", (info) => { console.log(info); if ( - this.socket && - this.socket.readyState === WebSocket.OPEN && + this.socket && this.socket.readyState === WebSocket.OPEN && !this.startedPlaying ) { Spicetify.Player.pause(); @@ -282,17 +242,14 @@ class SpotiQueue { (function init() { if ( - !Spicetify.Player || - !Spicetify.Platform || - !Spicetify.GraphQL || - !Spicetify.GraphQL.Request || - !Spicetify.GraphQL.Definitions + !Spicetify.Player || !Spicetify.Platform || !Spicetify.GraphQL || + !Spicetify.GraphQL.Request || !Spicetify.GraphQL.Definitions ) { setTimeout(init, 100); console.log("[SpotiQueue] loading extension... "); return; } - globalThis.spotiQueue = new SpotiQueue(); - globalThis.spotiQueue.initUi(); + const client = new SpotiQueue(); + client.initUi(); })(); diff --git a/src/main.ts b/src/main.ts index 55eb43c..e82b581 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,11 +1,12 @@ import { SpotifyWS } from "./spotify.ts" import { HeartbeatWS } from "./heartbeat.ts"; import { PlayerManager } from "./playerManager.ts"; -import { serveDir } from "@std/http/file-server"; +import { serveFile } from "@std/http/file-server"; import { UserWS } from "./user.ts"; const USER_PATTERN = new URLPattern({pathname: "/ws/user"}) const SPOTIFY_PATTERN = new URLPattern({pathname: "/ws/spotify"}) +const song = "spotify:track:2VxJVGiTsK6UyrxPJJ2lR9"; function commonWs(req: Request) { const { socket, response } = Deno.upgradeWebSocket(req); @@ -32,11 +33,11 @@ Deno.serve((req) => { const spState = spotify?.ws.readyState; if (spState !== undefined && spState !== WebSocket.CLOSED) spotify?.ws.close(); - new SpotifyWS(socket); + spotify = new SpotifyWS(socket, () => song); new HeartbeatWS(socket); return response } - return serveDir(req, {fsRoot: "./static", urlRoot: ""}) + return serveFile(req, "./static/index.html") }) diff --git a/src/playerManager.ts b/src/playerManager.ts index acfee68..0b404bd 100644 --- a/src/playerManager.ts +++ b/src/playerManager.ts @@ -58,11 +58,6 @@ export class PlayerManager { return song.song; } - public broadcastStatus() { - for (const user of Object.values(this.userQueue)) { - user.userWS.updateStatus(); - } - } public updateMergedQueue() { this.generateMergedQueue(); this.broadcastMergedQueue(); @@ -74,7 +69,7 @@ export class PlayerManager { } private broadcastMergedQueue() { for (const user of Object.values(this.userQueue)) { - user.userWS.updateQueue(); + user.userWS.broadcastQueue(this.mergedQueue); } } diff --git a/src/spotify.ts b/src/spotify.ts index 9611c16..d224219 100644 --- a/src/spotify.ts +++ b/src/spotify.ts @@ -22,10 +22,7 @@ export class SpotifyWS { this.onMessage = this.onMessage.bind(this); this.onClose = this.onClose.bind(this); - this.onOpen = this.onOpen.bind(this); - this.ws.addEventListener("message", this.onMessage); - this.ws.addEventListener("open", this.onOpen); this.ws.addEventListener("close", this.onClose); } @@ -46,10 +43,7 @@ export class SpotifyWS { public sendSong(uri?: string) { const song = uri ?? playerManager.getNext()?.uri; - if (!song) { - this.send(buildCommand("next_song")); - return - }; + if (!song) return; this.send(buildCommand("next_song", { song })); } @@ -59,13 +53,8 @@ export class SpotifyWS { this.ws.send(data); } - private onOpen() { - globalThis.spotify = this; - playerManager.broadcastStatus(); - } private onClose() { globalThis.spotify = undefined; - playerManager.broadcastStatus(); } private onMessage(msg: MessageEvent) { const text = msg.data; diff --git a/src/user.ts b/src/user.ts index 4d52667..47448fc 100644 --- a/src/user.ts +++ b/src/user.ts @@ -23,23 +23,24 @@ export class UserWS { this.ws.send(buildCommand("logout")) this.ws.close(); } + public broadcastQueue(queue: MergedQueue) { + this.ws.send(buildCommand("updatemergedqueue", {queue})) + } public updateQueue() { const queue = this.playerQueue?.queue; if (!queue) return; - this.ws.send(buildCommand("getqueue", {personal: queue, merged: playerManager.mergedQueue })) - } - public updateStatus() { - this.ws.send(buildCommand("status", {status: spotify !== undefined})) + this.ws.send(buildCommand("getqueue", {queue})) } + private spotifyConnected(): boolean { + return spotify === undefined; + } private login(name: string) { this.name = name; this.loggedIn = true; this.playerQueue = playerManager.login(name, this); - this.updateQueue(); - this.updateStatus(); } private async onMessage(msg: MessageEvent) { const command = parseCommand(msg.data); @@ -72,11 +73,12 @@ export class UserWS { const cmd = command as Command<{query: string}>; - let songs = await spotify?.search(cmd.d.query).catch(_ => console.log("Failed to search...")) + const songs = await spotify?.search(cmd.d.query) if (songs === undefined) { - songs = [] - } - this.ws.send(buildCommand("search", {songs})) + this.ws.send(buildCommand("search", {songs: [], connected: false})) + } else { + this.ws.send(buildCommand("search", {songs, connected: true})) + } break } @@ -102,11 +104,6 @@ export class UserWS { this.playerQueue.queue.push(cmd.d.song); playerManager.updateMergedQueue(); - break - } - case "status": { - this.updateStatus(); - break } } } diff --git a/static/index.html b/static/index.html index 6f37483..bca5d1a 100644 --- a/static/index.html +++ b/static/index.html @@ -1,543 +1,352 @@ - + - - SpotiQueue - - + Spotify Queue + - -
-
-
-

Login

- -
-
- - - - - - - -
- -
-
-
+ - -