diff --git a/web/src/routes/+layout.svelte b/web/src/routes/+layout.svelte index 9ea00a2c..d1e22e3d 100644 --- a/web/src/routes/+layout.svelte +++ b/web/src/routes/+layout.svelte @@ -6,8 +6,17 @@ import { queryClient } from '$lib/query/client'; import { user } from '$lib/auth/store.svelte'; import Shell from '$lib/components/Shell.svelte'; + import { + registerAudioEl, + reportTimeUpdate, + reportDuration, + reportStateFromAudio, + player + } from '$lib/player/store.svelte'; + import { useMediaSession } from '$lib/player/mediaSession.svelte'; let { children } = $props<{ children: import('svelte').Snippet }>(); + let audioEl: HTMLAudioElement | undefined = $state(); // Reactive guard: runs every time page.url or user.value changes. $effect(() => { @@ -20,8 +29,52 @@ goto('/', { replaceState: true }); } }); + + $effect(() => { + if (audioEl) registerAudioEl(audioEl); + return () => registerAudioEl(null); + }); + + $effect(() => { + if (!audioEl) return; + const src = player.current?.stream_url; + if (src) { + const absolute = new URL(src, window.location.origin).href; + if (audioEl.src !== absolute) audioEl.src = src; + } else { + audioEl.removeAttribute('src'); + audioEl.load(); + } + }); + + $effect(() => { + if (audioEl) audioEl.volume = player.volume; + }); + + $effect(() => { + if (!audioEl) return; + if (player.isPlaying) { + audioEl.play().catch(() => { /* interrupted / blocked — store gets the event */ }); + } else { + audioEl.pause(); + } + }); + + useMediaSession(); + + {#if user.value !== null && page.url.pathname !== '/login'} {@render children()}