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