From a722a404dcdac6ce9addeef3a8514f33e6cc64ba Mon Sep 17 00:00:00 2001 From: codey Date: Wed, 30 Jul 2025 07:58:44 -0400 Subject: [PATCH] switch to HLS streams for desktop as well --- src/components/AudioPlayer.jsx | 169 ++++++++++++++++----------------- 1 file changed, 82 insertions(+), 87 deletions(-) diff --git a/src/components/AudioPlayer.jsx b/src/components/AudioPlayer.jsx index 8b75e34..bc1d964 100644 --- a/src/components/AudioPlayer.jsx +++ b/src/components/AudioPlayer.jsx @@ -1,20 +1,18 @@ import { useState, useEffect, useRef } from "react"; -import { Howl, Howler } from "howler"; +import Hls from "hls.js"; import { metaData } from "../config"; import Play from "@mui/icons-material/PlayArrow"; import Pause from "@mui/icons-material/Pause"; import "@styles/player.css"; const API_URL = "https://api.codey.lol"; -Howler.html5PoolSize = 32; -const isIOS = /iP(ad|hone|od)/.test(navigator.userAgent); const STATIONS = { - main: { label: "Main", streamPath: "/sfm.ogg" }, - rock: { label: "Rock", streamPath: "/rock.ogg" }, - rap: { label: "Rap", streamPath: "/rap.ogg" }, - electronic: { label: "Electronic", streamPath: "/electronic.ogg" }, - classical: { label: "Classical", streamPath: "/classical.ogg" }, - pop: { label: "Pop", streamPath: "/pop.ogg" }, + main: { label: "Main" }, + rock: { label: "Rock" }, + rap: { label: "Rap" }, + electronic: { label: "Electronic" }, + classical: { label: "Classical" }, + pop: { label: "Pop" }, }; let activeInterval = null; @@ -39,7 +37,8 @@ export function Player() { const [elapsed, setElapsed] = useState(0); const [duration, setDuration] = useState(0); - const soundRef = useRef(null); + const audioRef = useRef(null); + const hlsRef = useRef(null); const uuidRef = useRef(null); const lastStationData = useRef(null); @@ -52,77 +51,67 @@ export function Player() { const progress = duration > 0 ? (elapsed / duration) * 100 : 0; const playStream = () => { - let streamPath = STATIONS[activeStation].streamPath; - if (isIOS) { - let lcStation = activeStation.toLowerCase(); - streamPath = `/hls/${lcStation}/${lcStation}.m3u8`; - console.log(`Replaced streamPath: ${streamPath}`); - } - const streamUrl = - "https://stream.codey.lol:9992" + - streamPath + - `?t=${Date.now()}`; + const lcStation = activeStation.toLowerCase(); + const streamUrl = `https://stream.codey.lol/hls/${lcStation}/${lcStation}.m3u8?t=${Date.now()}`; - if (soundRef.current) { - soundRef.current.stop(); - soundRef.current.unload(); + if (hlsRef.current) { + hlsRef.current.destroy(); + hlsRef.current = null; } - const newHowl = new Howl({ - src: [streamUrl], - html5: true, - onend: () => newHowl.play(), - onplay: () => setIsPlaying(true), - onpause: () => setIsPlaying(false), - onstop: () => setIsPlaying(false), - onloaderror: (_, err) => console.error("Load error", err), - onplayerror: (_, err) => { - console.error("Play error", err); - setTimeout(() => newHowl.play(), 1000); - }, + if (audioRef.current.canPlayType("application/vnd.apple.mpegurl")) { + // Native HLS support (Safari) + audioRef.current.src = streamUrl; + } else if (Hls.isSupported()) { + const hls = new Hls({ + maxBufferLength: 60, + }); + hls.loadSource(streamUrl); + hls.attachMedia(audioRef.current); + hlsRef.current = hls; + } else { + console.error("HLS is not supported in this browser."); + } + + audioRef.current.play().then(() => setIsPlaying(true)).catch((e) => { + console.error("Playback failed:", e); }); - - soundRef.current = newHowl; - newHowl.play(); }; -const togglePlayback = () => { - if (isPlaying) { - soundRef.current?.pause(); - setIsPlaying(false); - } else { - if (!soundRef.current) { - playStream(); + const togglePlayback = () => { + if (!audioRef.current) return; + + if (isPlaying) { + audioRef.current.pause(); + setIsPlaying(false); } else { - soundRef.current.play(); + audioRef.current.play().then(() => setIsPlaying(true)); } - setIsPlaying(true); - } -}; + }; -useEffect(() => { - if (soundRef.current) { - soundRef.current.stop(); - soundRef.current.unload(); - soundRef.current = null; - } + useEffect(() => { + if (audioRef.current) { + audioRef.current.pause(); + audioRef.current.src = ""; + setIsPlaying(false); + } - if (isPlaying) { playStream(); - } - -}, [activeStation]); - - + return () => { + if (hlsRef.current) { + hlsRef.current.destroy(); + } + }; + }, [activeStation]); useEffect(() => { clearGlobalMetadataInterval(); currentStationForInterval = activeStation; const setPageTitle = (artist, song) => { - document.title = metaData.title + ` - Radio - ${artist} - ${song} [${activeStation}]`; - } + document.title = `${metaData.title} - Radio - ${artist} - ${song} [${activeStation}]`; + }; const fetchTrackData = async () => { try { @@ -173,21 +162,17 @@ useEffect(() => { fetchTrackData(); activeInterval = setInterval(fetchTrackData, 1000); - return () => { - clearGlobalMetadataInterval(); - }; + return () => clearGlobalMetadataInterval(); }, [activeStation]); -const remaining = duration - elapsed; - -const progressColorClass = - progress >= 90 - ? "bg-red-500 dark:bg-red-400" - : progress >= 75 || remaining <= 20 - ? "bg-yellow-400 dark:bg-yellow-300" - : "bg-blue-500 dark:bg-blue-400"; - + const remaining = duration - elapsed; + const progressColorClass = + progress >= 90 + ? "bg-red-500 dark:bg-red-400" + : progress >= 75 || remaining <= 20 + ? "bg-yellow-400 dark:bg-yellow-300" + : "bg-blue-500 dark:bg-blue-400"; return ( <> @@ -211,27 +196,35 @@ const progressColorClass =
-
{trackAlbum}
- {trackAlbum +
+ {trackAlbum} +
+ {trackAlbum
+

serious.FM

{trackTitle}

{trackArtist}

{trackGenre &&

{trackGenre}

} +

{formatTime(elapsed)}

{formatTime(duration - elapsed)}

-
-
-
+ +
+
+
+
+ +
); -} \ No newline at end of file +}