s
This commit is contained in:
parent
e26d298c4c
commit
aa7d40f7a0
6 changed files with 122 additions and 100 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
|
||||||
|
.env
|
||||||
|
.cache
|
16
app.py
16
app.py
|
@ -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
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
Flask
|
Flask
|
||||||
spotipy
|
spotipy
|
||||||
|
python-dotenv
|
2
run.bat
2
run.bat
|
@ -1 +1 @@
|
||||||
python app.py
|
cmd /K python -m app
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Add table
Reference in a new issue