This commit is contained in:
Eduard Prigoana 2025-01-18 20:20:55 +02:00
parent e26d298c4c
commit aa7d40f7a0
6 changed files with 122 additions and 100 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
.env
.cache

16
app.py
View file

@ -1,13 +1,18 @@
from flask import Flask, render_template, request, redirect, url_for, jsonify from flask import Flask, render_template, request, jsonify
from spotipy import Spotify from spotipy import Spotify
from spotipy.oauth2 import SpotifyOAuth from spotipy.oauth2 import SpotifyOAuth
from dotenv import load_dotenv
import os
# Load environment variables from .env file
load_dotenv()
# Flask app # Flask app
app = Flask(__name__) app = Flask(__name__)
# Spotify API credentials # Spotify API credentials from environment variables
CLIENT_ID = "f7f2841e0c26492681499a53b4eca29f" CLIENT_ID = os.getenv("CLIENT_ID")
CLIENT_SECRET = "8e1a973438214682939b175bb9f1ed1d" CLIENT_SECRET = os.getenv("CLIENT_SECRET")
REDIRECT_URI = "http://localhost:80/callback" REDIRECT_URI = "http://localhost:80/callback"
SCOPE = "user-modify-playback-state user-read-playback-state" SCOPE = "user-modify-playback-state user-read-playback-state"
@ -23,6 +28,7 @@ sp = Spotify(auth_manager=SpotifyOAuth(
@app.route("/") @app.route("/")
def home(): def home():
return render_template("index.html") return render_template("index.html")
@app.route("/add", methods=["GET"]) @app.route("/add", methods=["GET"])
def add_song(): def add_song():
@ -58,7 +64,7 @@ def queue_table():
queue.append({"track": track_name, "artist": artist_name}) queue.append({"track": track_name, "artist": artist_name})
current = None current = None
if queue_data["currently_playing"]: if queue_data["currently_playing"]:
current = {"track":queue_data["currently_playing"]["name"], "artist":queue_data["currently_playing"]["artists"][0]["name"]} current = {"track": queue_data["currently_playing"]["name"], "artist": queue_data["currently_playing"]["artists"][0]["name"]}
return render_template("queue_table.html", queue=queue, length=len(queue), now_playing=current) return render_template("queue_table.html", queue=queue, length=len(queue), now_playing=current)
# Run the Flask app # Run the Flask app

View file

@ -1,2 +1,3 @@
Flask Flask
spotipy spotipy
python-dotenv

View file

@ -1 +1 @@
python app.py cmd /K python -m app

View file

@ -1,6 +1,10 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css"
>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Spotify Queue Manager</title> <title>Spotify Queue Manager</title>
@ -10,13 +14,9 @@
text-align: center; text-align: center;
margin: 0; margin: 0;
padding: 20px; padding: 20px;
background-color: #f0f0f0;
}
h1 {
color: #000000;
} }
form { form {
margin: 20px auto; width: 100%;
} }
input[type="text"] { input[type="text"] {
width: 80%; width: 80%;
@ -25,34 +25,49 @@
border: 1px solid #ccc; border: 1px solid #ccc;
border-radius: 5px; border-radius: 5px;
} }
button { .loading-spinner {
background-color: #000000; display: none;
color: white; text-align: center;
border: none; padding: 10px;
padding: 10px 20px; font-size: 16px;
border-radius: 5px; color: #555;
cursor: pointer;
} }
button:hover { .loading-spinner.active {
background-color: #000000; display: block;
} }
/* Styling for the iframe to stretch to bottom and hide scrollbar */
iframe { iframe {
width: 100%; width: 100%;
height: calc(100vh); /* Adjusted to leave space for other content */
border: none; border: none;
margin-top: 20px; overflow: hidden; /* Hide the scrollbar */
overflow: hidden; transition: height 0.3s ease-in-out;
}
/* Optional: Smooth fade on iframe content changes */
iframe.content {
transition: opacity 0.5s ease-in-out;
opacity: 1;
} }
</style> </style>
</head> </head>
<body> <body>
<h1>Queue</h1> <h1>Queue</h1>
<form id="addSongForm" onsubmit="addSong(event)"> <form id="addSongForm" onsubmit="addSong(event)">
<input type="text" id="songInput" name="song_name" placeholder="Adauga melodie sau artist " required> <input type="text" id="songInput" name="song_name" placeholder="Add song or artist" required>
<button type="submit">Adauga</button> <button style="width: 10%;" class="contrast" type="submit">Add</button>
</form> </form>
<div class="message" id="message"></div> <div class="message" id="message"></div>
<!-- Queue Table Embedded in an iframe --> <!-- Now Playing Widget -->
<iframe src="/queue_table" id="queueIframe"></iframe> <p>Now Playing:</p>
<div class="now-playing" id="nowPlaying">
<div id="nowPlayingInfo">
Loading now playing...
</div>
</div>
<iframe id="queueTable" src="/queue_table" scrolling="no" seamless="seamless"></iframe>
<script> <script>
// Function to handle song addition via GET request // Function to handle song addition via GET request
async function addSong(event) { async function addSong(event) {
@ -86,28 +101,56 @@
songInput.value = ""; songInput.value = "";
} }
// Reload the iframe to reflect the updated queue // Show loading spinner
document.getElementById("queueIframe").contentWindow.location.reload(); const loadingSpinner = document.getElementById("loadingSpinner");
loadingSpinner.classList.add("active");
setTimeout(() => {
messageDiv.textContent = "";
}, 3000)
}
// Adjust the iframe height dynamically to avoid scrollbars
function resizeIframe() {
const iframe = document.getElementById('queueIframe');
iframe.style.height = iframe.contentWindow.document.body.scrollHeight+20 + 'px';
} }
// Resize iframe after it loads and periodically refresh the iframe // Function to fetch "Now Playing" information
const iframe = document.getElementById('queueIframe'); async function fetchNowPlaying() {
iframe.onload = resizeIframe; try {
setInterval(() => { const response = await fetch('https://lastplayed.prigoana.com/Labirint84/');
iframe.contentWindow.location.reload(); const data = await response.json();
}, 15000);
const track = data.track;
if (track && track["@attr"] && track["@attr"].nowplaying === "true") {
const nowPlayingInfo = document.getElementById('nowPlayingInfo');
const nowPlayingImage = document.getElementById('nowPlayingImage');
nowPlayingInfo.innerHTML = `${track.name} by ${track.artist["#text"]}`;
nowPlayingImage.innerHTML = `<img src="${track.image[2]["#text"]}" alt="${track.name}">`;
}
} catch (error) {
console.error('Error fetching now playing:', error);
}
}
// Call fetchNowPlaying on page load
fetchNowPlaying();
// Set an interval to fetch now playing every 3 seconds
setInterval(fetchNowPlaying, 3000);
// Function to update iframe content
async function updateIframeContent() {
try {
const iframe = document.getElementById("queueTable");
const response = await fetch('/queue_table');
const html = await response.text();
// Replace iframe content with smooth transition
iframe.srcdoc = html;
// Optionally: Apply smooth fade in for iframe content
iframe.classList.add('content');
} catch (error) {
console.error('Error updating iframe:', error);
}
}
// Update iframe content every 3 seconds
setInterval(updateIframeContent, 3000);
// Add event listener for resize events
window.addEventListener('resize', resizeIframe);
</script> </script>
</body> </body>
</html> </html>

View file

@ -1,63 +1,32 @@
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="en" class="pico">
<head> <head>
<meta charset="UTF-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1">
<style> <meta name="color-scheme" content="light dark" />
body { <link
margin: 0; rel="stylesheet"
padding: 0; href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.fuchsia.min.css"
font-family: Arial, sans-serif; >
} <title>table</title>
h2 { </head>
color: #000000;
margin: 10px 0;
}
.now-playing {
text-align: center;
margin: 20px 0;
font-size: 1.2em;
}
table {
border-collapse: collapse;
margin: 0;
font-size: 1em;
font-family: Arial, sans-serif;
}
th, td {
padding: 12px;
border: 1px solid #ddd;
text-align: left;
}
th {
background-color: #000000;
color: white;
}
body {
margin: 0;
padding: 0;
}
</style>
</head>
<body> <body>
<center> <center> <main class="container">
<table> <table class="striped" style="table-layout: fixed; overflow: hidden;">
<thead> <thead style=" text-align: center;">
<tr> <tr style=" text-align: center;">
<th>#</th> <th scope="row" style=" text-align: center;">Track</th>
<th>Track</th> <th style=" text-align: center;">Artist</th>
<th>Artist</th>
</tr> </tr>
</thead> </thead style=" text-align: center;">
<tbody> <tbody style=" text-align: center;">
{% for i in range(length) %} {% for i in range(length) %}
<tr> <tr style=" text-align: center;">
<td>{{ i+1 }}</td> <td style=" text-align: center;">{{ queue[i].track }}</td>
<td>{{ queue[i].track }}</td> <td style=" text-align: center;">{{ queue[i].artist }}</td>
<td>{{ queue[i].artist }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table></center> </table></center></main>
</body> </body>
</html> </html>