feat: Add a ton of features to make it better
All checks were successful
Build Docker / BuildImage (push) Successful in 2m15s

This commit is contained in:
2025-08-19 18:01:42 +10:00
parent 54839cf185
commit 7d0b9df50c
6 changed files with 201 additions and 22 deletions

1
current_track.json Normal file
View File

@@ -0,0 +1 @@
{"name": "girls will b girls", "cover": "https://i.scdn.co/image/ab67616d0000b273ea3a18c31fd757d953c7836a"}

3
server.log Normal file
View File

@@ -0,0 +1,3 @@
Changing Song
Song Name: girls will b girls
Playing Song

View File

@@ -18,9 +18,24 @@ import dotenv
dotenv.load_dotenv()
SPEAKER_NAME = os.getenv("SPEAKER_NAME", "Family Room Speakers")
app = Flask(__name__)
def log(message: str):
"""
Log a message to the server log file.
"""
log_path = "server.log"
if not os.path.exists(log_path):
with open(log_path, "w") as f:
f.write("")
with open(log_path, "a") as f:
f.write(f"{datetime.now().isoformat()} - {message}\n")
print(f"{datetime.now().isoformat()} - {message}") # Also print to console for debugging
def find(name, path):
for root, dirs, files in os.walk(path):
if name in files:
@@ -85,11 +100,21 @@ def restart():
"""
# Execute a `pkill spotifyd` command to stop the spotifyd process
status = os.system("pkill spotifyd")
hookPath = os.path.join(os.getcwd(), "song-hook.sh")
# Clear the server log
log_path = "server.log"
if os.path.exists(log_path):
with open(log_path, "w") as f:
f.write("")
# Start with a new process
os.system("spotifyd -d 'Family Room Speakers'")
output = os.system(f"spotifyd -d '{SPEAKER_NAME}' --onevent {hookPath}")
if output != 0:
log("Failed to restart spotifyd")
else:
log("spotifyd restarted successfully")
return redirect('/')
@@ -141,6 +166,35 @@ def api_data():
return jsonify(data)
@app.route("/api/v1/logs", methods=["GET"])
def api_logs():
"""
Returns the last 100 lines of the server log.
"""
log_path = "server.log"
# Check if the log file exists
if not os.path.isfile(log_path):
return jsonify({"logs": "Server not running"}), 404
lines = []
try:
with open(log_path, "r") as f:
lines = f.readlines()[-100:]
except Exception as e:
lines = [f"Error reading log file: {e}"]
return jsonify({"logs": "".join(lines)})
@app.route("/api/v1/current_track", methods=["GET"])
def api_current_track():
# If the current_track.json file exists, read it and return its contents
if os.path.isfile("current_track.json"):
with open("current_track.json", "r") as f:
track_data = json.load(f)
return jsonify(track_data)
else:
return jsonify({"error": "No current track data available"}), 404
# endregion

44
song-hook.py Executable file
View File

@@ -0,0 +1,44 @@
#!/usr/bin/env python3
import os
from datetime import datetime
# "PLAYER_EVENT": "Player Event",
# "TRACK_ID": "Track ID",
# "TRACK_COVER": "Track Cover",
# "endoftrack": "End of Track",
ENVLOGS = {
"CLIENT_NAME": "Device Connected",
"VOLUME": "Volume Level",
"AUTOPLAY": "Autoplay Status",
"SHUFFLE": "Shuffle Status",
"REPEAT": "Repeat Status",
"TRACK_NAME": "Song Name",
}
PLAYER_EVENTS = {
"start": "Playing Song",
"change": "Changing Song",
}
LOG_FILE = "/home/nathan/Git/spotifyd-webui/server.log"
def main():
with open(LOG_FILE, "a") as f:
# Get PLAYER_EVENT from environment variables
player_event = os.getenv("PLAYER_EVENT", "Unknown Event")
player_event = PLAYER_EVENTS.get(player_event, None)
if player_event:
f.write(player_event + "\n")
for key, value in os.environ.items():
# Only log specific environment variables using format {ENVLOGS[key]}: value
if key in ENVLOGS:
f.write(f"{ENVLOGS[key]}: {value}\n")
# Save current name and cover
track_name = os.getenv("TRACK_NAME", None)
track_cover = os.getenv("TRACK_COVER", None)
if track_name and track_cover:
with open("current_track.json", "w") as f:
f.write(f'{{"name": "{track_name}", "cover": "{track_cover}"}}\n')
if __name__ == "__main__":
main()

View File

@@ -8,15 +8,12 @@ h1 {
padding: 0;
}
.centre {
margin-top: 10%;
margin-top: 6%;
text-align: center;
}
a {
color: #ffffff;
text-decoration: none;
}
a:hover {
text-decoration: underline;
.spacer {
height: 40px;
}
/* Mike section styling */
@@ -26,32 +23,78 @@ a:hover {
margin-left: auto;
margin-right: auto;
padding: 20px;
background-color: rgba(50, 50, 50, 0.3);
border-radius: 8px;
background-color: rgba(50, 50, 50, 0.5);
border-radius: 12px;
box-shadow: 0 2px 12px rgba(0,0,0,0.25);
text-align: center;
}
.mike-section h2 {
color: #f0f0f0;
margin-top: 0;
font-size: 1.5em;
}
.mike-section p {
line-height: 1.6;
margin-bottom: 15px;
font-size: 1.1em;
}
.spotify-logo {
width: 90%;
object-fit: cover;
border-radius: 12px;
box-shadow: 0 2px 12px rgba(0,0,0,0.18);
background: #222;
border: 2px solid #444;
}
#current-track-section {
margin-top: 20px;
}
#current-track {
font-size: 1.2em;
color: #fff;
margin-top: 8px;
margin-bottom: 0;
font-weight: 500;
}
#logs-section {
margin-top: 40px;
}
#server-logs {
font-family: monospace;
font-size: 14px;
background: #222;
color: #eee;
padding: 10px;
border-radius: 5px;
max-height: 400px;
overflow: auto;
text-align: left;
box-shadow: 0 1px 6px rgba(0,0,0,0.12);
}
/* Button improvements */
.button {
display: inline-block;
padding: 10px 20px;
background-color: #ffffff;
color: #000000;
padding: 12px 28px;
background-color: #1db954;
color: #fff;
border-radius: 5px;
text-decoration: none;
transition: background-color 0.3s ease;
border: 1px solid #ffffff;
transition: background 0.2s, color 0.2s;
border: none;
font-size: 1.1em;
font-weight: 600;
box-shadow: 0 1px 6px rgba(0,0,0,0.10);
margin-top: 18px;
}
.button:hover {
background-color: #000000;
color: #ffffff;
border: 1px solid #ffffff;
background-color: #14833b;
color: #fff;
}

View File

@@ -18,6 +18,40 @@
<div class="spacer"></div>
<!-- Current track -->
<div class="mike-section" id="current-track-section">
<img src="/assets/img/favicon.png" alt="Song Logo" class="spotify-logo" id="song-logo">
<p id="current-track">Loading...</p>
</div>
<div class="mike-section" id="logs-section">
<h2>Server Logs</h2>
<pre id="server-logs"
style="background:#222;color:#eee;padding:10px;border-radius:5px;max-height:400px;overflow:auto;"></pre>
</div>
<script>
async function fetchLogs() {
const res = await fetch('/api/v1/logs');
const data = await res.json();
document.getElementById('server-logs').textContent = data.logs;
}
fetchLogs();
setInterval(fetchLogs, 5000); // Refresh logs every 5 seconds
async function fetchCurrentTrack() {
const res = await fetch('/api/v1/current_track');
if (res.ok) {
const data = await res.json();
document.getElementById('current-track').textContent = data.name;
document.getElementById('song-logo').src = data.cover || '/assets/img/favicon.png';
} else {
document.getElementById('current-track').textContent = 'No current track data available';
}
}
fetchCurrentTrack();
setInterval(fetchCurrentTrack, 5000); // Refresh current track every 5 seconds
</script>
</body>
</html>