diff --git a/Client/favicon.ico b/Client/favicon.ico
new file mode 100644
index 0000000..bbe20ae
Binary files /dev/null and b/Client/favicon.ico differ
diff --git a/Client/images/placeholder.png b/Client/images/placeholder.png
new file mode 100644
index 0000000..a684493
Binary files /dev/null and b/Client/images/placeholder.png differ
diff --git a/Client/images/play-pause.png b/Client/images/play-pause.png
new file mode 100644
index 0000000..3796891
Binary files /dev/null and b/Client/images/play-pause.png differ
diff --git a/Client/images/playlist.png b/Client/images/playlist.png
new file mode 100644
index 0000000..e9ff72a
Binary files /dev/null and b/Client/images/playlist.png differ
diff --git a/Client/images/search.png b/Client/images/search.png
new file mode 100644
index 0000000..b3f699a
Binary files /dev/null and b/Client/images/search.png differ
diff --git a/Client/images/settings.png b/Client/images/settings.png
new file mode 100644
index 0000000..bb23a10
Binary files /dev/null and b/Client/images/settings.png differ
diff --git a/Client/images/skip.png b/Client/images/skip.png
new file mode 100644
index 0000000..032beda
Binary files /dev/null and b/Client/images/skip.png differ
diff --git a/Client/index.html b/Client/index.html
new file mode 100644
index 0000000..e9aee2f
--- /dev/null
+++ b/Client/index.html
@@ -0,0 +1,90 @@
+
+
+
+ Jukebox Controller
+
+
+
+
+
+
+
+
+
+
Jukebox Remote
+
Add songs to the shared playlist below!
+
+
+
+
+
+ Go!
+
+
+
Search to find songs!
+
+
+
+
+
+
+
+
+
+
Client Settings (Saved to device)
+
+
Server IP:
+
IP of the device running the song server
+
+
+
+
Alert Time:
+
How long alerts stay on screen for (seconds)
+
+
+
Server Settings (Saved to server)
+
+
Party mode:
+
Add random songs to the queue when it is about to be empty
+
N/A
+
+
+
Volume:
+
Volume of the music
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Client/manifest.json b/Client/manifest.json
new file mode 100644
index 0000000..7ab15d3
--- /dev/null
+++ b/Client/manifest.json
@@ -0,0 +1,15 @@
+{
+ "name": "Jukebox Remote",
+ "short_name": "Jukebox Remote",
+ "start_url": "index.html",
+ "display": "standalone",
+ "background_color": "#eeeeee",
+ "theme_color": "#eeeeee",
+ "orientation": "portrait-primary",
+ "icons": [
+ {
+ "src": "/favicon.ico",
+ "type": "image/ico", "sizes": "100x100"
+ }
+ ]
+ }
\ No newline at end of file
diff --git a/Client/scripts.js b/Client/scripts.js
new file mode 100644
index 0000000..3aafbff
--- /dev/null
+++ b/Client/scripts.js
@@ -0,0 +1,329 @@
+let ip
+let alertTime = 2
+async function alertText(text="Song Added!") {
+ alertbox = document.getElementById("alert");
+ alertbox.innerHTML = text;
+ await new Promise(r => setTimeout(r, alertTime*1000));
+ alertbox.innerHTML = ""
+}
+// 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="") {
+ try{
+ const response = await fetch("http://" + ip+"/"+source, {
+ method: "POST",
+ body: JSON.stringify(bodyInfo),
+ headers: {
+ "Content-type": "application/json; charset=UTF-8"
+ }
+ });
+ const data = await response.json();
+ return await data;
+ } catch(e) {
+ if (e == "TypeError: Failed to fetch"){
+ alertText("error: NoConnect to Server (is the ip set?)")
+ } else {
+ alertText("error: " + e)
+ }
+ const response=null;
+ return response;
+ }
+}
+
+
+//cookie reader is taken from internet because cookies ae too complicated for me
+//i still understand how it works though promise just i see no reason to write this from scratch
+function getCookie(cname) {
+ let name = cname + "=";
+ let decodedCookie = decodeURIComponent(document.cookie);
+ let ca = decodedCookie.split(';');
+ for(let i = 0; i ";
+ document.getElementById("playlist-mode").style.display = "block";
+ document.getElementById("songlist-mode").style.display = "none";
+ document.getElementById("settings-mode").style.display = "none";
+ generateVisualPlaylist();
+ } else if (buttonType == "se") {
+ document.getElementById("songlist").innerHTML = "Search to find songs! ";
+ document.getElementById("playlist").innerHTML = "";
+ document.getElementById("playlist-mode").style.display = "none";
+ document.getElementById("songlist-mode").style.display = "block";
+ document.getElementById("settings-mode").style.display = "none";
+ } else if (buttonType == "st") {
+ document.getElementById("songlist").innerHTML = "";
+ document.getElementById("playlist").innerHTML = "";
+ document.getElementById("playlist-mode").style.display = "none";
+ document.getElementById("songlist-mode").style.display = "none";
+ document.getElementById("settings-mode").style.display = "block";
+ checkSettings()
+ } else if (buttonType = "pm") {
+ await getFromServer({setting: "partymode-toggle"}, "settings")
+ checkSettings(true)
+ }
+
+
+}
+
+function searchSongsEnter(e) {
+ if (e.keyCode == 13) {
+ searchSongs(document.getElementById("songsearch").value)
+ }
+}
+
+async function searchSongs(searchTerm){
+ let optionslist = []
+
+ document.getElementById("songlist").innerHTML = ""
+ searchResults = await getFromServer({search:searchTerm},"search").then()
+ for (let index in searchResults) {
+ optionslist.push([index,searchResults[index][0],searchResults[index][1],searchResults[index][2]]);
+ }
+ //generate the visual song list
+ for(let i = 0; i < optionslist.length; i++) {
+ let newItem = document.createElement("div");
+ newItem.className = "item";
+ newItem.id = optionslist[i][3];
+ let image = document.createElement("img");
+ try {
+ if (optionslist[i][2] == null) {
+ throw "no image lolz"
+ }
+ image.src = optionslist[i][2];
+ } catch(err){
+ image.src = "./images/placeholder.png";
+ }
+ image.id = String(optionslist[i][3])+" image";
+ let head3 = document.createElement("h3");
+ head3.innerText = optionslist[i][0];
+ let head4 = document.createElement("h4");
+ head4.innerText=optionslist[i][1];
+ newItem.appendChild(image);
+ newItem.appendChild(head3);
+ newItem.appendChild(head4);
+ document.getElementById("songlist").appendChild(newItem);
+ //display error if no results
+
+ }
+ if (optionslist.length == 0) {
+ document.getElementById("songlist").innerHTML = "We might not have that one... ";
+ }
+}
+function alertTimeEnter(e){
+ if (e.key == "Enter") {
+ e.preventDefault();
+ alertTimeSet(document.getElementById("alerttimetextbox").value);
+ }
+}
+
+function alertTimeSet(time) {
+ alertTime = time;
+ document.cookie = "alertTime="+alertTime+"; path=/;"
+ alertText("Alerts stay on screen for " + alertTime.toString() + " seconds")
+}
+
+function ipSetEnter(e){
+ if (e.key == "Enter") {
+ e.preventDefault();
+ ipSetter(document.getElementById("iptextbox").value)
+ }
+}
+
+function ipSetter(){
+ ipBox = document.getElementById("iptextbox").value
+ if (ipBox == "") {
+ alertText("Your IP is set to "+ip)
+ } else {
+ if (ipBox.includes(":")) {
+ port = ipBox.slice(ipBox.indexOf(":")+1)
+ ip = ipBox;
+ document.cookie = "ip="+ip+"; path=/;"
+ alertText("Your IP is now set to "+ip.slice(0, ipBox.indexOf(":"))+" at port "+port)
+ } else {
+ ip = ipBox + ":19054"
+ document.cookie = "ip="+ip+"; path=/;"
+ alertText("Your IP is now set to "+ipBox+" at port 19054 (Default)")
+ }
+ }
+
+}
+
+async function checkSettings(skipServer=false) {
+ //check client stuff first so if the server doesn't exist it can still be changed and seen
+ if (ip.slice(-5)=="19054") {
+ document.getElementById("iptextbox").value = ip.slice(0,-6)
+ } else {
+ document.getElementById("iptextbox").value = ip;
+ }
+ document.getElementById("alerttimetextbox").value = alertTime
+ partyButtonState = document.getElementById("partymode-button").innerHTML;
+ x = await getFromServer({setting: "getsettings"}, "settings");
+ if (!(skipServer) || partyButtonState=="N/A") {
+ if (x["partymode"] == false) {
+ document.getElementById("partymode-button").innerHTML = "Off";
+ } else {
+ document.getElementById("partymode-button").innerHTML = "On";
+ }
+ } else if (document.getElementById("partymode-button").innerHTML == "Off") {
+ document.getElementById("partymode-button").innerHTML = "On";
+ } else {
+ document.getElementById("partymode-button").innerHTML = "Off";
+ }
+ document.getElementById("volumerange").value = parseInt(x["volume"])
+}
+
+async function generateVisualPlaylist(conditions="") {
+ document.getElementById("playlist").innerHTML = " ";
+ playlist = await getFromServer(null, "playlist");
+ if (playlist.length==0){
+ document.getElementById("playlist-alert").innerHTML = "Nothing's Queued..."
+ } else {
+ if (conditions=="skip-button") {
+ playlist.shift()
+ if (playlist.length==0){
+ document.getElementById("playlist-alert").innerHTML = "Nothing's Queued..."
+ }
+ }
+ for (i in playlist) {
+ let newItem = document.createElement("div");
+ newItem.className = "item";
+ newItem.id = playlist[i]["file"];
+ let image = document.createElement("img");
+ try {
+ if (playlist[i]["art"] == null) {
+ throw "no image lolz"
+ }
+ image.src = playlist[i]["art"];
+ } catch(err){
+ image.src = "./images/placeholder.png";
+ }
+ image.id = String(playlist[i]["file"])+" image";
+ let head3 = document.createElement("h3");
+ head3.innerText = playlist[i]["title"];
+ let head4 = document.createElement("h4");
+ head4.innerText=playlist[i]["artist"];
+ let head5 = document.createElement("h5");
+ let timeLeft =document.createElement("h5");
+ timeLeft.style.fontWeight = 100;
+ try {
+ if (i == 0) {
+ head5.innerHTML="Playing";
+ if ((conditions != "skip-button")) {
+ let mins = Math.floor(playlist[i]["time"]/60);
+ let secs = Math.floor(playlist[i]["time"]%60);
+ let durMins = Math.floor(playlist[i]["length"]/60);
+ let durSecs = Math.floor(playlist[i]["length"]%60);
+ timeLeft.innerHTML = mins.toString() +":"+ secs.toLocaleString('en-US', {minimumIntegerDigits: 2,useGrouping: false}) + "/"+ durMins.toString()+":"+durSecs.toLocaleString('en-US', {minimumIntegerDigits: 2,useGrouping: false});
+ }
+ }
+ }catch(err){
+ console.log(err)
+ }
+ 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);
+ }
+ }
+}
+
+async function submitSong(songid) {
+ getFromServer({song: songid}, "songadd")
+ alertText("Added to Queue")
+}
+function checkWhatSongWasClicked(e) {
+ itemId = e.srcElement.id;
+ if ((itemId.length-itemId.lastIndexOf("image") == 5) && itemId.lastIndexOf("image")!=-1) {
+ itemId = itemId.slice(0,-6)
+ }
+ //i feel like later dylan won't apreciate this
+ //one of my files was "file.MP3" so it didn't work
+ if (itemId.slice(-4).toLowerCase() == ".mp3") {
+ submitSong(itemId);
+ }
+}
+
+let optionslist = []
+
+//sets all de stuff for buttons
+document.addEventListener('keydown', function(e){
+ if (e.key == "/"){
+ document.getElementById("title").scrollIntoView();
+ document.getElementById("songsearch").select();
+ e.preventDefault()
+}})
+document.getElementById("playlist-mode").style.display = "none";
+document.getElementById("settings-mode").style.display = "none";
+//.ontouch for mobile??
+document.getElementById("volumerange").onchange = function() {
+ getFromServer({setting:"volume",level:this.value}, "settings")
+ if (this.value == 0) {
+ alertText("The volume is now set to 0 (Pause?)")
+ } else {
+ alertText("The volume is now set to " + this.value.toString())
+ }
+
+}
+document.getElementById("settings-button").addEventListener('click',function(){controlButton("st")});
+document.getElementById("play-pause-button").addEventListener('click', function(){controlButton("pp")});
+document.getElementById("playlist-button").addEventListener('click', function(){controlButton("pl")});
+document.getElementById("search-button").addEventListener('click', function(){controlButton("se")});
+document.getElementById("skip-button").addEventListener('click',function(){controlButton("sk")});
+document.getElementById("go-search").addEventListener('click', function(){searchSongs(document.getElementById("songsearch").value)})
+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("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)});
+//makes the controls look mostly normal on all screens, best solution i could find, idk man
+let tempWidth = document.getElementById('controls').clientWidth;
+document.getElementById("controls").style.marginLeft = "-"+String(parseInt(tempWidth/2))+"px";
+//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)
+//the port must be set manually using this method, but only has to be done once for the url that ends up being shared
+let params = new URLSearchParams(location.search);
+//tries the url first, then the cookie, then the default
+ip = params.get("ip")
+if (ip == null || ip=="") {
+ ip=getCookie("ip")
+}
+console.log(ip)
+if (ip==null || ip==""){
+ ip = ""
+}
+document.cookie = "ip="+ip+"; path=/;"
+
+
+alertTime = getCookie("alertTime")
+document.getElementById("alerttimetextbox").value = alertTime
+if (alertTime == "") {
+ alertTime = 2;
+ document.cookie = "alertTime="+alertTime+"; path=/;"
+}
\ No newline at end of file
diff --git a/Client/styles.css b/Client/styles.css
new file mode 100644
index 0000000..71db5a7
--- /dev/null
+++ b/Client/styles.css
@@ -0,0 +1,170 @@
+/* testing */
+
+/* Things that are always visible */
+
+body {
+ background-color: #EEEEEE;
+}
+* {
+ font-family: 'arial';
+}
+.italic {
+ font-style: italic;
+}
+h4 {
+ font-weight: 100;
+}
+.clear{
+ clear: both;
+ display: block;
+ content: "";
+ width: 100%;
+ }
+.controls {
+ max-width: 550px;
+ min-width: 300px;
+ position: fixed;
+ width:100%;
+ left: 50%;
+ bottom: 0;
+ margin: 0 auto;
+ background-color:inherit;
+}
+
+.alert {
+ position: fixed;
+ bottom: 10%;
+ width: 100%;
+ text-align: center;
+ z-index: 1000;
+ background-color: #EEEEEEd6;
+}
+
+.settings-button {
+ width: 15%;
+ max-width: 90px;
+ position:fixed;
+ top:0;
+ right:0;
+ margin: 3px;
+ background:inherit;
+ /* This is a circle background for the circle settings button
+ So it can display over other text and such */
+ border-radius: 50%;
+}
+
+.control-button{
+ width:20%;
+ max-width: 110px;
+ margin: auto 2%;
+}
+
+.intro {
+ width: 300px;
+ margin: auto;
+ text-align: center;
+}
+
+/* Songlist stuff */
+.songlist {
+ width: 60%;
+ min-width: 300px;
+ margin:auto auto 150px;
+ display: flex;
+ flex-wrap: wrap;
+}
+
+.songlist > .item{
+ border: 1px solid #333333;
+ width:30%;
+ max-width: 200px;
+ margin: 5px auto;
+ min-width: 100px;
+ background-color: inherit;
+}
+
+.songlist > .item > img{
+ max-width:200px;
+ width:100%
+}
+
+.songlist > .item > h3, .songlist > .item > h4{
+ margin-left: 2px;
+ margin-right: 2px;
+}
+
+.searchbox-holder {
+ width: 20%;
+ margin: 20px auto 0;
+ min-width: 250px;
+
+}
+.searchbox {
+ width: 65%;
+ margin: 1px;
+}
+.go-search {
+ width: 20%;
+ min-width: 50px;
+}
+/* playlist mode stuff */
+
+.playlist {
+ width: 60%;
+ min-width: 300px;
+ margin:auto auto 150px;
+}
+
+.playlist > .item{
+ border: 1px solid #333333;
+ display: flex;
+ max-width: 50em;
+ min-width: 200px;
+ margin: 5px auto;
+ height: auto;
+}
+
+.playlist > .item > .text {
+ display: inline-block;
+ margin: 0px 3px;
+}
+
+.playlist > .item > img {
+ display: inline-flex;
+ max-width: 100px;
+ width:30%;
+ margin: 0;
+ aspect-ratio: 1/1;
+}
+
+.playlist > .item > .text > * {
+ margin:5% 2px;
+}
+
+/* settings stuff */
+
+.settings {
+ width: 95%;
+ margin: auto auto 150px;
+ max-width: 600px;
+}
+
+.settings > .item {
+ margin-left: 10%;
+ width:fit-content;
+
+}
+
+.settings > .item:not(:last-child) {
+ padding-bottom: 10px;
+ border-bottom: 1px solid #333333;
+}
+
+.settings > .lastSet1 {
+ border-bottom: 0;
+}
+
+#volumerange {
+ background-color: #4477AA;
+ color: #4477ff;
+}
\ No newline at end of file
diff --git a/Server/databaseGenerator.py b/Server/databaseGenerator.py
new file mode 100644
index 0000000..fac4221
--- /dev/null
+++ b/Server/databaseGenerator.py
@@ -0,0 +1,78 @@
+import os
+from mutagen.easyid3 import EasyID3
+from mutagen.mp3 import MP3
+import requests, ast, time, math, argparse, json
+
+loading = ["-","\\","|","/"]
+
+apikeylastfm="YourLastfmKeyHere"
+
+songFiles = os.listdir(r'./sound')
+parser=argparse.ArgumentParser(description="Options for the generation of the song database")
+parser.add_argument('-m', '--mode', help='new/update: Remake database or update current', default= "update")
+parser.add_argument('-a', '--art', help="True/False: Add art to the database using LastFm (takes minimum 0.25s per song)", default="True")
+args = parser.parse_args()
+if args.mode == "update":
+ try:
+ with open('songDatabase.json', 'r') as handle:
+ songDatabaseList = json.load(handle)
+ except:
+ songDatabaseList=[]
+
+ for i in songDatabaseList:
+ try:
+ songFiles.index(i["file"]) != -1
+ except:
+ print("deleted: " + i["file"] + " from database")
+ songDatabaseList.pop(songDatabaseList.index(i))
+
+ for i in songDatabaseList:
+ songFiles.pop(songFiles.index(i["file"]))
+ print("new songs: " + str(songFiles))
+elif args.mode=="new":
+ songDatabaseList = []
+
+if args.art.lower() == "true":
+ x = len(songFiles)*0.25
+ if x > 60:
+ print("ETA "+ str(x/60) + " minutes")
+ else:
+ print("ETA "+ str(x) + " seconds")
+
+for i in songFiles:
+ try:
+ song = EasyID3("sound/"+i)
+ title = song['title'][0]
+ artist = song['artist'][0]
+ except:
+ try:
+ song = i.split("_")
+ title = song[0]
+ artist = song[1].split(".")[0]
+ except:
+ title = i
+ artist = None
+ if args.art.lower() == "true":
+ try:
+ image = ast.literal_eval(requests.post(url="http://ws.audioscrobbler.com/2.0/?method=track.getInfo&api_key="+apikeylastfm+"&artist="+artist+"&track="+title+"&format=json").text)["track"]["album"]["image"][1]["#text"]
+ if image == "":
+ image = ast.literal_eval(requests.post(url="http://ws.audioscrobbler.com/2.0/?method=track.getInfo&api_key="+apikeylastfm+"&artist="+artist+"&track="+title+"&format=json").text)["track"]["album"]["image"][2]["#text"]
+ if image == "":
+ image = None
+ time.sleep(0.25)
+ except:
+ image=None
+ else:
+ image=None
+ try:
+ length = math.ceil(MP3("sound/"+i).info.length)
+ except:
+ length = 0
+ if len(songFiles) != 1:
+ index = (songFiles.index(i))%4
+ print("\r" + str(loading[index] + str(math.floor((songFiles.index(i)/(len(songFiles)-1))*100))+ "%"), end='', flush=True)
+ # each "song" is stored as a dictionary containing the below stuff, and each dictionary is put into a list
+ songDatabaseList.append({"file":i,"title":title,"artist":artist,"art":image,"length":length})
+
+with open('songDatabase.json', 'w') as handle:
+ json.dump(songDatabaseList, handle)
diff --git a/Server/webbyBits.py b/Server/webbyBits.py
new file mode 100644
index 0000000..c14db2b
--- /dev/null
+++ b/Server/webbyBits.py
@@ -0,0 +1,128 @@
+from flask import Flask
+from flask import request
+from flask_cors import CORS
+import json,vlc,csv,threading,time,random
+random.seed()
+global partyMode
+global skipNow
+global songNext
+partyMode = False
+songNext = None
+skipNow = False
+playlist = []
+playlistLock = threading.Lock()
+fakeplayer = vlc.Instance()
+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__)
+CORS(app)
+with open('songDatabase.json', 'r') as handle:
+ songDatabaseList = json.load(handle)
+
+def queueSong(song):
+ with playlistLock:
+ playlist.append(song)
+# this is a loop that plays the songs and checks for playlist changes, skips, ect.
+def playQueuedSongs():
+ global skipNow
+ global songNext
+ global partyMode
+ while True:
+ with playlistLock:
+ z = str(player.get_state())
+
+ if playlist and (z == "State.Ended" or z== "State.Stopped" or z == "State.NothingSpecial" or skipNow == True):
+ player.stop()
+ skipNow = False
+ songNext = playlist.pop(0)
+ media = fakeplayer.media_new("sound/"+songNext)
+ player.set_media(media)
+ player.play()
+ elif (len(playlist) == 0) and skipNow==True:
+ skipNow=False
+ songNext = None
+ player.stop()
+ elif (len(playlist) == 0) and (z == "State.Ended" or z == "State.NothingSpecial" or z=="State.Stopped"):
+ songNext = None
+ elif (len(playlist)<1) and (partyMode == True):
+ playlist.append(random.choice(songDatabaseList)["file"])
+ time.sleep(1)
+
+queueThread = threading.Thread(target=playQueuedSongs)
+queueThread.daemon = True
+queueThread.start()
+
+@app.route("/controls", methods=['POST'])
+def playerControls():
+ global skipNow
+ global media
+ global partyMode
+ recieveData=request.get_json(force=True)
+ if recieveData["control"] != None:
+ if recieveData["control"] == "play-pause":
+ player.pause()
+ return "200"
+ elif recieveData["control"] == "skip":
+ skipNow = True
+ # print(str(player.get_state()))
+ return "200"
+ else:
+ return "400"
+
+@app.route("/settings", methods=['POST'])
+def settingsControl():
+ global partyMode
+ recieveData = request.get_json(force=True)
+ if recieveData["setting"] == "volume":
+ player.audio_set_volume(int(recieveData["level"]))
+ return "200"
+ elif recieveData["setting"] == "getsettings":
+ x = {"partymode":partyMode,"volume":player.audio_get_volume()}
+ return x
+ elif recieveData["setting"] == "partymode-toggle":
+ partyMode = not(partyMode)
+ return "200"
+ else:
+ return "400"
+@app.route("/search", methods=['POST'])
+def searchSongDB():
+ recieveData=request.get_json(force=True)
+ # the way i put the data in a list was really dumb looking back, i could and should have used a list of dictioaries like i was before
+ # i might try to change it but this layout is embedded deep in the client
+ tempData = {}
+ for i in songDatabaseList:
+ if ((i["title"].lower().find(recieveData['search'].lower())) > -1) or (recieveData['search'] == ""):
+ tempData[i["title"]] = [i["artist"],i["art"],i["file"]]
+ try:
+ if (i["artist"].lower().find(recieveData['search'].lower()) > -1):
+ tempData[i["title"]] = [i["artist"],i["art"],i["file"]]
+ except:
+ pass
+
+ return tempData
+
+@app.route("/songadd", methods=["POST"])
+def songadd():
+ recieveData=request.get_json(force=True)
+ queueSong(recieveData['song'])
+ return "200"
+@app.route("/playlist", methods=["POST"])
+def getPlaylist():
+ global songNext
+ tempPlaylist = []
+ for k in songDatabaseList:
+ if k["file"] == songNext:
+ temp = k.copy()
+ temp["playing"] = True
+ temp["time"] = player.get_time()/1000
+ tempPlaylist.append(temp)
+ for i in playlist:
+ for j in songDatabaseList:
+ if j["file"] == i:
+ tempPlaylist.append(j)
+ return tempPlaylist
+
+if __name__ == "__main__":
+ app.run(host='0.0.0.0', port='25565')
+
\ No newline at end of file