Note: Admin password must have been set from the server
+
+
Admin Password:
+
Enter to use admin restricted functions
+
+
+
+
Fine action control:
+
A check means that action is avalible to everyone
+
+
+
+
+
+
+
+
PartyJukebox is under an AGPLV3 liscense. You can access the source code here.
diff --git a/Client/scripts.js b/Client/scripts.js
index a63ee01..10bbd5f 100644
--- a/Client/scripts.js
+++ b/Client/scripts.js
@@ -192,6 +192,14 @@ async function checkSettings(skipServer=false) {
qrCodeGenerate()
document.getElementById("alerttimetextbox").value = alertTime
partyButtonState = document.getElementById("partymode-button").innerHTML;
+ checksforadmin = document.getElementById("admincheckholder")
+ // temporary
+ for (let i in checksforadmin.children) {
+ if (i.type == "checkbox") {
+ i.checked = true;
+ }
+ }
+ //ping the server here
x = await getFromServer({setting: "getsettings"}, "settings");
if (!(skipServer) || partyButtonState=="N/A") {
if (x["partymode"] == false) {
--
2.49.1
From 26e8e937852a72198c10c11773a0e616e4db5018 Mon Sep 17 00:00:00 2001
From: Kristy Fournier <124598538+kristy-fournier@users.noreply.github.com>
Date: Fri, 18 Jul 2025 18:31:02 -0400
Subject: [PATCH 02/12] Fixed adminperms labels
---
Client/index.html | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/Client/index.html b/Client/index.html
index 23420b7..7d14a89 100644
--- a/Client/index.html
+++ b/Client/index.html
@@ -98,11 +98,11 @@ changes visibility with JS-->
Fine action control:
A check means that action is avalible to everyone
-
-
-
-
-
+
+
+
+
+
PartyJukebox is under an AGPLV3 liscense. You can access the source code here.
--
2.49.1
From ea0380e3eb4c16c9dd00213f3c46688f1eb737d4 Mon Sep 17 00:00:00 2001
From: Kristy Fournier <124598538+kristy-fournier@users.noreply.github.com>
Date: Fri, 18 Jul 2025 18:31:22 -0400
Subject: [PATCH 03/12] changed a for loop to fix for testing
---
Client/scripts.js | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/Client/scripts.js b/Client/scripts.js
index 10bbd5f..7ee24de 100644
--- a/Client/scripts.js
+++ b/Client/scripts.js
@@ -192,11 +192,11 @@ async function checkSettings(skipServer=false) {
qrCodeGenerate()
document.getElementById("alerttimetextbox").value = alertTime
partyButtonState = document.getElementById("partymode-button").innerHTML;
- checksforadmin = document.getElementById("admincheckholder")
+ let nodeList = document.getElementById("admincheckholder").children
// temporary
- for (let i in checksforadmin.children) {
- if (i.type == "checkbox") {
- i.checked = true;
+ for (let i=0; i
Date: Fri, 18 Jul 2025 18:31:46 -0400
Subject: [PATCH 04/12] Added base of password system and check for adding a
song
---
Server/webbyBits.py | 37 +++++++++++++++++++++++++++++++++----
1 file changed, 33 insertions(+), 4 deletions(-)
diff --git a/Server/webbyBits.py b/Server/webbyBits.py
index 614e1b4..d8fe771 100644
--- a/Server/webbyBits.py
+++ b/Server/webbyBits.py
@@ -8,7 +8,20 @@ parser=argparse.ArgumentParser(description="Options for the Webby Bits")
# this is no longer needed assuming my file works correctly with the generator
# parser.add_argument('-d','--directory',help="Directory of the song files (make sure this matches the directory used for the databaseGenerator)", default="./sound/")
parser.add_argument('-p','--port',help="Port to host on, not the same as the web (client) port",default='19054')
-portTheUserPicked=parser.parse_args().port
+parser.add_argument('-a','--admin',help="Add an admin password to be used in the client. DO NOT use a password you use elsewhere",default="")
+args = parser.parse_args()
+portTheUserPicked=args.port
+ADMIN_PASS = args.admin
+if not(ADMIN_PASS):
+ ADMIN_PASS = None
+# True = everyone, False = admin only. Change in client while in use.
+controlPerms = {
+ "PP":True,
+ "SK":True,
+ "AS":True,
+ "PM":True,
+ "VOL":True
+}
fileofDB = sql.connect("songDatabase.db")
songDatabase = fileofDB.cursor()
@@ -119,9 +132,16 @@ def settingsControl():
elif recieveData["setting"] == "partymode-toggle":
partyMode = not(partyMode)
return "200"
+ elif recieveData["setting"] == "perms":
+ if ADMIN_PASS == recieveData["password"] and ADMIN_PASS:
+ controlPerms = recieveData["admin"]
+ return "200"
+ else:
+ return "401"
+
elif recieveData["setting"] == "getsettings":
# probably should have made this a different request type or something but it works
- x = {"partymode":partyMode,"volume":player.audio_get_volume()}
+ x = {"partymode":partyMode,"volume":player.audio_get_volume(),"admin":controlPerms}
return x
else:
return "400"
@@ -154,8 +174,17 @@ def searchSongDB():
@app.route("/songadd", methods=["POST"])
def songadd():
recieveData=request.get_json(force=True)
- queueSong(recieveData['song'])
- return "200"
+ if ADMIN_PASS and ADMIN_PASS == recieveData['password']:
+ # Pass exists and is correct
+ queueSong(recieveData['song'])
+ return "200"
+ elif ADMIN_PASS and not(controlPerms["AS"]):
+ # Pass exists, or this action isn't restricted
+ return "401"
+ else:
+ # No pass
+ queueSong(recieveData['song'])
+ return "200"
@app.route("/playlist", methods=["POST"])
def getPlaylist():
global songNext
--
2.49.1
From f41255e45653934e43a3017185d0da9983d3c0a0 Mon Sep 17 00:00:00 2001
From: Kristy Fournier <124598538+kristy-fournier@users.noreply.github.com>
Date: Fri, 18 Jul 2025 19:21:21 -0400
Subject: [PATCH 05/12] added server side password verification to everything
that needs it
---
Server/webbyBits.py | 44 +++++++++++++++++++++++++++++---------------
1 file changed, 29 insertions(+), 15 deletions(-)
diff --git a/Server/webbyBits.py b/Server/webbyBits.py
index d8fe771..22a84c5 100644
--- a/Server/webbyBits.py
+++ b/Server/webbyBits.py
@@ -16,11 +16,11 @@ if not(ADMIN_PASS):
ADMIN_PASS = None
# True = everyone, False = admin only. Change in client while in use.
controlPerms = {
- "PP":True,
- "SK":True,
- "AS":True,
- "PM":True,
- "VOL":True
+ "PP":True, #done
+ "SK":True, #done
+ "AS":True, #done
+ "PM":True, #done
+ "VOL":True #done
}
fileofDB = sql.connect("songDatabase.db")
@@ -111,34 +111,48 @@ def playerControls():
recieveData=request.get_json(force=True)
if recieveData["control"] != None:
if recieveData["control"] == "play-pause":
- player.pause()
- return "200"
+ if ADMIN_PASS == recieveData['password'] or not(ADMIN_PASS) or controlPerms["PP"]:
+ player.pause()
+ return "200"
+ else:
+ return "401"
elif recieveData["control"] == "skip":
- skipNow = True
- # print(str(player.get_state()))
- return "200"
+ if ADMIN_PASS == recieveData['password'] or not(ADMIN_PASS) or controlPerms["SK"]:
+ skipNow = True
+ return "200"
+ else:
+ return "401"
else:
return "400"
@app.route("/settings", methods=['POST'])
def settingsControl():
+ global controlPerms
# set the volume and partymode
global partyMode
global player
recieveData = request.get_json(force=True)
if recieveData["setting"] == "volume":
- volumePassed = player.audio_set_volume(int(recieveData["level"]))
- return {"volumePassed":volumePassed}
+ if ADMIN_PASS == recieveData['password'] or not(ADMIN_PASS) or controlPerms["VOL"]:
+ volumePassed = player.audio_set_volume(int(recieveData["level"]))
+ return {"volumePassed":volumePassed}
+ else:
+ return "401"
elif recieveData["setting"] == "partymode-toggle":
- partyMode = not(partyMode)
- return "200"
+ if ADMIN_PASS == recieveData['password'] or not(ADMIN_PASS) or controlPerms["PM"]:
+ partyMode = not(partyMode)
+ return "200"
+ else:
+ return "401"
elif recieveData["setting"] == "perms":
+ print(ADMIN_PASS)
+ print(recieveData["password"])
if ADMIN_PASS == recieveData["password"] and ADMIN_PASS:
+ #if an adminpass doesn't exist these perms can never be changed
controlPerms = recieveData["admin"]
return "200"
else:
return "401"
-
elif recieveData["setting"] == "getsettings":
# probably should have made this a different request type or something but it works
x = {"partymode":partyMode,"volume":player.audio_get_volume(),"admin":controlPerms}
--
2.49.1
From 1910b30acc32ea6979ed8a171b4d01df8e73e1fe Mon Sep 17 00:00:00 2001
From: Kristy Fournier <124598538+kristy-fournier@users.noreply.github.com>
Date: Fri, 18 Jul 2025 20:13:18 -0400
Subject: [PATCH 06/12] everything but the auto-updating settings should be
working
and also skipping isnt working on the playlist page
---
Client/index.html | 4 ++--
Client/scripts.js | 33 ++++++++++++++++++++++++++++++---
2 files changed, 32 insertions(+), 5 deletions(-)
diff --git a/Client/index.html b/Client/index.html
index 7d14a89..8bd1d0a 100644
--- a/Client/index.html
+++ b/Client/index.html
@@ -92,7 +92,7 @@ changes visibility with JS-->
Admin Password:
Enter to use admin restricted functions
-
+
Fine action control:
@@ -101,7 +101,7 @@ changes visibility with JS-->
-
+
diff --git a/Client/scripts.js b/Client/scripts.js
index 7ee24de..07d5d73 100644
--- a/Client/scripts.js
+++ b/Client/scripts.js
@@ -1,5 +1,6 @@
-let ip
-let alertTime = 2
+let ip;
+let alertTime = 2;
+let adminPass = "";
async function alertText(text="Song Added!") {
alertbox = document.getElementById("alert");
alertbox.innerHTML = text;
@@ -10,8 +11,11 @@ async function alertText(text="Song Added!") {
}
// a lot of this is kinda waffly because i was trying to get
// it to return the right stuff and javascript is asyrcronouse (boo)
-async function getFromServer(bodyInfo, source="") {
+async function getFromServer(bodyInfo, source="",password=adminPass) {
try{
+ if (bodyInfo != null) {
+ bodyInfo["password"] = password;
+ }
const response = await fetch("http://"+ip+"/"+source, {
method: "POST",
body: JSON.stringify(bodyInfo),
@@ -20,10 +24,15 @@ async function getFromServer(bodyInfo, source="") {
}
});
const data = await response.json();
+ if (data == "401") {
+ alertText("error: Admin restricted action")
+ }
return await data;
} catch(e) {
if (e == "TypeError: Failed to fetch"){
alertText("error: Can't Connect to Server (is the ip set?)")
+ } else if(e == "") {
+
} else {
alertText("error: " + e)
}
@@ -309,6 +318,22 @@ function toggleDark(e) {
}
}
+function adminPassEnter(e) {
+ if (e.key == "Enter") {
+ e.preventDefault();
+ adminPass=document.getElementById("adminpasswordbox").value
+ alertText("Admin Password Updated")
+ }
+}
+function submitPerms() {
+ let tempData = {}
+ tempData["PP"] = document.getElementById("playpausesettingcheckbox").checked
+ tempData["SK"] = document.getElementById("skipsongsettingcheckbox").checked
+ tempData["AS"] = document.getElementById("addsongsettingcheckbox").checked
+ tempData["PM"] = document.getElementById("partymodesettingcheckbox").checked
+ tempData["VOL"] = document.getElementById("partymodesettingcheckbox").checked
+ getFromServer({"setting":"perms","admin":tempData},"settings")
+}
let optionslist = []
@@ -344,6 +369,8 @@ document.getElementById("go-search").addEventListener('click', function(){search
document.getElementById("songsearch").addEventListener('keydown', function(e){searchSongsEnter(e)});
document.getElementById("iptextbox").addEventListener('keydown', function(e){ipSetEnter(e)});
document.getElementById("alerttimetextbox").addEventListener('keydown', function(e){alertTimeEnter(e)});
+document.getElementById("adminpasswordbox").addEventListener('keydown',function(e){adminPassEnter(e)});
+document.getElementById("admincheckholder").addEventListener('click',function(){submitPerms()});
document.getElementById("partymode-button").addEventListener('click',function(){controlButton("pm")})
//sets the fact that clicking a song needs to return its id to the function to find it
document.getElementById("songlist").addEventListener('click', function(e){checkWhatSongWasClicked(e)});
--
2.49.1
From 36c2286b41a12f0735554e419a9a5cc9a3809ccc Mon Sep 17 00:00:00 2001
From: Kristy Fournier <124598538+kristy-fournier@users.noreply.github.com>
Date: Fri, 3 Oct 2025 15:32:14 -0400
Subject: [PATCH 07/12] Update readme.md
---
readme.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/readme.md b/readme.md
index e63bcbd..59f555b 100644
--- a/readme.md
+++ b/readme.md
@@ -65,6 +65,7 @@ From left to right:
- The currently playing song is identified, and has the duration listed
- The play-pause button toggles playing
- The skip button goes to the next track
+ - *No "previous" button is a design decision (It's a feature not a bug)*
- The search button opens the search screen (pictured)
- The settings button (top right) opens the settings menu
- Server IP allows you to change the ip that the site connects to
--
2.49.1
From 1733a485b4ab874750e219f4db9626c073adc922 Mon Sep 17 00:00:00 2001
From: Kristy Fournier <124598538+kristy-fournier@users.noreply.github.com>
Date: Fri, 3 Oct 2025 17:26:12 -0400
Subject: [PATCH 08/12] 99% done
also added some new comments and notes about future design changes
---
Client/scripts.js | 33 ++++++++++++++++++++++++++-------
Server/webbyBits.py | 25 ++++++++++++++-----------
2 files changed, 40 insertions(+), 18 deletions(-)
diff --git a/Client/scripts.js b/Client/scripts.js
index 07d5d73..2f8130d 100644
--- a/Client/scripts.js
+++ b/Client/scripts.js
@@ -222,6 +222,16 @@ async function checkSettings(skipServer=false) {
document.getElementById("partymode-button").innerHTML = "Off";
}
document.getElementById("volumerange").value = parseInt(x["volume"])
+
+ // do the admin checkboxes here
+ // seemingly i almost finished it last time, dunno why i stopped here
+ // like as far as i can tell this is the last step
+ let currentAdminPerms = x["admin"];
+ document.getElementById("addsongsettingcheckbox").checked = currentAdminPerms["AS"];
+ document.getElementById("skipsongsettingcheckbox").checked = currentAdminPerms["SK"];
+ document.getElementById("playpausesettingcheckbox").checked = currentAdminPerms["PP"];
+ document.getElementById("partymodesettingcheckbox").checked = currentAdminPerms["PM"];
+ document.getElementById("volumechangesettingcheckbox").checked = currentAdminPerms["VOL"];
}
async function generateVisualPlaylist(conditions="") {
@@ -325,14 +335,20 @@ function adminPassEnter(e) {
alertText("Admin Password Updated")
}
}
-function submitPerms() {
+async function submitPerms() {
let tempData = {}
- tempData["PP"] = document.getElementById("playpausesettingcheckbox").checked
- tempData["SK"] = document.getElementById("skipsongsettingcheckbox").checked
- tempData["AS"] = document.getElementById("addsongsettingcheckbox").checked
- tempData["PM"] = document.getElementById("partymodesettingcheckbox").checked
- tempData["VOL"] = document.getElementById("partymodesettingcheckbox").checked
- getFromServer({"setting":"perms","admin":tempData},"settings")
+ tempData["PP"] = document.getElementById("playpausesettingcheckbox").checked;
+ tempData["SK"] = document.getElementById("skipsongsettingcheckbox").checked;
+ tempData["AS"] = document.getElementById("addsongsettingcheckbox").checked;
+ tempData["PM"] = document.getElementById("partymodesettingcheckbox").checked;
+ tempData["VOL"] = document.getElementById("partymodesettingcheckbox").checked;
+ let returncode = await getFromServer({"setting":"perms","admin":tempData},"settings");
+ if (returncode === "401") {
+ // just so that the checkboxes don't change if you click without the
+ // (I know i could do this better but this is good enough for now)
+ // okay this actually doesn't work but i have to go eat dinner
+ checkSettings();
+ }
}
let optionslist = []
@@ -350,6 +366,8 @@ document.getElementById("settings-mode").style.display = "none";
document.getElementById("volumerange").onchange = async function() {
let returnValue = await getFromServer({setting:"volume",level:this.value}, "settings")
if (returnValue["volumePassed"] !=0) {
+ // i forgot about this, i had to do this because it confused the crap out of me one time
+ // vlc doesn't let you change the volume of nothing, which makes sense if you think about it
alertText("Nothing is playing")
document.getElementById("volumerange").value = -1
}
@@ -378,6 +396,7 @@ document.getElementById("songlist").addEventListener('click', function(e){checkW
let tempWidth = document.getElementById('controls').clientWidth;
document.getElementById("controls").style.marginLeft = "-"+String(parseInt(tempWidth/2))+"px";
// document.getElementById("darkmode-button").addEventListener('click',function(){toggleDark()})
+
//for my use case (my immediate family), they dont know how to set an ip
//using this allows the creator of the link for, a qr code for example, to set the ip before distributing the code, and it would all work smoothly
//example (http://192.168.1.100:8000/?ip=192.168.1.100:19054 sets the ip to the same host at the default port)
diff --git a/Server/webbyBits.py b/Server/webbyBits.py
index 22a84c5..fb81d9f 100644
--- a/Server/webbyBits.py
+++ b/Server/webbyBits.py
@@ -10,7 +10,11 @@ parser=argparse.ArgumentParser(description="Options for the Webby Bits")
parser.add_argument('-p','--port',help="Port to host on, not the same as the web (client) port",default='19054')
parser.add_argument('-a','--admin',help="Add an admin password to be used in the client. DO NOT use a password you use elsewhere",default="")
args = parser.parse_args()
+
+
portTheUserPicked=args.port
+#Just a note that the return code "401" as of now is used to mean "you don't have the password"
+#This is not great design, and the whole "returning string codes" thing is something to add to the todo list
ADMIN_PASS = args.admin
if not(ADMIN_PASS):
ADMIN_PASS = None
@@ -36,7 +40,7 @@ elif "/" in soundLocation:
soundLocation += "/"
else:
soundLocation += "\\"
-print(soundLocation)
+#print(soundLocation)
#Create Virtual table for searching
songDatabase.execute("DROP TABLE virtualSongs;")
songDatabase.execute("CREATE VIRTUAL TABLE virtualSongs USING fts5(filename, title, artist, art, length);")
@@ -58,7 +62,7 @@ player = fakeplayer.media_player_new()
# for client side volume to work as well as possible, set system volume to 100 and control in app
player.audio_set_volume(100)
app = Flask(__name__)
-# because you are posting from another domain to this one, you need CORS
+# because you are POSTing from another domain to this one, you need CORS
CORS(app)
def queueSong(song):
@@ -124,6 +128,8 @@ def playerControls():
return "401"
else:
return "400"
+ else:
+ return "400"
@app.route("/settings", methods=['POST'])
def settingsControl():
@@ -145,8 +151,8 @@ def settingsControl():
else:
return "401"
elif recieveData["setting"] == "perms":
- print(ADMIN_PASS)
- print(recieveData["password"])
+ # print(ADMIN_PASS)
+ # print(recieveData["password"])
if ADMIN_PASS == recieveData["password"] and ADMIN_PASS:
#if an adminpass doesn't exist these perms can never be changed
controlPerms = recieveData["admin"]
@@ -188,17 +194,14 @@ def searchSongDB():
@app.route("/songadd", methods=["POST"])
def songadd():
recieveData=request.get_json(force=True)
- if ADMIN_PASS and ADMIN_PASS == recieveData['password']:
- # Pass exists and is correct
+ if (ADMIN_PASS and ADMIN_PASS == recieveData['password']):
+ # Pass exists and is correct, or it's not restricted
queueSong(recieveData['song'])
return "200"
- elif ADMIN_PASS and not(controlPerms["AS"]):
+ else:
# Pass exists, or this action isn't restricted
return "401"
- else:
- # No pass
- queueSong(recieveData['song'])
- return "200"
+
@app.route("/playlist", methods=["POST"])
def getPlaylist():
global songNext
--
2.49.1
From 76971ea75e6b8e76b54594f77f387da383ce5e62 Mon Sep 17 00:00:00 2001
From: Kristy Fournier <124598538+kristy-fournier@users.noreply.github.com>
Date: Fri, 3 Oct 2025 18:51:12 -0400
Subject: [PATCH 09/12] Done adding admin password
Just gotta do documentation
---
Client/scripts.js | 30 +++++++++++++++++-------------
Client/styles.css | 8 ++++++++
Server/webbyBits.py | 18 ++++++++++--------
3 files changed, 35 insertions(+), 21 deletions(-)
diff --git a/Client/scripts.js b/Client/scripts.js
index 2f8130d..02ac217 100644
--- a/Client/scripts.js
+++ b/Client/scripts.js
@@ -1,6 +1,8 @@
let ip;
let alertTime = 2;
let adminPass = "";
+const ERR_NO_ADMIN = "401"; // gonna use this later to refactor
+
async function alertText(text="Song Added!") {
alertbox = document.getElementById("alert");
alertbox.innerHTML = text;
@@ -25,12 +27,12 @@ async function getFromServer(bodyInfo, source="",password=adminPass) {
});
const data = await response.json();
if (data == "401") {
- alertText("error: Admin restricted action")
+ alertText("Error: Admin restricted action")
}
return await data;
} catch(e) {
if (e == "TypeError: Failed to fetch"){
- alertText("error: Can't Connect to Server (is the ip set?)")
+ alertText("Error: Can't Connect to Server (is the ip set?)")
} else if(e == "") {
} else {
@@ -224,8 +226,6 @@ async function checkSettings(skipServer=false) {
document.getElementById("volumerange").value = parseInt(x["volume"])
// do the admin checkboxes here
- // seemingly i almost finished it last time, dunno why i stopped here
- // like as far as i can tell this is the last step
let currentAdminPerms = x["admin"];
document.getElementById("addsongsettingcheckbox").checked = currentAdminPerms["AS"];
document.getElementById("skipsongsettingcheckbox").checked = currentAdminPerms["SK"];
@@ -302,8 +302,12 @@ async function generateVisualPlaylist(conditions="") {
}
async function submitSong(songid) {
- getFromServer({song: songid}, "songadd")
- alertText("Added to Queue")
+ let returncode = await getFromServer({song: songid}, "songadd");
+ if(returncode == ERR_NO_ADMIN) {
+ // right now the error is alerted in getFromServer, maybe will change that
+ } else {
+ alertText("Added to Queue");
+ }
}
function checkWhatSongWasClicked(e) {
itemId = e.srcElement.id;
@@ -335,7 +339,7 @@ function adminPassEnter(e) {
alertText("Admin Password Updated")
}
}
-async function submitPerms() {
+async function submitPerms(e) {
let tempData = {}
tempData["PP"] = document.getElementById("playpausesettingcheckbox").checked;
tempData["SK"] = document.getElementById("skipsongsettingcheckbox").checked;
@@ -343,11 +347,11 @@ async function submitPerms() {
tempData["PM"] = document.getElementById("partymodesettingcheckbox").checked;
tempData["VOL"] = document.getElementById("partymodesettingcheckbox").checked;
let returncode = await getFromServer({"setting":"perms","admin":tempData},"settings");
- if (returncode === "401") {
- // just so that the checkboxes don't change if you click without the
- // (I know i could do this better but this is good enough for now)
- // okay this actually doesn't work but i have to go eat dinner
- checkSettings();
+ if (returncode == ERR_NO_ADMIN) {
+ // if you aren't allowed to check the box then toggle it again
+ // its not perfect if you spam click, but it gets the point across to the user
+ let clickedBox = e.srcElement;
+ clickedBox.checked = !clickedBox.checked;
}
}
@@ -388,7 +392,7 @@ document.getElementById("songsearch").addEventListener('keydown', function(e){se
document.getElementById("iptextbox").addEventListener('keydown', function(e){ipSetEnter(e)});
document.getElementById("alerttimetextbox").addEventListener('keydown', function(e){alertTimeEnter(e)});
document.getElementById("adminpasswordbox").addEventListener('keydown',function(e){adminPassEnter(e)});
-document.getElementById("admincheckholder").addEventListener('click',function(){submitPerms()});
+document.getElementById("admincheckholder").addEventListener('click',function(e){submitPerms(e)});
document.getElementById("partymode-button").addEventListener('click',function(){controlButton("pm")})
//sets the fact that clicking a song needs to return its id to the function to find it
document.getElementById("songlist").addEventListener('click', function(e){checkWhatSongWasClicked(e)});
diff --git a/Client/styles.css b/Client/styles.css
index 803c5df..a6c4cdf 100644
--- a/Client/styles.css
+++ b/Client/styles.css
@@ -177,6 +177,14 @@ h4 {
border-bottom: 0;
}
+.settings > .item > h2 {
+ margin-bottom: 4px;
+}
+
+.settings > .item > p {
+ margin-top: 0px
+}
+
.versionNumber {
font-size: 8px;
font-style: italic;
diff --git a/Server/webbyBits.py b/Server/webbyBits.py
index fb81d9f..c10a04b 100644
--- a/Server/webbyBits.py
+++ b/Server/webbyBits.py
@@ -13,8 +13,10 @@ args = parser.parse_args()
portTheUserPicked=args.port
-#Just a note that the return code "401" as of now is used to mean "you don't have the password"
-#This is not great design, and the whole "returning string codes" thing is something to add to the todo list
+# Just a note that the return code "401" as of now is used to mean "you don't have the password"
+# This is not great design, and the whole "returning string codes" thing is something to add to the todo list
+# I mean returning 200 when no return is necesary i think is fine but we'll see
+ERR_NO_ADMIN = "401"
ADMIN_PASS = args.admin
if not(ADMIN_PASS):
ADMIN_PASS = None
@@ -119,13 +121,13 @@ def playerControls():
player.pause()
return "200"
else:
- return "401"
+ return ERR_NO_ADMIN
elif recieveData["control"] == "skip":
if ADMIN_PASS == recieveData['password'] or not(ADMIN_PASS) or controlPerms["SK"]:
skipNow = True
return "200"
else:
- return "401"
+ return ERR_NO_ADMIN
else:
return "400"
else:
@@ -143,13 +145,13 @@ def settingsControl():
volumePassed = player.audio_set_volume(int(recieveData["level"]))
return {"volumePassed":volumePassed}
else:
- return "401"
+ return ERR_NO_ADMIN
elif recieveData["setting"] == "partymode-toggle":
if ADMIN_PASS == recieveData['password'] or not(ADMIN_PASS) or controlPerms["PM"]:
partyMode = not(partyMode)
return "200"
else:
- return "401"
+ return ERR_NO_ADMIN
elif recieveData["setting"] == "perms":
# print(ADMIN_PASS)
# print(recieveData["password"])
@@ -158,7 +160,7 @@ def settingsControl():
controlPerms = recieveData["admin"]
return "200"
else:
- return "401"
+ return ERR_NO_ADMIN
elif recieveData["setting"] == "getsettings":
# probably should have made this a different request type or something but it works
x = {"partymode":partyMode,"volume":player.audio_get_volume(),"admin":controlPerms}
@@ -200,7 +202,7 @@ def songadd():
return "200"
else:
# Pass exists, or this action isn't restricted
- return "401"
+ return ERR_NO_ADMIN
@app.route("/playlist", methods=["POST"])
def getPlaylist():
--
2.49.1
From 189cafd08a30d83fe429f78d5b993117051bcb49 Mon Sep 17 00:00:00 2001
From: Kristy Fournier <124598538+kristy-fournier@users.noreply.github.com>
Date: Fri, 3 Oct 2025 19:03:49 -0400
Subject: [PATCH 10/12] Readme update
Should be good to be pushed to main now
---
Client/scripts.js | 2 +-
readme.md | 10 ++++++++++
2 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/Client/scripts.js b/Client/scripts.js
index 02ac217..46d1a54 100644
--- a/Client/scripts.js
+++ b/Client/scripts.js
@@ -347,7 +347,7 @@ async function submitPerms(e) {
tempData["PM"] = document.getElementById("partymodesettingcheckbox").checked;
tempData["VOL"] = document.getElementById("partymodesettingcheckbox").checked;
let returncode = await getFromServer({"setting":"perms","admin":tempData},"settings");
- if (returncode == ERR_NO_ADMIN) {
+ if (returncode == ERR_NO_ADMIN || returncode == null) {
// if you aren't allowed to check the box then toggle it again
// its not perfect if you spam click, but it gets the point across to the user
let clickedBox = e.srcElement;
diff --git a/readme.md b/readme.md
index e63bcbd..0fa7206 100644
--- a/readme.md
+++ b/readme.md
@@ -28,6 +28,7 @@ webbyBits.py
* *If getting images, this process may take a long time with a large amount of mp3 files*
4. Run `webbyBits.py`
* *The port can be customized at runtime using* `-p portNumber` *as an atribute*
+ * *You can add an admin password at runtime with* `-a AdminPass` *as an atribute*
You can now connect with the client and use the app as normal. \
*Make sure you have turned down/off any other apps that might make noise or notification sounds* \
@@ -57,6 +58,15 @@ These are specific details on each section of the app, and how to use them
- Uses port 19054 by default
- `--port (port)` changes the port for that run
- The default port can be changed in the file
+ - Running with `--admin (admin password)` sets an admin password for moderation on the client
+ - Anyone who knows the admin password can enter it on the client and change the abilities of any non-admin users (for example to limit skipping)
+ - The total set of features that can be restricted is
+ - Skip track
+ - Play-pause toggle
+ - Add track
+ - Partymode toggle
+ - Change volume
+ - When this argument is left out (or empty string) the admin features aren't used, and everyone can do everything
### Client:
 \
--
2.49.1
From d24aca7f3948f624d01dd1bf8daa685bea56ff3a Mon Sep 17 00:00:00 2001
From: Kristy Fournier <124598538+kristy-fournier@users.noreply.github.com>
Date: Sun, 5 Oct 2025 09:02:48 -0400
Subject: [PATCH 11/12] made very clear passwords are not secure
---
readme.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/readme.md b/readme.md
index 0fa7206..9a79055 100644
--- a/readme.md
+++ b/readme.md
@@ -29,6 +29,7 @@ webbyBits.py
4. Run `webbyBits.py`
* *The port can be customized at runtime using* `-p portNumber` *as an atribute*
* *You can add an admin password at runtime with* `-a AdminPass` *as an atribute*
+ * ***NOTE: Do not reuse ANY password for this, it is 100% unsecure. The best option is just a random string you write down once***
You can now connect with the client and use the app as normal. \
*Make sure you have turned down/off any other apps that might make noise or notification sounds* \
@@ -59,6 +60,7 @@ These are specific details on each section of the app, and how to use them
- `--port (port)` changes the port for that run
- The default port can be changed in the file
- Running with `--admin (admin password)` sets an admin password for moderation on the client
+ - ***Note: Do not reuse a password, consider this like making whatever this string is public, no security is guaranteed***
- Anyone who knows the admin password can enter it on the client and change the abilities of any non-admin users (for example to limit skipping)
- The total set of features that can be restricted is
- Skip track
--
2.49.1
From 86fde793056e12928479aa7186870c85a8eba337 Mon Sep 17 00:00:00 2001
From: Kristy Fournier <124598538+kristy-fournier@users.noreply.github.com>
Date: Sun, 5 Oct 2025 09:04:24 -0400
Subject: [PATCH 12/12] make VERY CLEAR the password feature is not secure
---
readme.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/readme.md b/readme.md
index 9a79055..a54546e 100644
--- a/readme.md
+++ b/readme.md
@@ -30,6 +30,7 @@ webbyBits.py
* *The port can be customized at runtime using* `-p portNumber` *as an atribute*
* *You can add an admin password at runtime with* `-a AdminPass` *as an atribute*
* ***NOTE: Do not reuse ANY password for this, it is 100% unsecure. The best option is just a random string you write down once***
+ * This is intended for protecting certain features for small closed events, not for public security
You can now connect with the client and use the app as normal. \
*Make sure you have turned down/off any other apps that might make noise or notification sounds* \
--
2.49.1