Add the html file, basic socketio structure

This commit is contained in:
Wesley Neuhaus 2024-08-11 02:08:13 -04:00
parent 9bcf3ddb60
commit a1ee1d9a8d
3 changed files with 966 additions and 0 deletions

View File

@ -19,6 +19,10 @@ music_directory = os.path.expanduser('~/Music') # Adjust path as necessary
def index(): def index():
return render_template('manage1.html') return render_template('manage1.html')
@app.route('/admin')
def index():
return render_template('manage1.html')
@app.route('/display') @app.route('/display')
def display(): def display():
return render_template('display1.html') return render_template('display1.html')

72
main3.py Normal file
View File

@ -0,0 +1,72 @@
from flask import Flask, render_template
from flask_socketio import SocketIO, emit
from flask_cors import CORS
import yt_dlp
import vlc
import time
import os
import eyed3
home_directory = os.path.expanduser("~")
#app = Flask(__name__,template_folder=home_directory)
app = Flask(__name__, template_folder=(home_directory + "/Code/GR/GR-Jukebox"))
socketio = SocketIO(app, cors_allowed_origins="*")
CORS(app)
player = vlc.MediaPlayer()
music_queue = []
title_queue = []
music_directory = os.path.expanduser('~/Music')
@app.route('/')
def index():
return render_template('manage1.html')
@app.route('/display')
def display():
return render_template('display1.html')
@socketio.on('list_music')
def list_music():
music_files = []
for filename in os.listdir(music_directory):
if filename.endswith('.mp3'):
path = os.path.join(music_directory, filename)
audiofile = eyed3.load(path)
title = audiofile.tag.title if audiofile.tag else 'Unknown Title'
music_files.append({'id': filename.split('.')[0], 'title': title})
emit('music_list', music_files)
@socketio.on('download')
def download(data):
url = data['youtube_url']
# ... existing download logic ...
emit('download_status', {'status': 'success', 'message': 'Song added successfully'})
@socketio.on('play')
def play(data):
global playing_thread
music_queue.append(data['songId'])
title_queue.append(data['songTitle'])
if not playing_thread:
# ... existing play logic ...
emit('play_status', {'status': 'success', 'message': 'Playing song'})
@socketio.on('get_queue')
def get_queue():
emit('queue', [{'id': idx, 'title': title} for idx, title in enumerate(title_queue)])
@socketio.on('remove_from_queue')
def remove_from_queue(data):
song_id = int(data['songId'])
# ... existing remove logic ...
emit('remove_status', {'status': 'success', 'message': 'Song removed successfully'})
@socketio.on('current')
def current():
if len(title_queue) > 0:
emit('current_song', {'title': title_queue[0]})
else:
emit('current_song', {'title': 'Nothing is playing'})
if __name__ == "__main__":
socketio.run(app, host='0.0.0.0', debug=True, port=5000)

890
music-dashboard.html Normal file
View File

@ -0,0 +1,890 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Legendary Music Player</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
<style>
body {
font-family: 'Arial', sans-serif;
background-color: #121212;
color: #ffffff;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
.music-player {
width: 90%;
max-width: 1200px;
background-color: #1f1f1f;
border-radius: 15px;
overflow: hidden;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
transition: transform 0.3s;
}
.top-panel {
display: flex;
justify-content: space-between;
align-items: center;
gap: 10px;
padding: 20px;
background-color: #292929;
border-bottom: 1px solid #444;
}
.top-panel input[type="text"] {
flex-grow: 1;
padding: 12px;
border: none;
border-radius: 5px;
background-color: #333;
color: #fff;
}
.top-panel button {
padding: 12px 25px;
border: none;
border-radius: 5px;
background-color: #f1c40f;
color: #121212;
cursor: pointer;
transition: background-color 0.3s;
font-weight: bold;
position: relative;
}
.top-panel button:hover {
background-color: #e1b308;
}
.top-panel button::after {
content: attr(data-tooltip);
position: absolute;
bottom: -30px;
left: 50%;
transform: translateX(-50%);
background-color: rgba(0, 0, 0, 0.8);
color: #fff;
padding: 5px 10px;
border-radius: 5px;
font-size: 12px;
white-space: nowrap;
display: none;
}
.top-panel button:hover::after {
display: block;
}
.main-content {
display: flex;
height: 600px;
}
.nav-panel {
width: 25%;
background-color: #212121;
padding: 20px;
border-right: 1px solid #444;
display: flex;
flex-direction: column;
}
.nav-panel .nav-bar {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.nav-panel .nav-bar button {
flex: 1;
margin: 0 5px;
padding: 12px 0;
border: none;
border-radius: 5px;
background-color: #f1c40f;
color: #121212;
cursor: pointer;
transition: background-color 0.3s;
font-weight: bold;
}
.nav-panel .nav-bar button:hover {
background-color: #e1b308;
}
.nav-panel .search-bar {
padding: 12px;
border: none;
border-radius: 5px;
background-color: #333;
color: #fff;
margin-bottom: 20px;
}
.nav-panel .music-list {
flex-grow: 1;
overflow-y: auto;
}
.nav-panel .music-list ul {
list-style: none;
padding: 0;
margin: 0;
}
.nav-panel .music-list li {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px;
border-bottom: 1px solid #444;
cursor: pointer;
transition: background-color 0.3s;
word-wrap: break-word;
}
.nav-panel .music-list li:hover {
background-color: #333;
}
.nav-panel .music-list li .music-title {
flex-grow: 1;
font-size: 16px;
font-weight: bold;
}
.nav-panel .music-list li .delete-button {
margin-left: 10px;
font-size: 16px;
color: #f1c40f;
cursor: pointer;
}
.content-panel {
width: 75%;
padding: 20px;
display: flex;
flex-direction: column;
align-items: center;
}
.content-panel .current-song {
margin-bottom: 20px;
text-align: center;
}
.content-panel .current-song h2 {
margin: 0 0 10px 0;
font-size: 24px;
font-weight: bold;
}
.content-panel .current-song p {
margin: 0;
font-size: 18px;
color: #f1c40f;
}
.content-panel .current-song img {
max-width: 100%;
max-height: 300px;
border-radius: 10px;
margin-top: 10px;
}
.content-panel .queue {
margin-top: 20px;
width: 100%;
}
.content-panel .queue h3 {
margin: 0 0 10px 0;
font-size: 20px;
font-weight: bold;
}
.content-panel .queue ul {
list-style: none;
padding: 0;
margin: 0;
max-height: 400px;
overflow-y: auto;
width: 100%;
}
.content-panel .queue li {
padding: 15px;
border-bottom: 1px solid #444;
display: flex;
justify-content: space-between;
align-items: center;
transition: background-color 0.3s;
cursor: pointer;
background-color: #2a2a2a;
border-radius: 5px;
margin-bottom: 10px;
}
.content-panel .queue li:hover {
background-color: #333;
}
.content-panel .queue li.active {
background-color: #444;
color: #f1c40f;
}
.content-panel .queue li .song-info {
display: flex;
flex-direction: column;
flex-grow: 1;
}
.content-panel .queue li .song-title {
font-size: 16px;
font-weight: bold;
}
.content-panel .queue li .song-artist {
font-size: 14px;
color: #aaa;
}
.content-panel .queue li .buttons {
display: flex;
align-items: center;
gap: 10px;
}
.content-panel .queue li .play-button,
.content-panel .queue li .delete-button {
font-size: 16px;
color: #f1c40f;
cursor: pointer;
}
.player-controls {
display: flex;
justify-content: center;
align-items: center;
margin-top: 20px;
}
.player-controls button {
background: none;
border: none;
color: #f1c40f;
font-size: 24px;
margin: 0 10px;
cursor: pointer;
position: relative;
}
.player-controls button:hover {
color: #e1b308;
}
.player-controls button.active {
color: #e1b308;
}
.player-controls button::after {
content: attr(data-tooltip);
position: absolute;
bottom: -30px;
left: 50%;
transform: translateX(-50%);
background-color: rgba(0, 0, 0, 0.8);
color: #fff;
padding: 5px 10px;
border-radius: 5px;
font-size: 12px;
white-space: nowrap;
display: none;
z-index: 1269;
}
.player-controls button:hover::after {
display: block;
}
.seek-bar {
width: 100%;
margin-top: 10px;
display: flex;
align-items: center;
gap: 10px;
position: relative;
}
.seek-bar .progress-bar {
width: 100%;
height: 12.69px;
background: #333;
border-radius: 8px;
position: relative;
cursor: pointer;
}
.seek-bar .progress {
height: 100%;
background: linear-gradient(90deg, #f1c40f, #e1b308);
border-radius: 8px;
box-shadow: 0 0 15px rgba(241, 196, 15, 0.7);
}
.seek-bar .thumb {
width: 16px;
height: 16px;
background: linear-gradient(90deg, #ffd119, #e1b308);
border-radius: 50%;
border: 2px solid #121212;
position: absolute;
top: 50%;
transform: translate(-50%, -50%);
cursor: pointer;
box-shadow: 0 0 15px rgba(255, 209, 25, 0.7);
}
.song-duration {
font-size: 14px;
color: #aaa;
}
canvas {
width: 100%;
height: 150px;
border-radius: 10px;
background-color: #212121;
transition: transform 0.1s;
}
.queue-container {
display: flex;
flex-direction: column;
max-height: 400px;
width: 100%;
padding: 0;
margin: 0;
}
.queue-view {
display: flex;
flex-direction: column;
max-height: 300px;
height: 12.69rem;
overflow-y: auto;
width: 100%;
padding: 0;
margin-bottom: 0;
margin-right: 0;
margin-left: 0;
margin-top: -0.869rem;
}
.spark {
position: absolute;
width: 5px;
height: 5px;
background-color: #ffd119;
border-radius: 50%;
animation: spark 0.5s linear infinite;
opacity: 0;
}
@keyframes spark {
0% {
opacity: 1;
transform: translate(0, 0);
}
100% {
opacity: 0;
transform: translate(50px, -50px);
}
}
.spark-container {
position: relative;
}
.spark-container.active .spark {
display: block;
}
.spark-container .spark {
display: none;
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.14.0/Sortable.min.js"></script>
</head>
<body>
<div class="music-player" id="music-player">
<div class="top-panel">
<input type="text" id="youtube-url" placeholder="Add song from YouTube URL" />
<button onclick="addSongFromURL()" data-tooltip="Add song from YouTube URL">Add</button>
<input type="file" id="audio-upload" style="display: none;" accept="audio/*" />
<button onclick="document.getElementById('audio-upload').click()" data-tooltip="Upload an audio file">Upload</button>
</div>
<div class="main-content">
<div class="nav-panel">
<div class="nav-bar">
<button onclick="showAllMusic()">All Music</button>
<button onclick="showPlaylists()">Playlists</button>
</div>
<input type="text" class="search-bar" placeholder="Search..." oninput="searchMusic(event)" />
<div class="music-list">
<ul id="music-list">
</ul>
</div>
</div>
<div class="content-panel">
<div class="current-song">
<h2>Currently Playing</h2>
<p id="current-song-title">No song playing</p>
<img id="current-song-image" src="" alt="" style="display: none;" />
</div>
<canvas id="visualizer"></canvas>
<div class="player-controls">
<button onclick="prevSong()" data-tooltip="Previous song"><i class="fas fa-backward"></i></button>
<button onclick="rewindSong()" data-tooltip="Rewind song"><i class="fas fa-undo"></i></button>
<button onclick="togglePlayPause()" data-tooltip="Play/Pause"><i id="play-pause-icon" class="fas fa-play"></i></button>
<button onclick="nextSong()" data-tooltip="Next song"><i class="fas fa-forward"></i></button>
<div id="repeat-container" class="spark-container">
<button onclick="toggleRepeat()" id="repeat-button" data-tooltip="Repeat"><i id="repeat-icon" class="fas fa-redo"></i></button>
<div class="spark"></div>
<div class="spark"></div>
<div class="spark"></div>
<div class="spark"></div>
<div class="spark"></div>
</div>
</div>
<div class="seek-bar">
<span id="current-time" class="song-duration">0:00</span>
<div class="progress-bar" id="progress-bar" onclick="seekTo(event)">
<div class="progress" id="progress"></div>
<div class="thumb" id="thumb"></div>
</div>
<span id="total-duration" class="song-duration">0:00</span>
</div>
<div class="queue-container">
<h3>Queue</h3>
<div class="queue-view">
<div class="queue">
<ul id="queue-list">
</ul>
</div>
</div>
</div>
<audio id="audio-player" style="display: none;" ontimeupdate="updateSeekBar()" onloadedmetadata="updateDuration()">
Your browser does not support the audio element.
</audio>
</div>
</div>
</div>
<script>
let queue = [];
let currentIndex = 0;
let isPlaying = false;
let isRepeating = false;
let isDragging = false;
const audioContext = new(window.AudioContext || window.webkitAudioContext)();
const audioPlayer = document.getElementById('audio-player');
const analyser = audioContext.createAnalyser();
const source = audioContext.createMediaElementSource(audioPlayer);
source.connect(analyser);
analyser.connect(audioContext.destination);
const canvas = document.getElementById('visualizer');
const canvasContext = canvas.getContext('2d');
const musicPlayer = document.getElementById('music-player');
analyser.fftSize = 256;
const bufferLength = analyser.frequencyBinCount;
const dataArray = new Uint8Array(bufferLength);
function drawVisualizer() {
requestAnimationFrame(drawVisualizer);
analyser.getByteFrequencyData(dataArray);
canvasContext.fillStyle = '#212121';
canvasContext.fillRect(0, 0, canvas.width, canvas.height);
const barWidth = (canvas.width / bufferLength) * 2.5;
let barHeight;
let x = 0;
for (let i = 0; i < bufferLength; i++)
{
barHeight = dataArray[i];
canvasContext.fillStyle = 'rgba(241, 196, 15, 0.869)';
canvasContext.fillRect(x, canvas.height - barHeight / 2, barWidth, barHeight / 2);
let colorIntensity = 255 - barHeight;
let toneColor = `rgba(${colorIntensity}, ${colorIntensity * 0.569}, 15, 0.369)`;
canvasContext.fillStyle = toneColor;
canvasContext.fillRect(x, canvas.height - barHeight / 2, barWidth, barHeight / 2);
x += barWidth + 1;
}
}
function addSongFromURL() {
const url = document.getElementById('youtube-url').value;
if (url) {
queue.push({
title: `Song ${queue.length + 1}`,
artist: 'Artist',
url,
image: ''
});
updateQueue();
document.getElementById('youtube-url').value = '';
}
}
document.getElementById('audio-upload').addEventListener('change', function(event) {
const file = event.target.files[0];
if (file) {
const url = URL.createObjectURL(file);
queue.push({
title: file.name,
artist: 'Uploaded File',
url
});
updateQueue();
}
});
function updateQueue() {
const queueList = document.getElementById('queue-list');
queueList.innerHTML = '';
queue.forEach((song, index) => {
const li = document.createElement('li');
li.classList.toggle('active', index === currentIndex);
li.innerHTML = `
<div class="song-info">
<span class="song-title">${song.title}</span>
<span class="song-artist">${song.artist}</span>
</div>
<div class="buttons">
<span class="play-button" onclick="playSong(${index})"><i class="fas fa-play"></i></span>
<span class="delete-button" onclick="deleteSong(${index})"><i class="fas fa-trash"></i></span>
</div>
`;
queueList.appendChild(li);
});
makeQueueSortable();
}
function makeQueueSortable() {
Sortable.create(document.getElementById('queue-list'), {
onEnd: function(evt) {
const [movedItem] = queue.splice(evt.oldIndex, 1);
queue.splice(evt.newIndex, 0, movedItem);
updateQueue();
}
});
}
function playSong(index) {
currentIndex = index;
const currentSong = queue[index];
document.getElementById('current-song-title').textContent = `${currentSong.title} - ${currentSong.artist}`;
audioPlayer.src = currentSong.url;
audioPlayer.style.display = 'none';
audioPlayer.play().then(() => {
audioContext.resume();
});
isPlaying = true;
updatePlayPauseIcon();
updateQueue();
resetSeekBar();
if (currentSong.image) {
document.getElementById('current-song-image').src = currentSong.image;
document.getElementById('current-song-image').style.display = 'block';
} else {
document.getElementById('current-song-image').style.display = 'none';
}
}
function deleteSong(index) {
queue.splice(index, 1);
if (index === currentIndex && queue.length > 0) {
currentIndex = (index === queue.length) ? index - 1 : index;
playSong(currentIndex);
} else if (queue.length === 0) {
document.getElementById('current-song-title').textContent = 'No song playing';
document.getElementById('current-song-image').style.display = 'none';
isPlaying = false;
updatePlayPauseIcon();
resetSeekBar();
}
updateQueue();
}
function togglePlayPause() {
if (isPlaying) {
audioPlayer.pause();
isPlaying = false;
} else {
if (queue.length > 0 && audioPlayer.src === "") {
playSong(currentIndex);
} else {
audioPlayer.play().then(() => {
audioContext.resume();
});
}
isPlaying = true;
}
updatePlayPauseIcon();
}
function updatePlayPauseIcon() {
const icon = document.getElementById('play-pause-icon');
if (isPlaying) {
icon.classList.remove('fa-play');
icon.classList.add('fa-pause');
} else {
icon.classList.remove('fa-pause');
icon.classList.add('fa-play');
}
}
function prevSong() {
currentIndex = (currentIndex > 0) ? currentIndex - 1 : queue.length - 1;
playSong(currentIndex);
}
function nextSong() {
currentIndex = (currentIndex < queue.length - 1) ? currentIndex + 1 : 0;
playSong(currentIndex);
}
function rewindSong() {
audioPlayer.currentTime = 0;
}
function toggleRepeat() {
isRepeating = !isRepeating;
const icon = document.getElementById('repeat-icon');
const button = document.getElementById('repeat-button');
const container = document.getElementById('repeat-container');
if (isRepeating) {
icon.classList.add('active');
button.classList.add('active');
container.classList.add('active');
startSparks();
} else {
icon.classList.remove('active');
button.classList.remove('active');
container.classList.remove('active');
stopSparks();
}
}
function startSparks() {
document.querySelectorAll('.spark').forEach(spark => {
spark.style.animation = `spark 0.5s linear infinite`;
});
}
function stopSparks() {
document.querySelectorAll('.spark').forEach(spark => {
spark.style.animation = '';
});
}
function seekTo(event) {
if (!isDragging) {
const progressBar = document.getElementById('progress-bar');
const rect = progressBar.getBoundingClientRect();
const offsetX = event.clientX - rect.left;
const percentage = offsetX / progressBar.clientWidth;
const seekTo = audioPlayer.duration * percentage;
audioPlayer.currentTime = seekTo;
}
}
function startDrag() {
isDragging = true;
}
function stopDrag() {
isDragging = false;
}
function dragThumb(event) {
if (isDragging) {
const progressBar = document.getElementById('progress-bar');
const rect = progressBar.getBoundingClientRect();
const offsetX = event.clientX - rect.left;
const percentage = Math.min(Math.max(offsetX / progressBar.clientWidth, 0), 100);
const seekTo = audioPlayer.duration * percentage;
audioPlayer.currentTime = seekTo;
}
}
function updateSeekBar() {
const progressBar = document.getElementById('progress-bar');
const progress = document.getElementById('progress');
const thumb = document.getElementById('thumb');
const value = (audioPlayer.currentTime / audioPlayer.duration) * 100;
progress.style.width = `${value}%`;
thumb.style.left = `${value}%`;
const currentMinutes = Math.floor(audioPlayer.currentTime / 60);
const currentSeconds = Math.floor(audioPlayer.currentTime % 60);
document.getElementById('current-time').textContent = `${currentMinutes}:${currentSeconds < 10 ? '0' : ''}${currentSeconds}`;
}
function resetSeekBar() {
const progressBar = document.getElementById('progress-bar');
const progress = document.getElementById('progress');
const thumb = document.getElementById('thumb');
progress.style.width = '0';
thumb.style.left = '0';
document.getElementById('current-time').textContent = '0:00';
document.getElementById('total-duration').textContent = '0:00';
}
function updateDuration() {
const totalMinutes = Math.floor(audioPlayer.duration / 60);
const totalSeconds = Math.floor(audioPlayer.duration % 60);
document.getElementById('total-duration').textContent = `${totalMinutes}:${totalSeconds < 10 ? '0' : ''}${totalSeconds}`;
}
function showAllMusic() {
const musicList = document.getElementById('music-list');
musicList.innerHTML = '';
for (let i = 1; i <= 20; i++) {
const li = document.createElement('li');
li.innerHTML = `
<span class="music-title">All Music ${i}</span>
<span class="delete-button" onclick="deleteMusicItem(this)"><i class="fas fa-trash"></i></span>
`;
musicList.appendChild(li);
}
}
function showPlaylists() {
const musicList = document.getElementById('music-list');
musicList.innerHTML = '';
for (let i = 1; i <= 10; i++) {
const li = document.createElement('li');
li.innerHTML = `
<span class="music-title">Playlist ${i}</span>
<span class="delete-button" onclick="deleteMusicItem(this)"><i class="fas fa-trash"></i></span>
`;
musicList.appendChild(li);
}
}
function deleteMusicItem(element) {
const listItem = element.closest('li');
listItem.remove();
}
function searchMusic(event) {
const query = event.target.value.toLowerCase();
const musicList = document.getElementById('music-list');
const musicItems = musicList.getElementsByTagName('li');
const queueList = document.getElementById('queue-list');
const queueItems = queueList.getElementsByTagName('li');
for (let item of musicItems) {
const text = item.textContent.toLowerCase();
item.style.display = text.includes(query) ? '' : 'none';
}
for (let item of queueItems) {
const text = item.textContent.toLowerCase();
item.style.display = text.includes(query) ? '' : 'none';
}
}
function calculateTilt(element, event) {
const rect = element.getBoundingClientRect();
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
const centerX = rect.width / 2;
const centerY = rect.height / 2;
const deltaX = (x - centerX) / centerX;
const deltaY = (y - centerY) / centerY;
return {
rotateX: deltaY * -10,
rotateY: deltaX * 10
};
}
canvas.addEventListener('mousemove', (event) => {
const {
rotateX,
rotateY
} = calculateTilt(canvas, event);
canvas.style.transform = `perspective(1000px) rotateX(${rotateX}deg) rotateY(${rotateY}deg)`;
});
canvas.addEventListener('mouseleave', () => {
canvas.style.transform = 'perspective(1000px) rotateX(0) rotateY(0)';
});
musicPlayer.addEventListener('mousemove', (event) => {
const {
rotateX,
rotateY
} = calculateTilt(musicPlayer, event);
musicPlayer.style.transform = `perspective(1000px) rotateX(${rotateX / 12.69}deg) rotateY(${rotateY / 12.69}deg)`;
});
musicPlayer.addEventListener('mouseleave', () => {
musicPlayer.style.transform = 'perspective(1000px) rotateX(0) rotateY(0)';
});
audioPlayer.addEventListener('ended', function() {
if (isRepeating) {
playSong(currentIndex);
} else {
queue.splice(currentIndex, 1);
if (queue.length > 0) {
if (currentIndex >= queue.length) {
currentIndex = 0;
}
playSong(currentIndex);
} else {
document.getElementById('current-song-title').textContent = 'No song playing';
document.getElementById('current-song-image').style.display = 'none';
isPlaying = false;
updatePlayPauseIcon();
updateQueue();
resetSeekBar();
}
}
});
drawVisualizer();
makeQueueSortable();
const thumb = document.getElementById('thumb');
thumb.addEventListener('mousedown', startDrag);
document.addEventListener('mouseup', stopDrag);
document.addEventListener('mousemove', dragThumb);
</script>
</body>
</html>