Sockets are implemented
need a lot of fixes but are in a mostly working state as of now
This commit is contained in:
parent
384b369eee
commit
f064183b9a
4 changed files with 111 additions and 32 deletions
|
|
@ -6,6 +6,7 @@
|
|||
<link rel="stylesheet" href="styles.css">
|
||||
<link rel="manifest" href="manifest.json" />
|
||||
<meta charset="utf-8">
|
||||
<script src="https://cdn.socket.io/4.7.5/socket.io.min.js"></script>
|
||||
</head>
|
||||
<body id="test-body">
|
||||
<!--Cookie Popup(does it matter if im not tracking them? i have no idea)-->
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ const VALID_FILE_EXT = ["mp3","flac","wav"];
|
|||
let playlistTimeTimer=null;
|
||||
let playlistElapsedSeconds=0;
|
||||
let playlistSongLength=-1;
|
||||
let currentlyPlaying = false;
|
||||
|
||||
const params = new URLSearchParams(location.search);
|
||||
|
||||
|
|
@ -101,10 +102,12 @@ function getCookie(cname) {
|
|||
//someone more organised than me would have set all these html elements to variables so they dont have to get them 50 times
|
||||
// also someone who likes things not being dumb more than me would have separated the client and server buttons
|
||||
async function controlButton(buttonType) {
|
||||
clearInterval(playlistTimeTimer);
|
||||
if (buttonType == "pp") { // Play-Pause button
|
||||
getFromServer({control: "play-pause"}, "controls")
|
||||
let result = await getFromServer({control: "play-pause"}, "controls");
|
||||
console.log(result);
|
||||
currentlyPlaying = result["data"]["playingState"];
|
||||
} else if (buttonType == "sk") { // Skip button
|
||||
clearInterval(playlistTimeTimer);
|
||||
let returnCode = await getFromServer({control: "skip"}, "controls");
|
||||
console.log(returnCode["ok"])
|
||||
if(returnCode["ok"]) {
|
||||
|
|
@ -113,6 +116,7 @@ async function controlButton(buttonType) {
|
|||
}
|
||||
}
|
||||
} else if (buttonType == "pl") { // Playlist button
|
||||
clearInterval(playlistTimeTimer);
|
||||
document.getElementById("songlist").innerHTML = "";
|
||||
document.getElementById("playlist").innerHTML = "<h1 id=\"playlist-alert\"></h1>";
|
||||
document.getElementById("playlist-mode").style.display = "block";
|
||||
|
|
@ -120,12 +124,14 @@ async function controlButton(buttonType) {
|
|||
document.getElementById("settings-mode").style.display = "none";
|
||||
generateVisualPlaylist();
|
||||
} else if (buttonType == "se") { //SearchMode button
|
||||
clearInterval(playlistTimeTimer);
|
||||
document.getElementById("songlist").innerHTML = "<h1>Search to find songs!</h1>";
|
||||
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") { //Settings button
|
||||
clearInterval(playlistTimeTimer);
|
||||
document.getElementById("songlist").innerHTML = "";
|
||||
document.getElementById("playlist").innerHTML = "";
|
||||
document.getElementById("playlist-mode").style.display = "none";
|
||||
|
|
@ -251,19 +257,28 @@ function qrCodeGenerate() {
|
|||
});
|
||||
}
|
||||
|
||||
async function displayElapsedPlaylistTime(elapsed=0,length=0) {
|
||||
if(Math.floor(elapsed) === Math.floor(length)){
|
||||
console.log("somethingShouldBeHappening")
|
||||
playlistElapsedSeconds = 0;
|
||||
generateVisualPlaylist();
|
||||
async function displayElapsedPlaylistTime(elapsed=0,length=-1) {
|
||||
if(currentlyPlaying) {
|
||||
if(Math.floor(elapsed) > Math.floor(length) && typeof length === "number" && typeof elapsed === "number"){
|
||||
// console.log("somethingShouldBeHappening")
|
||||
playlistElapsedSeconds = 0;
|
||||
generateVisualPlaylist();
|
||||
}
|
||||
let mins = Math.floor(elapsed/60);
|
||||
let secs = Math.floor(elapsed%60);
|
||||
let durMins = Math.floor(length/60);
|
||||
let durSecs = Math.floor(length%60);
|
||||
let timeLeft = document.getElementById("elapsed-time-display");
|
||||
if(mins > durMins) {
|
||||
mins = durMins;
|
||||
if(secs > durSecs) {
|
||||
secs = durSecs;
|
||||
}
|
||||
}
|
||||
|
||||
timeLeft.innerHTML = mins.toString() +":"+ secs.toLocaleString('en-US', {minimumIntegerDigits: 2,useGrouping: false}) + "/"+ durMins.toString()+":"+durSecs.toLocaleString('en-US', {minimumIntegerDigits: 2,useGrouping: false});
|
||||
// playlistElapsedSeconds++;
|
||||
}
|
||||
let mins = Math.floor(elapsed/60);
|
||||
let secs = Math.floor(elapsed%60);
|
||||
let durMins = Math.floor(length/60);
|
||||
let durSecs = Math.floor(length%60);
|
||||
let timeLeft = document.getElementById("elapsed-time-display");
|
||||
timeLeft.innerHTML = mins.toString() +":"+ secs.toLocaleString('en-US', {minimumIntegerDigits: 2,useGrouping: false}) + "/"+ durMins.toString()+":"+durSecs.toLocaleString('en-US', {minimumIntegerDigits: 2,useGrouping: false});
|
||||
playlistElapsedSeconds++;
|
||||
}
|
||||
|
||||
async function checkSettings(skipServer=false) {
|
||||
|
|
@ -313,7 +328,8 @@ async function checkSettings(skipServer=false) {
|
|||
async function generateVisualPlaylist(conditions="") {
|
||||
document.getElementById("playlist").innerHTML = "<h1 id=\"playlist-alert\"></h1>";
|
||||
data = await getFromServer(null, "playlist");
|
||||
playlist = data["data"];
|
||||
playlist = data["data"]["playlist"];
|
||||
currentlyPlaying = data["data"]["playingState"]
|
||||
playlist = Object.values(playlist).map(obj => {
|
||||
const filename = Object.keys(obj)[0]; // Get the filename
|
||||
const songData = obj[filename]; // Get the song metadata
|
||||
|
|
@ -323,7 +339,7 @@ async function generateVisualPlaylist(conditions="") {
|
|||
clearInterval(playlistTimeTimer);
|
||||
document.getElementById("playlist-alert").innerHTML = "Nothing's Queued..."
|
||||
} else {
|
||||
if (conditions=="skip-button") {
|
||||
if (conditions==="skip-button") {
|
||||
playlist.shift()
|
||||
if (playlist.length==0){
|
||||
document.getElementById("playlist-alert").innerHTML = "Nothing's Queued..."
|
||||
|
|
@ -375,9 +391,6 @@ async function generateVisualPlaylist(conditions="") {
|
|||
playlistSongLength = playlist[0]["length"];
|
||||
displayElapsedPlaylistTime(playlistElapsedSeconds,playlistSongLength);
|
||||
clearInterval(playlistTimeTimer);
|
||||
playlistTimeTimer = setInterval(() => {
|
||||
displayElapsedPlaylistTime(playlistElapsedSeconds,playlistSongLength);
|
||||
},1000)
|
||||
}
|
||||
}
|
||||
}catch(err){
|
||||
|
|
@ -386,6 +399,9 @@ async function generateVisualPlaylist(conditions="") {
|
|||
}
|
||||
}
|
||||
}
|
||||
playlistTimeTimer = setInterval(() => {
|
||||
displayElapsedPlaylistTime(playlistElapsedSeconds,playlistSongLength);
|
||||
},1000)
|
||||
}
|
||||
|
||||
async function submitSong(songid) {
|
||||
|
|
@ -566,3 +582,25 @@ if (alertTime == "") {
|
|||
}
|
||||
// this is the code that makes the qr code at the very start
|
||||
qrCodeGenerate()
|
||||
|
||||
// socket testing stuff
|
||||
|
||||
socket = io("http://"+ip,{
|
||||
reconnectionAttemps: 5,
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
socket.on("songAdd", function(data) {
|
||||
console.log("recieved data from songAdd");
|
||||
console.log(data);
|
||||
generateVisualPlaylist();
|
||||
})
|
||||
|
||||
socket.on("timeUpdate", function(data) {
|
||||
console.log("recieved data from timeUpdate");
|
||||
console.log(data);
|
||||
playlistElapsedSeconds = data["elapsedTime"];
|
||||
playingState = data["playingState"]
|
||||
});
|
||||
|
||||
socket.on("skipSong",generateVisualPlaylist)
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
from flask import Flask
|
||||
from flask import request
|
||||
from flask_cors import CORS
|
||||
from flask_socketio import SocketIO
|
||||
import sqlite3 as sql
|
||||
import vlc,threading,time,random,argparse,dotenv,os,hashlib,string
|
||||
# Argparse Stuff
|
||||
|
|
@ -64,20 +65,46 @@ player.audio_set_volume(100)
|
|||
app = Flask(__name__)
|
||||
# because you are POSTing from another domain to this one, you need CORS
|
||||
CORS(app)
|
||||
# Replace the star with the frontend domain if you dislike being hacked
|
||||
socketio = SocketIO(app, cors_allowed_origins="*")
|
||||
|
||||
def queueSong(song):
|
||||
with playlistLock:
|
||||
playlist.append(song)
|
||||
socketio.emit("songAdd",getSongInfo(song))
|
||||
|
||||
def getSongInfo(song):
|
||||
fileofDB = sql.connect("songDatabase.db")
|
||||
songDatabase = fileofDB.cursor()
|
||||
songDatabase.execute("SELECT * FROM songs WHERE filename = ?",[song])
|
||||
result = songDatabase.fetchall()[0]
|
||||
# again, this is still using the old JSON format to avoid client changes
|
||||
k = {
|
||||
"title": result[1],
|
||||
"artist": result[2],
|
||||
"art": result[3],
|
||||
"length": result[4]
|
||||
}
|
||||
fileofDB.close()
|
||||
return {song:k}
|
||||
|
||||
# this is a loop that plays the songs and checks for playlist changes, skips, ect.
|
||||
counter = 0
|
||||
def playQueuedSongs():
|
||||
global skipNow
|
||||
global songNext
|
||||
global partyMode
|
||||
global counter
|
||||
while True:
|
||||
with playlistLock:
|
||||
counter+=1
|
||||
if(counter > 10):
|
||||
playingState = str(player.get_state()) == "State.Playing"
|
||||
socketio.emit('timeUpdate',{"elapsedTime":player.get_time()/1000,"playingState":playingState})
|
||||
playerState = str(player.get_state())
|
||||
endStates = ["State.Ended","State.Stopped","State.NothingSpecial"]
|
||||
if playerState == "State.Ended":
|
||||
socketio.emit("skipSong",None)
|
||||
if playlist and (playerState in endStates or skipNow == True):
|
||||
# New song is in the queue and (the previous song is over or skip has been pressed)
|
||||
player.stop()
|
||||
|
|
@ -87,6 +114,7 @@ def playQueuedSongs():
|
|||
player.set_media(media)
|
||||
player.play()
|
||||
elif (skipNow==True or (playerState in endStates)):
|
||||
# print(playerState)
|
||||
# skip was pressed and there are no new songs
|
||||
skipNow=False
|
||||
songNext = None
|
||||
|
|
@ -99,11 +127,16 @@ def playQueuedSongs():
|
|||
# adds the random songs for party mode
|
||||
# the above 2 means this only applies if (a song is playing or paused) and (the queue is empty)
|
||||
playlist.append(result[0][0])
|
||||
socketio.emit('songAdd',getSongInfo(result[0][0]))
|
||||
# check for new songs every second
|
||||
# I just didn't want to eat too much processing looping
|
||||
# this also has another useful affect that skips get "queued" to only 1 per second, that way somebody usually can't skip twice accidentally
|
||||
time.sleep(1)
|
||||
|
||||
@socketio.on("connect")
|
||||
def handleConnect():
|
||||
pass
|
||||
|
||||
@app.route("/controls", methods=['POST'])
|
||||
def playerControls():
|
||||
# recieve control inputs (play/pause and skip) from the webUI
|
||||
|
|
@ -113,10 +146,12 @@ def playerControls():
|
|||
try:
|
||||
if recieveData["control"] == "play-pause":
|
||||
if ADMIN_PASS == recieveData['password'] or controlPerms["PP"]:
|
||||
playingState = str(player.get_state())=="State.Playing"
|
||||
player.pause()
|
||||
return ERR_200
|
||||
return {"error":"ok","data":{"playingState":not(playingState)}},200
|
||||
else:
|
||||
return ERR_NO_ADMIN
|
||||
playingState = str(player.get_state())=="State.Playing"
|
||||
return {"error":"Admin Restricted Action","data":{"playingState":playingState}},401
|
||||
elif recieveData["control"] == "skip":
|
||||
if ADMIN_PASS == recieveData['password'] or controlPerms["SK"]:
|
||||
skipNow = True
|
||||
|
|
@ -163,7 +198,7 @@ def settingsControl():
|
|||
elif recieveData["setting"] == "perms":
|
||||
if ADMIN_PASS == recieveData["password"]:
|
||||
controlPerms = recieveData["admin"]
|
||||
print(recieveData["admin"])
|
||||
# print(recieveData["admin"])
|
||||
return ERR_200
|
||||
else:
|
||||
return ERR_NO_ADMIN
|
||||
|
|
@ -261,8 +296,11 @@ def getPlaylist():
|
|||
}
|
||||
tempPlaylist.append({i:k})
|
||||
fileofDB.close()
|
||||
|
||||
return {"error":"ok","data":tempPlaylist}
|
||||
playingState = False
|
||||
if(str(player.get_state())=="State.Playing"):
|
||||
playingState = True
|
||||
# print(playingState)
|
||||
return {"error":"ok","data":{"playlist":tempPlaylist,"playingState":playingState}},200
|
||||
|
||||
if __name__ == "__main__":
|
||||
# There's not really a whole lot of point to a main function for something like this, you'd never use any of these methods
|
||||
|
|
@ -271,5 +309,5 @@ if __name__ == "__main__":
|
|||
queueThread = threading.Thread(target=playQueuedSongs)
|
||||
queueThread.daemon = True
|
||||
queueThread.start()
|
||||
app.run(host='0.0.0.0', port=portTheUserPicked)
|
||||
socketio.run(app=app,host='0.0.0.0', port=portTheUserPicked)
|
||||
|
||||
14
wishlist.md
14
wishlist.md
|
|
@ -1,7 +1,5 @@
|
|||
## Wishlist
|
||||
*Features I would like to add, will be completed in any order*
|
||||
- [x] Admin password
|
||||
* Allows restricting certain features and changing permissions on the fly on the client
|
||||
- [ ] Refactoring existing code
|
||||
- [x] Remove old comments
|
||||
- [ ] Update the SQL -> Server -> Client pipeline when searching and building playlist
|
||||
|
|
@ -24,7 +22,11 @@
|
|||
- Potentially a "redemption code" system, which can be tracked client side
|
||||
- All of this is very hackable without a server-side login.
|
||||
- [ ] Websockets / some method of updating the time remaining to any client on the playlist screen
|
||||
* currently the screen just grabs the "elapsed time" once when it is loaded
|
||||
* websockets can re-update clients
|
||||
* not actually sure if i can CORS-socket but we're sure gonna try
|
||||
- [ ] Set a timeout to change the time
|
||||
* This is implemented in a very broken way right now
|
||||
- [x] Set a timeout to change the time (to start)
|
||||
- [x] Send updates to the playlist in real time when songs are added
|
||||
* This is only kind of done, still needs work
|
||||
- [ ] Update the playlist's html without destroying it (create 1 new element)
|
||||
- [x] Tell clients looking at the playlist when the song has been paused (so they can pause the local timers)
|
||||
* Again, still needs work
|
||||
* This is currently solved by just sending the time and "playing status" once a second-ish
|
||||
Loading…
Add table
Add a link
Reference in a new issue