diff --git a/src/components/AudioPlayer.jsx b/src/components/AudioPlayer.jsx index 4acc8f9..9d3ec17 100644 --- a/src/components/AudioPlayer.jsx +++ b/src/components/AudioPlayer.jsx @@ -54,7 +54,7 @@ export function Player() { const lcStation = activeStation.toLowerCase(); const streamUrl = `https://stream.codey.lol/hls/${lcStation}/${lcStation}.m3u8?t=${Date.now()}`; - // Cleanup existing stream + // Cleanup if (hlsRef.current) { hlsRef.current.destroy(); hlsRef.current = null; @@ -64,37 +64,78 @@ export function Player() { if (!audio) return; if (audio.canPlayType("application/vnd.apple.mpegurl")) { - // Native support (Safari) audio.src = streamUrl; + audio.load(); audio.play().then(() => setIsPlaying(true)).catch(console.error); - } else if (Hls.isSupported()) { - const hls = new Hls({ - maxBufferLength: 30, - maxMaxBufferLength: 60, - abrEwmaFastLive: 2.0, - abrEwmaSlowLive: 6.0, - abrBandWidthFactor: 0.95, // Bias toward higher quality - autoStartLoad: true, - startLevel: -1, // adaptive start - }); - - hls.loadSource(streamUrl); - hls.attachMedia(audio); - - hls.on(Hls.Events.ERROR, (_, data) => { - if (data.fatal) { - console.error("HLS fatal error:", data); - hls.destroy(); - } - }); - - hlsRef.current = hls; - audio.play().then(() => setIsPlaying(true)).catch(console.error); - } else { - console.error("HLS not supported"); + return; } + + if (!Hls.isSupported()) { + console.error("HLS not supported"); + return; + } + + const hls = new Hls({ + maxBufferLength: 30, + maxMaxBufferLength: 60, + autoStartLoad: true, + startLevel: -1, + }); + + hls.attachMedia(audio); + + hls.on(Hls.Events.MEDIA_ATTACHED, () => { + hls.loadSource(streamUrl); + }); + + hls.on(Hls.Events.MANIFEST_PARSED, () => { + audio.play().then(() => setIsPlaying(true)).catch(console.error); + }); + + hls.on(Hls.Events.ERROR, (event, data) => { + console.warn("HLS error:", data); + + if (data.fatal) { + switch (data.type) { + case Hls.ErrorTypes.NETWORK_ERROR: + case Hls.ErrorTypes.MEDIA_ERROR: + case Hls.ErrorTypes.OTHER_ERROR: + console.error("Fatal HLS error, attempting recovery…"); + try { + hls.destroy(); + hlsRef.current = null; + setIsPlaying(false); + // Delay before trying to reconnect + setTimeout(() => { + playStream(); + }, 3000); + } catch (e) { + console.error("HLS recovery failed:", e); + } + break; + default: + hls.destroy(); + hlsRef.current = null; + setIsPlaying(false); + } + } + }); + + hlsRef.current = hls; }; + useEffect(() => { + const interval = setInterval(() => { + const audio = audioRef.current; + if (audio && isPlaying && audio.readyState < 2) { + console.warn("Playback seems stalled. Reloading stream."); + playStream(); + } + }, 15000); // Check every 15 seconds + + return () => clearInterval(interval); +}, [isPlaying]); + const togglePlayback = () => { const audio = audioRef.current; if (!audio) return;