Spaces:
Runtime error
Runtime error
| let isLooping = false; // Flag to manage loop state | |
| let isLoadingMusic = false; // Flag to manage loading state | |
| let currentPlaying = ''; // Store the filename of the currently playing music | |
| let trackList = []; // Store currently displayed track filenames | |
| let currentPage = 1; | |
| let totalPages = 0; | |
| let audio = new Audio(); | |
| window.onload = () => { | |
| // Attach click handlers for paging | |
| document.getElementById('prev-page').addEventListener('click', () => changePage(-1)); | |
| document.getElementById('next-page').addEventListener('click', () => changePage(1)); | |
| loadMusicList(); | |
| document.getElementById('search-bar').addEventListener('keyup', () => { | |
| currentPage = 1; // Reset to first page on new search | |
| loadMusicList(); | |
| }); | |
| document.getElementById('audio-player').style.display = 'none'; | |
| updatePageIndicator(); | |
| setupCustomAudioPlayer(); | |
| }; | |
| function changePage(direction) { | |
| currentPage += direction; | |
| loadMusicList(); | |
| } | |
| function updatePageIndicator(totalPages) { | |
| document.getElementById('page-indicator').textContent = `${currentPage}/${totalPages}`; | |
| } | |
| function debounce(func, delay) { | |
| let debounceTimer; | |
| return function() { | |
| const context = this; | |
| const args = arguments; | |
| clearTimeout(debounceTimer); | |
| debounceTimer = setTimeout(() => func.apply(context, args), delay); | |
| }; | |
| } | |
| function toggleLoop() { | |
| isLooping = !isLooping; | |
| document.getElementById('loop-toggle').textContent = `Loop: ${isLooping ? 'On' : 'Off'}`; | |
| const player = document.getElementById('audio-player'); | |
| player.loop = isLooping; | |
| } | |
| function updateNavigationVisibility(searchQuery, totalPages) { | |
| const navigation = document.getElementById('navigation'); | |
| if (searchQuery) { | |
| navigation.style.display = 'none'; // Hide navigation during search | |
| } else { | |
| navigation.style.display = 'flex'; // Show navigation when not searching | |
| document.getElementById('prev-page').disabled = currentPage <= 1; | |
| document.getElementById('next-page').disabled = currentPage >= totalPages; | |
| } | |
| } | |
| // Call loadMusicList when the search input changes | |
| document.getElementById('search-bar').addEventListener('keyup', debounce(() => { | |
| currentPage = 1; | |
| loadMusicList(); | |
| }, 500)); // Waits for 0.5s of inactivity after typing | |
| function loadMusicList() { | |
| const searchQuery = document.getElementById('search-bar').value.toLowerCase(); | |
| const params = new URLSearchParams({ page: currentPage }); | |
| if (searchQuery) { | |
| params.append('search', searchQuery); // Add search query to the request if it exists | |
| } | |
| // Disabling buttons and showing loading indicator before the request | |
| document.getElementById('prev-page').disabled = true; | |
| document.getElementById('next-page').disabled = true; | |
| const loaderContainer = document.getElementById('loader-container'); | |
| loaderContainer.innerHTML = ''; // Clears any existing loader elements if they are present | |
| const loaderElement = document.createElement('div'); | |
| loaderElement.classList.add('loader'); | |
| loaderContainer.appendChild(loaderElement); | |
| fetch(`/tracks?${params}`) | |
| .then(response => response.json()) | |
| .then(data => { | |
| const { tracks, total } = data; | |
| const musicList = document.getElementById('music-list'); | |
| musicList.innerHTML = ''; | |
| trackList = []; | |
| const totalPages = Math.ceil(total / 5); | |
| // Hide loading indicator | |
| loaderContainer.innerHTML = ''; | |
| // Re-enable buttons based on page state | |
| document.getElementById('prev-page').disabled = currentPage === 1; | |
| document.getElementById('next-page').disabled = currentPage === totalPages || searchQuery; | |
| tracks.forEach(track => { | |
| trackList.push(track.filename); | |
| const musicItem = createMusicItem(track); | |
| musicList.appendChild(musicItem); | |
| }); | |
| // Show/hide navigation based on whether there is a search query | |
| updatePageIndicator(totalPages); | |
| updateNavigationVisibility(searchQuery, totalPages); | |
| updatePlayingIndicator(); | |
| }); | |
| } | |
| function createMusicItem(track) { | |
| const musicItem = document.createElement('div'); | |
| musicItem.classList.add('music-item'); | |
| musicItem.setAttribute('data-filename', track.filename); | |
| const artworkImg = document.createElement('img'); | |
| artworkImg.src = track.artwork ? track.artwork : 'placeholder_art.png'; | |
| artworkImg.alt = 'Album Art'; | |
| artworkImg.classList.add('album-art'); | |
| const fileNameSpan = document.createElement('span'); | |
| fileNameSpan.textContent = track.filename; | |
| musicItem.append(artworkImg, fileNameSpan); | |
| // Bind the music item click to playMusic | |
| musicItem.addEventListener('click', () => playMusic(track.filename)); | |
| return musicItem; | |
| } | |
| function updatePlayingIndicator() { | |
| const musicItems = document.querySelectorAll('.music-item'); | |
| musicItems.forEach(item => { | |
| if (item.getAttribute('data-filename') === currentPlaying) { | |
| item.style.boxShadow = '0 0 10px rgba(255, 255, 255, 0.5)'; // Green box shadow | |
| } else { | |
| item.style.boxShadow = ''; // Remove box shadow from other items | |
| } | |
| }); | |
| } | |
| function setupCustomAudioPlayer() { | |
| // Play/Pause toggle | |
| document.getElementById('play-pause-btn').addEventListener('click', function() { | |
| if (audio.paused) { | |
| audio.play(); | |
| this.textContent = 'ββ'; // Update button text to "ββ" | |
| } else { | |
| audio.pause(); | |
| this.textContent = 'βΆ'; // Update button text to "βΆ" | |
| } | |
| }); | |
| // Update progress bar as the audio plays | |
| audio.addEventListener('timeupdate', function() { | |
| const progress = document.getElementById('progress-bar'); | |
| progress.value = (audio.currentTime / audio.duration) * 100; | |
| }); | |
| // Seek in the audio when the progress bar value changes | |
| document.getElementById('progress-bar').addEventListener('input', function() { | |
| const duration = audio.duration; | |
| audio.currentTime = (this.value * duration) / 100; | |
| }); | |
| // Mute toggle | |
| document.getElementById('mute-btn').addEventListener('click', function() { | |
| audio.muted = !audio.muted; | |
| this.textContent = audio.muted ? 'π' : 'π'; // Update button text based on mute state | |
| }); | |
| // Volume control | |
| document.getElementById('volume-control').addEventListener('input', function() { | |
| audio.volume = this.value / 100; | |
| }); | |
| // Initial volume (100%) | |
| audio.volume = 1.0; | |
| audio.addEventListener('timeupdate', function() { | |
| updateProgress(); | |
| }); | |
| // When audio metadata is loaded, update duration | |
| audio.addEventListener('loadedmetadata', function() { | |
| document.getElementById('duration').textContent = formatTime(audio.duration); | |
| }); | |
| } | |
| function updateProgress() { | |
| const progress = document.getElementById('progress-bar'); | |
| progress.value = (audio.currentTime / audio.duration) * 100; | |
| document.getElementById('current-time').textContent = formatTime(audio.currentTime); | |
| } | |
| function formatTime(seconds) { | |
| let minutes = Math.floor(seconds / 60); | |
| minutes = (minutes >= 10) ? minutes : "0" + minutes; | |
| seconds = Math.floor(seconds % 60); | |
| seconds = (seconds >= 10) ? seconds : "0" + seconds; | |
| return minutes + ":" + seconds; | |
| } | |
| function playMusic(fileName) { | |
| if (isLoadingMusic && fileName === currentPlaying) { | |
| console.log("This song is already loading."); | |
| return; | |
| } else if (!isLoadingMusic && fileName === currentPlaying) { | |
| console.log("This song is already playing."); | |
| return; | |
| } | |
| isLoadingMusic = true; | |
| currentPlaying = fileName; | |
| updatePlayingIndicator(); | |
| document.getElementById('audio-player').style.display = 'none'; // Hide custom player UI | |
| const loaderContainer = document.getElementById('loader-container'); | |
| loaderContainer.innerHTML = ''; | |
| const loaderElement = document.createElement('div'); | |
| loaderElement.classList.add('loader'); | |
| loaderContainer.appendChild(loaderElement); | |
| // Set the new source file for audio | |
| audio.src = `/music/${fileName}`; | |
| audio.load(); // Start loading the new source file | |
| audio.oncanplaythrough = () => { | |
| document.getElementById('audio-player').style.display = 'flex'; // Show custom player UI | |
| loaderContainer.innerHTML = ''; | |
| isLoadingMusic = false; | |
| document.getElementById('play-pause-btn').textContent = 'ββ'; // Update play/pause button | |
| audio.play(); // Start playback | |
| }; | |
| // Updated onended event handler within playMusic function | |
| audio.onended = () => { | |
| if (isLooping) { | |
| // Replay the current song | |
| audio.currentTime = 0; | |
| audio.play(); | |
| } else { | |
| const currentIndex = trackList.indexOf(currentPlaying); | |
| if (currentIndex !== -1 && currentIndex < trackList.length - 1) { | |
| playMusic(trackList[currentIndex + 1]); | |
| } | |
| } | |
| }; | |
| audio.onerror = () => { | |
| isLoadingMusic = false; | |
| alert(`Error loading the music: ${fileName}`); | |
| currentPlaying = ''; // Reset the current playing track | |
| }; | |
| } |