const playerButton = document.querySelector('.player-button'), audio = document.querySelector('audio'), timeline = document.querySelector('.timeline'), timeInfo = document.getElementById('timeInfo'), playIcon = ` `, pauseIcon = ` `, soundIcon = ` ` function toggleAudio () { if (audio.paused) { audio.play(); playerButton.innerHTML = pauseIcon; } else { audio.pause(); playerButton.innerHTML = playIcon; } } playerButton.addEventListener('click', toggleAudio); let isSeeking = false; // --- Slider (Timeline) events --- // Mouse events timeline.addEventListener('mousedown', () => { isSeeking = true; }); timeline.addEventListener('mouseup', () => { isSeeking = false; changeSeek(); // Update the audio currentTime based on the slider position }); // Touch events timeline.addEventListener('touchstart', () => { isSeeking = true; }); timeline.addEventListener('touchend', () => { isSeeking = false; changeSeek(); }); timeline.addEventListener('touchcancel', () => { isSeeking = false; }); // --- When metadata is loaded, set slider range in seconds --- audio.addEventListener('loadedmetadata', () => { timeline.min = 0; timeline.max = audio.duration; }); // --- Seek function: directly set audio.currentTime using slider's value (in seconds) --- function changeSeek() { audio.currentTime = timeline.value; } // --- Utility: Format seconds as mm:ss --- function formatTime(seconds) { const minutes = Math.floor(seconds / 60); const secs = Math.floor(seconds % 60); return `${minutes < 10 ? '0' : ''}${minutes}:${secs < 10 ? '0' : ''}${secs}`; } // --- Update timeline, time info, and media session on each time update --- audio.ontimeupdate = function() { // --- Update the slider thumb position (in seconds) while playing --- if (!isSeeking) { timeline.value = audio.currentTime; const percentagePosition = (audio.currentTime / audio.duration) * 100; timeline.style.backgroundSize = `${percentagePosition}% 100%`; } // --- Update the time display --- const currentTimeFormatted = formatTime(audio.currentTime); const durationFormatted = isNaN(audio.duration) ? "00:00" : formatTime(audio.duration); timeInfo.innerHTML = `${currentTimeFormatted} / ${durationFormatted}`; if ('mediaSession' in navigator && 'setPositionState' in navigator.mediaSession && !isNaN(audio.duration)) { navigator.mediaSession.setPositionState({ duration: audio.duration, playbackRate: audio.playbackRate, position: audio.currentTime }); } }; // --- When audio ends --- audio.onended = function() { playerButton.innerHTML = playIcon; }; async function downloadAudio() { const src = audio.currentSrc || audio.src; if (!src) return; // Build a fresh URL every time const requestUrl = new URL(src, window.location.href); requestUrl.searchParams.set('_', Date.now()); // Force network fetch and include same‑origin credentials const response = await fetch(requestUrl.toString(), { credentials: 'same-origin', cache: 'no-store' }); if (!response.ok) { throw new Error(`Download failed: ${response.status}`); } // Turn the response into a blob, then download it const blob = await response.blob(); const blobUrl = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = blobUrl; // Retain original filename if possible a.download = decodeURIComponent(src.split('/').pop() || 'audio'); document.body.appendChild(a); a.click(); document.body.removeChild(a); // Cleanup URL.revokeObjectURL(blobUrl); }