diff --git a/static/app.css b/static/app.css
index 69fd905..b4b64da 100644
--- a/static/app.css
+++ b/static/app.css
@@ -226,4 +226,7 @@ footer {
#reload-button svg.rotate {
animation: rotate-icon 0.5s linear;
+ transition: opacity 1s ease;
+ opacity: 0;
+ cursor: auto;
}
\ No newline at end of file
diff --git a/static/app.js b/static/app.js
index 730d554..6c352aa 100644
--- a/static/app.js
+++ b/static/app.js
@@ -175,99 +175,113 @@ function attachEventListeners() {
});
});
+// Cache common DOM elements
+const nowPlayingInfo = document.getElementById('nowPlayingInfo');
+const audioPlayer = document.getElementById('globalAudio');
+const playerButton = document.querySelector('.player-button');
+const audioPlayerContainer = document.getElementById('audioPlayerContainer');
+const footer = document.querySelector('footer');
+
// Global variable to store the current fetch's AbortController.
let currentFetchController = null;
document.querySelectorAll('.play-file').forEach(link => {
link.addEventListener('click', async function (event) {
event.preventDefault();
- const fileType = this.getAttribute('data-file-type');
- const relUrl = this.getAttribute('data-url');
- const nowPlayingInfo = document.getElementById('nowPlayingInfo');
+ const { fileType, url: relUrl, index } = this.dataset;
+
// Remove the class from all file items.
document.querySelectorAll('.file-item').forEach(item => {
item.classList.remove('currently-playing');
});
if (fileType === 'music') {
- const idx = this.getAttribute('data-index');
- const audioPlayer = document.getElementById('globalAudio');
- const playerButton = document.querySelector('.player-button');
-
- // Update currentMusicIndex based on the clicked element.
- currentMusicIndex = idx !== null ? parseInt(idx) : -1;
-
- // Show the audio player container.
- document.getElementById('audioPlayerContainer').style.display = "block"
-
- // Add the class to the clicked file item's parent.
+ // Update the current music index.
+ currentMusicIndex = index !== undefined ? parseInt(index) : -1;
+
+ // Display the audio player container.
+ audioPlayerContainer.style.display = "block";
+
+ // Mark the clicked item as currently playing.
this.closest('.file-item').classList.add('currently-playing');
- // Abort any previous fetch if it is still running.
+ // Abort any previous fetch if still running.
if (currentFetchController) {
currentFetchController.abort();
}
- // Create a new AbortController for the current fetch.
currentFetchController = new AbortController();
+ // Pause the audio and clear its source.
audioPlayer.pause();
- audioPlayer.src = ''; // Clear existing source.
- // only show this if it takes longer. avoid flicker
- let loaderTimeout = setTimeout(() => {
+ audioPlayer.src = '';
+
+ // Set a timeout to display a loader message if needed.
+ const loaderTimeout = setTimeout(() => {
playerButton.innerHTML = playIcon;
nowPlayingInfo.textContent = "Wird geladen...";
}, 500);
- document.querySelector('footer').style.display = 'flex';
+
+ footer.style.display = 'flex';
- const mediaUrl = '/media/' + relUrl;
+ const mediaUrl = `/media/${relUrl}`;
try {
- // Pass the signal to the fetch so it can be aborted.
+ // Perform a HEAD request to verify media availability.
const response = await fetch(mediaUrl, { method: 'HEAD', signal: currentFetchController.signal });
- clearTimeout(loaderTimeout); // done loading. stop timeout
+ clearTimeout(loaderTimeout);
+
if (response.status === 403) {
nowPlayingInfo.textContent = "Fehler: Zugriff verweigert.";
- window.location.href = '/'; // Redirect if forbidden.
+ window.location.href = '/';
+ return;
} else if (!response.ok) {
nowPlayingInfo.textContent = `Fehler: Unerwarteter Status (${response.status}).`;
console.error('Unexpected response status:', response.status);
- } else {
- audioPlayer.src = mediaUrl;
- audioPlayer.load();
- audioPlayer.play();
- playerButton.innerHTML = pauseIcon;
- const pathParts = relUrl.split('/');
- const folderName = pathParts[pathParts.length - 2];
- const fileName = pathParts.pop();
- const pathStr = pathParts.join('/');
- // write into hardware player
- if ('mediaSession' in navigator) {
- navigator.mediaSession.metadata = new MediaMetadata({
- title: currentMusicFiles[currentMusicIndex].title,
- artist: folderName,
- artwork: [
- { src: '/static/icons/logo-192x192.png', sizes: '192x192', type: 'image/png' }
- ]
- });
- };
- nowPlayingInfo.innerHTML = pathStr.replace(/\//g, ' > ') + '
' + fileName.replace('.mp3', '') + '';
- // Delay preloading to avoid blocking playback
- setTimeout(preload_audio, 1000);
- };
+ return;
+ }
+
+ // Set the media URL, load, and play the audio.
+ audioPlayer.src = mediaUrl;
+ audioPlayer.load();
+ await audioPlayer.play();
+ playerButton.innerHTML = pauseIcon;
+
+ // Process file path for display.
+ const pathParts = relUrl.split('/');
+ const folderName = pathParts[pathParts.length - 2];
+ const fileName = pathParts.pop();
+ const pathStr = pathParts.join('/');
+
+ // Update Media Session metadata if available.
+ if ('mediaSession' in navigator) {
+ navigator.mediaSession.metadata = new MediaMetadata({
+ title: currentMusicFiles[currentMusicIndex].title,
+ artist: folderName,
+ artwork: [
+ { src: '/static/icons/logo-192x192.png', sizes: '192x192', type: 'image/png' }
+ ]
+ });
+ }
+
+ nowPlayingInfo.innerHTML = pathStr.replace(/\//g, ' > ') +
+ '
' +
+ fileName.replace('.mp3', '') + '';
+
+ // Delay preloading to avoid blocking playback.
+ setTimeout(preload_audio, 1000);
} catch (error) {
- // If the fetch was aborted, error.name will be 'AbortError'.
if (error.name === 'AbortError') {
console.log('Previous fetch aborted.');
} else {
console.error('Error fetching media:', error);
nowPlayingInfo.textContent = "Fehler: Netzwerkproblem oder ungültige URL.";
- };
- };
+ }
+ }
} else if (fileType === 'image') {
// Open the gallery modal for image files.
openGalleryModal(relUrl);
- };
+ }
});
});
@@ -341,23 +355,20 @@ function reloadDirectory() {
// Force reflow to reset the animation
void reloadBtnSVG.offsetWidth;
reloadBtnSVG.classList.add("rotate");
-
- // Gradually fade out the button by reducing opacity over 500ms
- setTimeout(() => {
- reloadBtnSVG.classList.remove("rotate");
- reloadBtn.style.transition = 'opacity 0.5s ease';
- reloadBtn.style.opacity = '0';
- isReloadButtonVisible = false; // Update the visibility status.
- }, 500);
+ isReloadButtonVisible = false;
// Gradually fade back in after 10 seconds
setTimeout(() => {
+ reloadBtnSVG.classList.remove("rotate");
reloadBtn.style.transition = 'opacity 0.5s ease';
reloadBtn.style.opacity = '1';
- isReloadButtonVisible = true; // Update the visibility status.
+ reloadBtn.style.pointer = 'cursor';
+ isReloadButtonVisible = true;
}, 10000);
}
+
+
if ('mediaSession' in navigator) {
// Handler for the play action