diff --git a/extension/spotiqueue.js b/extension/spotiqueue.js
index da999b1..bad7d01 100644
--- a/extension/spotiqueue.js
+++ b/extension/spotiqueue.js
@@ -1,4 +1,5 @@
-const DEFAULT_WS_URL = "ws://localhost:8000/ws/spotify";
+const DEFAULT_WS_URL =
+ localStorage.getItem("spotiqueueUrl") ?? "ws://localhost:8000/ws/spotify";
const DEFAULT_RECONNECT_MS = 1000;
class SpotiQueue {
@@ -72,14 +73,16 @@ 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) {
@@ -111,14 +114,19 @@ class SpotiQueue {
return;
}
- if (data.c === "next_song" && data.d && data.d.song) {
- try {
- Spicetify.Player.playUri(data.d.song);
- Spicetify.Player.setRepeat(false);
+ 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();
this.startedPlaying = true;
- console.log("[SpotiQueue] New song received!");
- } catch (err) {
- console.error("[SpotiQueue] Error playing received song:", err);
}
}
@@ -141,7 +149,6 @@ class SpotiQueue {
includeAuthors: true,
});
- console.log(data)
songs = data.searchV2.tracksV2.items.reduce((o, c) => {
const item = c.item.data;
@@ -151,14 +158,12 @@ 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],
},
});
@@ -171,16 +176,17 @@ class SpotiQueue {
console.log("[SpotiQueue] Found", songs);
this.send({
- "c": "search",
- "d": {
- "id": id,
- "results": songs,
+ c: "search",
+ d: {
+ id: id,
+ results: songs,
},
});
}
}
- _onError() {
+ _onError(e) {
try {
+ console.log(e);
if (this.socket) this.socket.close();
} catch {
// ignore
@@ -193,6 +199,7 @@ class SpotiQueue {
this.reconnectInterval,
this.closing,
);
+
if (!this.reconnectInterval && !this.closing) {
this.reconnectInterval = setInterval(
() => this.connect(),
@@ -200,35 +207,68 @@ class SpotiQueue {
);
}
- if (this.button) this.button.style.color = "";
+ if (this.button && !this.closing) {
+ this.button.style.color = "";
+ }
+
this.closing = false;
}
initUi() {
- 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
- }
+ const btn = new Spicetify.Topbar.Button("SpotiQueue", "enhance", () => {});
- 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();
@@ -242,14 +282,17 @@ 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;
}
- const client = new SpotiQueue();
- client.initUi();
+ globalThis.spotiQueue = new SpotiQueue();
+ globalThis.spotiQueue.initUi();
})();
diff --git a/src/main.ts b/src/main.ts
index e82b581..55eb43c 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,12 +1,11 @@
import { SpotifyWS } from "./spotify.ts"
import { HeartbeatWS } from "./heartbeat.ts";
import { PlayerManager } from "./playerManager.ts";
-import { serveFile } from "@std/http/file-server";
+import { serveDir } 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);
@@ -33,11 +32,11 @@ Deno.serve((req) => {
const spState = spotify?.ws.readyState;
if (spState !== undefined && spState !== WebSocket.CLOSED) spotify?.ws.close();
- spotify = new SpotifyWS(socket, () => song);
+ new SpotifyWS(socket);
new HeartbeatWS(socket);
return response
}
- return serveFile(req, "./static/index.html")
+ return serveDir(req, {fsRoot: "./static", urlRoot: ""})
})
diff --git a/src/playerManager.ts b/src/playerManager.ts
index 0b404bd..acfee68 100644
--- a/src/playerManager.ts
+++ b/src/playerManager.ts
@@ -58,6 +58,11 @@ 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();
@@ -69,7 +74,7 @@ export class PlayerManager {
}
private broadcastMergedQueue() {
for (const user of Object.values(this.userQueue)) {
- user.userWS.broadcastQueue(this.mergedQueue);
+ user.userWS.updateQueue();
}
}
diff --git a/src/spotify.ts b/src/spotify.ts
index d224219..9611c16 100644
--- a/src/spotify.ts
+++ b/src/spotify.ts
@@ -22,7 +22,10 @@ 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);
}
@@ -43,7 +46,10 @@ export class SpotifyWS {
public sendSong(uri?: string) {
const song = uri ?? playerManager.getNext()?.uri;
- if (!song) return;
+ if (!song) {
+ this.send(buildCommand("next_song"));
+ return
+ };
this.send(buildCommand("next_song", { song }));
}
@@ -53,8 +59,13 @@ 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 47448fc..4d52667 100644
--- a/src/user.ts
+++ b/src/user.ts
@@ -23,24 +23,23 @@ 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", {queue}))
+ this.ws.send(buildCommand("getqueue", {personal: queue, merged: playerManager.mergedQueue }))
+ }
+ public updateStatus() {
+ this.ws.send(buildCommand("status", {status: spotify !== undefined}))
}
- 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);
@@ -73,12 +72,11 @@ export class UserWS {
const cmd = command as Command<{query: string}>;
- const songs = await spotify?.search(cmd.d.query)
+ let songs = await spotify?.search(cmd.d.query).catch(_ => console.log("Failed to search..."))
if (songs === undefined) {
- this.ws.send(buildCommand("search", {songs: [], connected: false}))
- } else {
- this.ws.send(buildCommand("search", {songs, connected: true}))
- }
+ songs = []
+ }
+ this.ws.send(buildCommand("search", {songs}))
break
}
@@ -104,6 +102,11 @@ 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 bca5d1a..6f37483 100644
--- a/static/index.html
+++ b/static/index.html
@@ -1,352 +1,543 @@
-
+
- Spotify Queue
-
+
+ SpotiQueue
+
+
-
-
Enter your name
-
-
+
+
-
-
- Spotify Queue
-
-
+
+
+
+
+
+
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
My Queue
-
-
-
-
Merged Queue
-
+
+
-
-
-
-
+
+
+
+
-
+
\ No newline at end of file
diff --git a/static/old.html b/static/old.html
new file mode 100644
index 0000000..bca5d1a
--- /dev/null
+++ b/static/old.html
@@ -0,0 +1,352 @@
+
+
+
+
+
+ Spotify Queue
+
+
+
+
+
+
+
Enter your name
+
+
+
+
+
+
+ Spotify Queue
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
My Queue
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file