You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
119 lines
3.6 KiB
119 lines
3.6 KiB
|
9 months ago
|
let currentTrackIndex = 0;
|
||
|
|
let tracks = [];
|
||
|
|
const audio = new Audio();
|
||
|
|
let isPlaying = false;
|
||
|
|
|
||
|
|
// DOM Elements
|
||
|
|
const playBtn = document.getElementById('playBtn');
|
||
|
|
const trackList = document.getElementById('track-list');
|
||
|
|
const currentTimeElem = document.getElementById('current-time');
|
||
|
|
const durationElem = document.getElementById('duration-time');
|
||
|
|
const progressContainer = document.getElementById('progress-container');
|
||
|
|
const progressBar = document.getElementById('progress-bar');
|
||
|
|
|
||
|
|
// Fetch tracks and initialize
|
||
|
|
fetch('/tracks')
|
||
|
|
.then(response => response.json())
|
||
|
|
.then(data => {
|
||
|
|
tracks = data;
|
||
|
|
renderPlaylist();
|
||
|
|
loadTrack(currentTrackIndex);
|
||
|
|
});
|
||
|
|
|
||
|
|
function renderPlaylist() {
|
||
|
|
trackList.innerHTML = tracks.map((track, index) => `
|
||
|
|
<div class="list-group-item ${index === 0 ? 'active' : ''}"
|
||
|
|
onclick="playTrack(${index})">
|
||
|
|
${track.title} <span class="text-muted float-end">${track.artist}</span>
|
||
|
|
<span>${track.explicit === 'true' ? '<i class="bi bi-exclamation-circle" title="Explicit language"></i>' : ''} </span>
|
||
|
|
</div>
|
||
|
|
`).join('');
|
||
|
|
}
|
||
|
|
|
||
|
|
function playTrack(index) {
|
||
|
|
currentTrackIndex = index;
|
||
|
|
const items = document.querySelectorAll('.list-group-item')
|
||
|
|
items.forEach(item => item.classList.remove('active'));
|
||
|
|
items[index].classList.add('active');
|
||
|
|
items[index].scrollIntoView( { behavior: 'smooth', block: 'nearest'})
|
||
|
|
|
||
|
|
loadTrack(index);
|
||
|
|
audio.play();
|
||
|
|
isPlaying = true;
|
||
|
|
updatePlayButton();
|
||
|
|
}
|
||
|
|
|
||
|
|
function loadTrack(index) {
|
||
|
|
const track = tracks[index];
|
||
|
|
audio.src = `/audio/${track.file}`;
|
||
|
|
document.getElementById('cover').src = `/cover/${track.cover}`;
|
||
|
|
document.getElementById('song-title').textContent = track.title;
|
||
|
|
document.getElementById('song-artist').textContent = track.artist;
|
||
|
|
}
|
||
|
|
|
||
|
|
function togglePlay() {
|
||
|
|
if (isPlaying) {
|
||
|
|
audio.pause();
|
||
|
|
} else {
|
||
|
|
audio.play();
|
||
|
|
}
|
||
|
|
isPlaying = !isPlaying;
|
||
|
|
updatePlayButton();
|
||
|
|
}
|
||
|
|
|
||
|
|
function updatePlayButton() {
|
||
|
|
const icon = playBtn.querySelector('i');
|
||
|
|
icon.classList.toggle('bi-play-fill', !isPlaying);
|
||
|
|
icon.classList.toggle('bi-pause-fill', isPlaying);
|
||
|
|
}
|
||
|
|
|
||
|
|
function nextTrack() {
|
||
|
|
currentTrackIndex = (currentTrackIndex + 1) % tracks.length;
|
||
|
|
playTrack(currentTrackIndex);
|
||
|
|
}
|
||
|
|
|
||
|
|
function previousTrack() {
|
||
|
|
currentTrackIndex = (currentTrackIndex - 1 + tracks.length) % tracks.length;
|
||
|
|
playTrack(currentTrackIndex);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Time formatting
|
||
|
|
function formatTime(seconds) {
|
||
|
|
const minutes = Math.floor(seconds / 60);
|
||
|
|
seconds = Math.floor(seconds % 60);
|
||
|
|
return `${minutes}:${seconds.toString().padStart(2, '0')}`;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Audio event listeners
|
||
|
|
audio.addEventListener('timeupdate', () => {
|
||
|
|
const progressPercent = (audio.currentTime / audio.duration) * 100;
|
||
|
|
progressBar.style.width = `${progressPercent}%`;
|
||
|
|
|
||
|
|
currentTimeElem.textContent = formatTime(audio.currentTime);
|
||
|
|
durationElem.textContent = formatTime(duration);
|
||
|
|
});
|
||
|
|
|
||
|
|
audio.addEventListener('loadedmetadata', () => {
|
||
|
|
durationElem.textContent = formatTime(audio.duration);
|
||
|
|
});
|
||
|
|
|
||
|
|
audio.addEventListener('ended', nextTrack);
|
||
|
|
audio.addEventListener('play', () => {
|
||
|
|
isPlaying = true;
|
||
|
|
updatePlayButton();
|
||
|
|
});
|
||
|
|
audio.addEventListener('pause', () => {
|
||
|
|
isPlaying = false;
|
||
|
|
updatePlayButton();
|
||
|
|
});
|
||
|
|
|
||
|
|
progressContainer.addEventListener('click', (e) => {
|
||
|
|
if (!audio.duration) return;
|
||
|
|
|
||
|
|
const rect = progressContainer.getBoundingClientRect();
|
||
|
|
const x = e.clientX - rect.left;
|
||
|
|
const percentage = x / progressContainer.offsetWidth;
|
||
|
|
const seekTime = percentage * audio.duration;
|
||
|
|
audio.currentTime = seekTime;
|
||
|
|
});
|