diff --git a/Server/__pycache__/versionNum.cpython-313.pyc b/Server/__pycache__/versionNum.cpython-313.pyc new file mode 100644 index 0000000..d9f0a4b Binary files /dev/null and b/Server/__pycache__/versionNum.cpython-313.pyc differ diff --git a/Server/static/scripts.js b/Server/static/scripts.js index 6b1e019..688c8a8 100644 --- a/Server/static/scripts.js +++ b/Server/static/scripts.js @@ -198,11 +198,17 @@ function searchSongsEnter(e) { } } -async function searchSongs(searchTerm){ +async function searchSongs(searchTerm,page=-1){ document.getElementById("songlist").innerHTML = "" - let fetchResults = await getFromServer("search?query="+searchTerm); - let searchResults = fetchResults.data; + let fetchResults = await getFromServer("search?query="+searchTerm+"&page="+page); + let searchResults = fetchResults.data.songsobj; //generate the visual song list + // let x = document.createElement("button") + // x.addEventListener("click",()=>{ + // searchSongs(searchTerm,page+1); + // }) + // x.textContent = "Next Page" + // document.getElementById("songlist-mode").appendChild(x) for(var fileName in searchResults) { let currentSongInJSON = searchResults[fileName] let newItem = document.createElement("div"); @@ -241,6 +247,8 @@ async function searchSongs(searchTerm){ if (JSON.stringify(searchResults)==JSON.stringify({})) { //display error if no results document.getElementById("songlist").innerHTML = "

We might not have that one...

"; + } else { + } } diff --git a/Server/templates/index.html b/Server/templates/index.html index 8775d11..1cbc5cf 100644 --- a/Server/templates/index.html +++ b/Server/templates/index.html @@ -113,7 +113,7 @@ changes visibility with JS-->

PartyJukebox is under an AGPLV3 liscense. You can access the source code here.

-

Release {{ REL_VER_NUM }}

+

Release {{ REL_VER_NUM }}

diff --git a/Server/versionNum.py b/Server/versionNum.py new file mode 100644 index 0000000..123cfbd --- /dev/null +++ b/Server/versionNum.py @@ -0,0 +1,67 @@ +from __future__ import annotations +class VersionNumber: + def __init__(self,major:int,minor:int,patch:int,extra:str): + self.major = major + self.minor = minor + self.patch = patch + self.extra = extra + + # From String like "x.y.z-extra" + @staticmethod + def fromString(verString:str) -> VersionNumber: + numList = verString.split(".") + major = int(numList[0]) + minor = int(numList[1]) + patch = int(numList[2].split("-")[0]) + extra = numList[2].split("-")[1] + return VersionNumber(major,minor,patch,extra) + + + def clone(verNumIn:VersionNumber) -> VersionNumber: + return VersionNumber(verNumIn.major,verNumIn.minor,verNumIn.patch,verNumIn.extra) + + def __str__(self) -> str: + returnStr = f"v{self.major}.{self.minor}.{self.patch}" + if(self.extra): + returnStr += f"-{self.extra}" + return returnStr + + def __eq__(self,comp) -> bool: + if type(comp) == VersionNumber: + return self.major == comp.major and self.minor == comp.minor and self.patch == comp.patch and self.extra == comp.extra + elif type(comp) == str: + return self == VersionNumber.fromString(comp) + elif type(comp) == type(None): + return False + else: + raise TypeError + + def __gt__(self,comp): + if type(comp) == VersionNumber: + if(self.major > comp.major): + return True + elif(self.major == comp.major and self.minor > comp.minor): + return True + elif(self.major == comp.major and self.minor == comp.minor and self.patch > comp.patch): + return True + return False + elif type(comp) == str: + return self > VersionNumber.fromString(comp) + else: + raise TypeError + + def __ge__(self,comp): + return self > comp or self == comp + +if __name__ == "__main__": + x = VersionNumber(1,2,4,"alpha") + y = VersionNumber.fromString("1.2.3-beta") + z = VersionNumber.clone(x) + print(x) + print(y) + print(z) + print(f"X == Y: {x==y}") + print(f"X > Y: {x>y}") + print(f"Y < X: {y= Y: {z>=y}") + print(f"Z <= Y: {z<=y}") diff --git a/Server/webbyBits.py b/Server/webbyBits.py index 2b611af..74f98df 100644 --- a/Server/webbyBits.py +++ b/Server/webbyBits.py @@ -1,14 +1,14 @@ -import eventlet -eventlet.monkey_patch() +# import eventlet +# eventlet.monkey_patch() from flask import Flask from flask import request,render_template -from flask_cors import CORS from flask_socketio import SocketIO import sqlite3 as sql import vlc,threading,random,argparse,dotenv,os,hashlib,string,getpass +from versionNum import VersionNumber # So i'm famously bad at following Semantic versioning, we're gonna see how this goes -REL_VER_NUM = "0.0.1" +REL_VER_NUM = VersionNumber(0,0,2,"alpha") # Argparse Stuff parser=argparse.ArgumentParser(description="Options for the Webby Bits") @@ -19,7 +19,7 @@ portTheUserPicked=os.getenv("SERVER_PORT") ERR_NO_ADMIN = ({"error":"no-admin","data":None},401) ERR_200 = ({"error":"OK","data":None},200) -ERR_MISSING_ARGS = ({"error":"Request missing required arguments","data":None}),400 +ERR_MISSING_ARGS = ({"error":"Request missing required arguments","data":None},400) if bool(args.admin) and args.admin.lower() != "false": ADMIN_PASS = hashlib.sha256(bytes(getpass.getpass("Enter AdminPass: "),'utf-8')).hexdigest() else: @@ -47,6 +47,7 @@ try: soundLocation = songDatabase.fetchall()[0][1] except sql.OperationalError: print("No Database Found, try running databaseGenerator.py") + os._exit(1) if soundLocation[-1] == "/" or soundLocation[-1] == "\\": pass elif "/" in soundLocation: @@ -147,7 +148,7 @@ def handleConnect(): @app.route("/",methods=['GET']) def returnStaticFile(): - return render_template("index.html",REL_VER_NUM=REL_VER_NUM) + return render_template("index.html",REL_VER_NUM=str(REL_VER_NUM)) @app.route("/controls", methods=['POST']) def playerControls(): @@ -232,6 +233,9 @@ def settingsControl(): @app.route("/search", methods=['GET']) def searchSongDB(): recieveData = request.args.get("query") + page = int(request.args.get("page")) + if not(page): + page = 1 fileofDB = sql.connect("songDatabase.db") songDatabase = fileofDB.cursor() try: @@ -243,6 +247,12 @@ def searchSongDB(): else: songDatabase.execute("SELECT * FROM virtualSongs WHERE virtualSongs MATCH ?",['"' + recieveData +'"']) results = songDatabase.fetchall() + pages = (len(results)//20)+1 + if(page>0): + # Numbers <0 use old rendering + inBound = 20*(page-1) + outBound = 20*page + results = results[inBound:outBound] tempdata = {} # this is a temporary solution so i dont have to change the client for i in results: @@ -255,7 +265,7 @@ def searchSongDB(): } fileofDB.close() - return {"error":"ok","data":tempdata},200 + return {"error":"ok","data":{"songsobj":tempdata,"pages":pages}},200 except sql.OperationalError as e: print(e) fileofDB.close() @@ -325,6 +335,6 @@ if __name__ == "__main__": queueThread = threading.Thread(target=playQueuedSongs) queueThread.daemon = True queueThread.start() - print(f"PartyJukebox v{REL_VER_NUM} running on port {portTheUserPicked}") + print(f"PartyJukebox {REL_VER_NUM} running on port {portTheUserPicked}") socketio.run(app=app,host='0.0.0.0', port=portTheUserPicked) \ No newline at end of file diff --git a/wishlist.md b/wishlist.md index 2602f75..13d6bfb 100644 --- a/wishlist.md +++ b/wishlist.md @@ -14,7 +14,7 @@ - [ ] Security Updates - [x] `.env` file for the api keys and other runtime info to be set, rather than in the `.py` files - [x] Hashing rather than plaintext sending passwords (that way at least the password text itself isn't transmitted over the network) - - [ ] Actually use TLS, for posting (CORS seems like an issue) + - [ ] Actually use TLS, for posting - [ ] Accessibility - [ ] Better use of semantic HTML tags - [ ] Full keyboard control (tab, enter to select, tab between control buttons)