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:
 \