diff --git a/src/lib/components/GlobalPlayer.svelte b/src/lib/components/GlobalPlayer.svelte index 51783a7..1293062 100644 --- a/src/lib/components/GlobalPlayer.svelte +++ b/src/lib/components/GlobalPlayer.svelte @@ -60,12 +60,6 @@ let audioEl: HTMLAudioElement | null = null; - // Best-effort preload of the upcoming track's URL - let nextPreloadHref = $state(null); - - // Dedicated preloader element to warm the connection / decode pipeline a bit - let preloadEl = $state(null); - let isPlaying = $state(false); let currentTime = $state(0); let duration = $state(0); @@ -110,6 +104,66 @@ prevIsMobile = nextIsMobile; } + async function waitForEvent(el: HTMLMediaElement, eventName: string) { + await new Promise((resolve) => { + const onEvent = () => { + el.removeEventListener(eventName, onEvent); + resolve(); + }; + el.addEventListener(eventName, onEvent, { once: true }); + }); + } + + async function syncAndAutoplay() { + const el = audioEl; + if (!el) return; + + // Centralized logic to sync and play the current track. + const track = snap.currentTrack; + if (!track) { + if (el.src) { + el.removeAttribute("src"); + el.load(); + } + return; + } + + const desiredSrc = track.src; + const desiredAbs = new URL(desiredSrc, window.location.href).href; + const srcChanged = el.src !== desiredAbs; + + if (srcChanged) { + el.src = desiredSrc; + el.load(); + } + + el.volume = snap.volume; + + // If src changed, we MUST wait until the browser says it's ready. + if (srcChanged || el.readyState < 3) { + try { + await Promise.race([ + waitForEvent(el, "canplay"), + // Add a timeout to prevent getting stuck forever + new Promise((_, reject) => + setTimeout(() => reject(new Error("canplay timeout")), 8000), + ), + ]); + } catch (e) { + console.error("Audio load failed or timed out", e); + return; // Don't try to play if loading failed + } + } + + // Now, try to play. + try { + await el.play(); + } catch (e) { + console.warn("Autoplay was prevented.", e); + // isPlaying state will be updated by onAudioPause handler. + } + } + // Media Session bindings const media = createMediaSessionBindings({ play: () => void audioEl?.play(), @@ -135,83 +189,6 @@ // Scrubber is now driven by `bind:currentTime` on the