commit 96ad20dc64f168cc4ca2b0d052fd48023caf4959
parent f98908d2ef3a505b1ffbb41b3711dc2102f9ab33
Author: Hunter
Date:   Tue, 28 Oct 2025 14:41:30 -0400

handle seeking in unbuffered tracks

Diffstat:
Mscript.js | 108+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 103 insertions(+), 5 deletions(-)

diff --git a/script.js b/script.js @@ -57,8 +57,8 @@ audio.addEventListener('play', () => { audio.addEventListener('pause', () => { stopProgressBar(); - // Only show "Paused" if the user actually paused (not during track transitions) - if (!audio.ended) { + // Only show "Paused" if the user actually paused (not during track transitions or seeking) + if (!audio.ended && !isSeeking) { const song = songs[currentSongIndex]; updateCurrentSongDisplay(`Paused: ${song.artist} - ${song.title}`); } @@ -233,7 +233,7 @@ function resetProgressBar() { } function updateProgressBar() { - if (audio.duration && !isDragging) { + if (audio.duration && !isDragging && !isSeeking) { const currentTime = audio.currentTime; const duration = audio.duration; const progressPercentage = (currentTime / duration) * 100; @@ -259,6 +259,9 @@ function updateVisualProgress(event) { return clickPercentage; } +let isSeeking = false; +let targetSeekTime = null; + function applySeek(clickPercentage) { if (!playerReady) return; @@ -271,18 +274,113 @@ function applySeek(clickPercentage) { audio.addEventListener('loadedmetadata', function setInitialTime() { const duration = audio.duration; const seekTime = duration * clickPercentage; - audio.currentTime = seekTime; + attemptSeekWithRetry(seekTime, clickPercentage); prePlaySeekTime = seekTime; audio.removeEventListener('loadedmetadata', setInitialTime); }, { once: true }); } else if (audio.duration) { const duration = audio.duration; const seekTime = duration * clickPercentage; - audio.currentTime = seekTime; + attemptSeekWithRetry(seekTime, clickPercentage); prePlaySeekTime = seekTime; } } +function isTimeBuffered(time) { + // Check if the given time is within any buffered time range + for (let i = 0; i < audio.buffered.length; i++) { + if (time >= audio.buffered.start(i) && time <= audio.buffered.end(i)) { + return true; + } + } + return false; +} + +function attemptSeekWithRetry(seekTime, targetPercentage) { + targetSeekTime = seekTime; + isSeeking = true; + + // Lock the progress bar at the target position + progressBar.style.setProperty('--progress', targetPercentage * 100); + + const wasPlaying = !audio.paused; + + // Try to seek + audio.currentTime = seekTime; + + // Handler to check if we reached the target after seeking completes + function checkSeekSuccess() { + // Allow small tolerance for floating point comparison + if (Math.abs(audio.currentTime - targetSeekTime) > 0.5) { + // Browser clamped to buffered range - need to wait for more data + // Now pause and show seeking status + if (wasPlaying) { + audio.pause(); + } + const song = songs[currentSongIndex]; + updateCurrentSongDisplay(`Seeking: ${song.artist} - ${song.title}`); + continueSeekingToTarget(wasPlaying); + } else { + // Successfully reached target immediately (was already buffered) + isSeeking = false; + targetSeekTime = null; + // No need to update display - the seek was instant and playback continues normally + } + } + + audio.addEventListener('seeked', checkSeekSuccess, { once: true }); +} + +function continueSeekingToTarget(wasPlaying) { + const song = songs[currentSongIndex]; + + // Keep showing seeking status + updateCurrentSongDisplay(`Seeking: ${song.artist} - ${song.title}`); + + // Handler for when more data loads + function retrySeek() { + if (!isSeeking || targetSeekTime === null) { + return; // Seeking was cancelled + } + + audio.currentTime = targetSeekTime; + + // Check again after this seek completes + function checkAgain() { + if (!isSeeking || targetSeekTime === null) { + return; + } + + if (Math.abs(audio.currentTime - targetSeekTime) > 0.5) { + // Still not there, keep trying + continueSeekingToTarget(wasPlaying); + } else { + // Success! + isSeeking = false; + targetSeekTime = null; + + if (wasPlaying) { + audio.play(); + } else { + updateCurrentSongDisplay(`Paused: ${song.artist} - ${song.title}`); + } + } + } + + audio.addEventListener('seeked', checkAgain, { once: true }); + } + + // Wait for more data to load, then try again + audio.addEventListener('progress', retrySeek, { once: true }); + + // Also set a timeout fallback in case progress doesn't fire + setTimeout(() => { + if (isSeeking && targetSeekTime !== null && Math.abs(audio.currentTime - targetSeekTime) > 0.5) { + retrySeek(); + } + }, 1000); +} + function onProgressMouseDown(event) { if (!playerReady) return; isDragging = true;