Sockets allowing for live updates rather than complete rebuilds #7

Merged
kristy-fournier merged 20 commits from timerChanges into main 2026-02-11 09:39:36 -05:00
3 changed files with 102 additions and 16 deletions
Showing only changes of commit 87687506b1 - Show all commits

View file

@ -2,6 +2,7 @@
let ip;
let alertTime = 2;
let adminPass = "";
let justSkipped = false
const ERR_NO_ADMIN = 401;
const VALID_FILE_EXT = ["mp3","flac","wav"];
@ -104,15 +105,17 @@ function getCookie(cname) {
async function controlButton(buttonType) {
if (buttonType == "pp") { // Play-Pause button
let result = await getFromServer({control: "play-pause"}, "controls");
console.log(result);
// console.log(result);
currentlyPlaying = result["data"]["playingState"];
} else if (buttonType == "sk") { // Skip button
clearInterval(playlistTimeTimer);
// clearInterval(playlistTimeTimer);
copilot-pull-request-reviewer[bot] commented 2026-02-11 09:28:36 -05:00 (Migrated from github.com)

else if (buttonType = "pm") assigns instead of comparing, so this branch will always execute and also mutates buttonType. Change this to a comparison (===) so only the intended button triggers the party mode toggle.

    } else if (buttonType === "pm") { //Partymode toggle (in settings)
`else if (buttonType = "pm")` assigns instead of comparing, so this branch will always execute and also mutates `buttonType`. Change this to a comparison (`===`) so only the intended button triggers the party mode toggle. ```suggestion } else if (buttonType === "pm") { //Partymode toggle (in settings) ```
let returnCode = await getFromServer({control: "skip"}, "controls");
console.log(returnCode["ok"])
// console.log(returnCode["ok"])
copilot-pull-request-reviewer[bot] commented 2026-02-11 09:28:38 -05:00 (Migrated from github.com)

In the non-OK response branch, throw new Error(data.error) makes the subsequent alertText(...) line unreachable, and also forces callers into the catch path even though you already have the server-provided error payload. Consider either removing the throw and returning the structured { ok/status/error } response, or moving the alert before throwing and ensuring callers handle a null return without crashing.


In the non-OK response branch, `throw new Error(data.error)` makes the subsequent `alertText(...)` line unreachable, and also forces callers into the catch path even though you already have the server-provided error payload. Consider either removing the throw and returning the structured `{ ok/status/error }` response, or moving the alert before throwing and ensuring callers handle a `null` return without crashing. ```suggestion ```
if(returnCode["ok"]) {
if (document.getElementById("playlist-mode").style.display == "block") {
generateVisualPlaylist("skip-button");
skipInPlaylist();
playlistElapsedSeconds = 0;
justSkipped = true;
}
}
} else if (buttonType == "pl") { // Playlist button
@ -277,7 +280,7 @@ async function displayElapsedPlaylistTime(elapsed=0,length=-1) {
}
timeLeft.innerHTML = mins.toString() +":"+ secs.toLocaleString('en-US', {minimumIntegerDigits: 2,useGrouping: false}) + "/"+ durMins.toString()+":"+durSecs.toLocaleString('en-US', {minimumIntegerDigits: 2,useGrouping: false});
// playlistElapsedSeconds++;
playlistElapsedSeconds++;
}
}
@ -325,6 +328,76 @@ async function checkSettings(skipServer=false) {
document.getElementById("duplicateallowesettingcheckbox").checked = currentAdminPerms["DUP"];
}
async function addToPlaylist(songObject) {
i = document.getElementById("playlist").children.length-1
let newItem = document.createElement("div");
newItem.className = "item";
newItem.id = Object.keys(songObject)[0];
newItem.tabIndex = 0;
let image = document.createElement("img");
try {
if (songObject[newItem.id]["art"] == null) {
throw "no image lolz"
}
image.src = songObject[newItem.id]["art"];
} catch(err){
image.src = "./images/placeholder.png";
}
image.id = String(songObject[newItem.id])+" image";
let head3 = document.createElement("h3");
head3.innerText = songObject[newItem.id]["title"];
let head4 = document.createElement("h4");
head4.innerText= songObject[newItem.id]["artist"];
let head5 = document.createElement("h5");
let timeLeft =document.createElement("h5");
timeLeft.style.fontWeight = 100;
if(i==0) {
// they can all have the text, doesn't really matter, but only the first one
// should get the ids since its the one we want to mess with
head5.id = "playing-indicator-text";
timeLeft.id = "elapsed-time-display";
}
let textdiv = document.createElement("div")
textdiv.className="text"
newItem.appendChild(image);
textdiv.appendChild(head3);
textdiv.appendChild(head4);
textdiv.appendChild(timeLeft);
textdiv.appendChild(head5);
newItem.appendChild(textdiv);
document.getElementById("playlist").appendChild(newItem);
try {
if (i == 0) { // Only the first song in the loop gets a time
head5.innerHTML="Playing";
playlistElapsedSeconds = playlist[0]["time"];
playlistSongLength = playlist[0]["length"];
displayElapsedPlaylistTime(playlistElapsedSeconds,playlistSongLength);
clearInterval(playlistTimeTimer);
}
} catch(e) {
console.log("I dunno something bad happened:"+e);
}
}
async function skipInPlaylist() {
playlistElapsedSeconds = 0;
let playlistChildren = document.getElementById("playlist").children;
if(playlistChildren[1].nodeName === "DIV") {
playlistChildren[1].remove();
}
playlistChildren = document.getElementById("playlist").children;
if(playlistChildren.length === 1) {
playlistChildren[0].innerText = "Nothing's Queued..."
} else {
let firstElementTextChildren = playlistChildren[1].children[1].children
// console.log(firstElementTextChildren);
firstElementTextChildren[2].id = "elapsed-time-display";
firstElementTextChildren[3].id = "playing-indicator-text";
firstElementTextChildren[3].textContent = "Playing";
}
displayElapsedPlaylistTime(playlistElapsedSeconds,playlistSongLength);
}
async function generateVisualPlaylist(conditions="") {
document.getElementById("playlist").innerHTML = "<h1 id=\"playlist-alert\"></h1>";
data = await getFromServer(null, "playlist");
@ -398,10 +471,10 @@ async function generateVisualPlaylist(conditions="") {
console.error(err)
}
}
}
playlistTimeTimer = setInterval(() => {
displayElapsedPlaylistTime(playlistElapsedSeconds,playlistSongLength);
},1000)
}
}
async function submitSong(songid) {
@ -591,16 +664,22 @@ socket = io("http://"+ip,{
});
socket.on("songAdd", function(data) {
console.log("recieved data from songAdd");
// console.log("recieved data from songAdd");
console.log(data);
generateVisualPlaylist();
addToPlaylist(data);
})
socket.on("timeUpdate", function(data) {
console.log("recieved data from timeUpdate");
// console.log("recieved data from timeUpdate");
console.log(data);
playlistElapsedSeconds = data["elapsedTime"];
playingState = data["playingState"]
currentlyPlaying = data["playingState"]
});
socket.on("skipSong",generateVisualPlaylist)
socket.on("skipSong",() => {
if(justSkipped === false) {
skipInPlaylist();
} else {
justSkipped = false;
}
})

View file

@ -90,21 +90,22 @@ def getSongInfo(song):
# this is a loop that plays the songs and checks for playlist changes, skips, ect.
counter = 0
isPlaying = False
def playQueuedSongs():
global skipNow
global songNext
global partyMode
global counter
global isPlaying
while True:
with playlistLock:
counter+=1
if(counter > 10):
if(counter > 2):
playingState = str(player.get_state()) == "State.Playing"
socketio.emit('timeUpdate',{"elapsedTime":player.get_time()/1000,"playingState":playingState})
counter = 0
playerState = str(player.get_state())
endStates = ["State.Ended","State.Stopped","State.NothingSpecial"]
if playerState == "State.Ended":
socketio.emit("skipSong",None)
if playlist and (playerState in endStates or skipNow == True):
# New song is in the queue and (the previous song is over or skip has been pressed)
player.stop()
@ -113,7 +114,12 @@ def playQueuedSongs():
media = vlcInstance.media_new(soundLocation+songNext)
player.set_media(media)
player.play()
isPlaying = True
socketio.emit("skipSong",None)
elif (skipNow==True or (playerState in endStates)):
if(isPlaying):
socketio.emit("skipSong",None)
isPlaying = False
# print(playerState)
# skip was pressed and there are no new songs
skipNow=False

View file

@ -1,5 +1,6 @@
## Wishlist
*Features I would like to add, will be completed in any order*
- [ ] Loading indicator while awaiting server stuff
- [ ] Refactoring existing code
- [x] Remove old comments
- [ ] Update the SQL -> Server -> Client pipeline when searching and building playlist