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.
 
 
 
 

118 lines
3.6 KiB

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;
});