diff --git a/Client/index.html b/Client/index.html index 6012ac4..8bd1d0a 100644 --- a/Client/index.html +++ b/Client/index.html @@ -81,13 +81,30 @@ changes visibility with JS-->

Volume of the music

-
+

Share the remote:

-

Version 1.0.2

+

Admin Settings

+

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..46d1a54 100644 --- a/Client/scripts.js +++ b/Client/scripts.js @@ -1,5 +1,8 @@ -let ip -let alertTime = 2 +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; @@ -10,8 +13,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 +26,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?)") + alertText("Error: Can't Connect to Server (is the ip set?)") + } else if(e == "") { + } else { alertText("error: " + e) } @@ -192,6 +203,14 @@ async function checkSettings(skipServer=false) { qrCodeGenerate() document.getElementById("alerttimetextbox").value = alertTime partyButtonState = document.getElementById("partymode-button").innerHTML; + let nodeList = document.getElementById("admincheckholder").children + // temporary + for (let i=0; i .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 614e1b4..c10a04b 100644 --- a/Server/webbyBits.py +++ b/Server/webbyBits.py @@ -8,7 +8,26 @@ 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 +# 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 +# True = everyone, False = admin only. Change in client while in use. +controlPerms = { + "PP":True, #done + "SK":True, #done + "AS":True, #done + "PM":True, #done + "VOL":True #done +} fileofDB = sql.connect("songDatabase.db") songDatabase = fileofDB.cursor() @@ -23,7 +42,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);") @@ -45,7 +64,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): @@ -98,30 +117,53 @@ 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 ERR_NO_ADMIN 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 ERR_NO_ADMIN else: return "400" + 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 ERR_NO_ADMIN 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 ERR_NO_ADMIN + 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 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()} + x = {"partymode":partyMode,"volume":player.audio_get_volume(),"admin":controlPerms} return x else: return "400" @@ -154,8 +196,14 @@ 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, or it's not restricted + queueSong(recieveData['song']) + return "200" + else: + # Pass exists, or this action isn't restricted + return ERR_NO_ADMIN + @app.route("/playlist", methods=["POST"]) def getPlaylist(): global songNext diff --git a/readme.md b/readme.md index 59f555b..54cdf7c 100644 --- a/readme.md +++ b/readme.md @@ -28,6 +28,9 @@ 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* + * ***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* \ @@ -57,6 +60,16 @@ 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 + - ***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 + - 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: ![image](./Screenshot_MAIN.png) \