Add the html file, basic socketio structure
This commit is contained in:
parent
9bcf3ddb60
commit
a1ee1d9a8d
4
main1.py
4
main1.py
@ -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
72
main3.py
Normal 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
890
music-dashboard.html
Normal 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>
|
||||||
Loading…
x
Reference in New Issue
Block a user