Created
March 31, 2025 18:39
-
-
Save simonespa/16cc9f87b4629dbc1c65e212670aed64 to your computer and use it in GitHub Desktop.
Dash.js player with react
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
SyntheticEvent, | |
useCallback, | |
useEffect, | |
useRef, | |
useState, | |
} from "react"; | |
import RadioList from "@/ui/radio-list"; | |
interface Dashjs { | |
MediaPlayer: () => MediaPlayerFactory; | |
} | |
interface MediaPlayerFactory { | |
create: () => MediaPlayer; | |
} | |
interface MediaPlayer { | |
initialize: ( | |
audio: HTMLElement | null, | |
url: string, | |
autoStart: boolean | |
) => object; | |
destroy: () => void; | |
} | |
declare const dashjs: Dashjs; | |
const AKAMAI = | |
"https://a.files.bbci.co.uk/ms6/live/3441A116-B12E-4D2F-ACA8-C1984642FA4B/audio/simulcast/dash/uk/pc_hd_abr_v2/aks/{SID}.mpd"; | |
export default function Home() { | |
const [url, setUrl] = useState<string | undefined>(undefined); | |
const audioRef = useRef<HTMLAudioElement | null>(null); | |
const playerRef = useRef<MediaPlayer | null>(null); | |
/** | |
* Initialize the dash.js player | |
* @description This effect runs when the component mounts and when the URL changes. | |
* It creates a new dash.js player instance and initializes it with the audio element and the URL. | |
* The effect cleans up by destroying the player instance when the component unmounts. | |
* @see {@link https://reactjs.org/docs/hooks-reference.html#useeffect} | |
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLAudioElement} | |
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLAudioElement/Audio} | |
*/ | |
useEffect(() => { | |
if (!playerRef.current) { | |
try { | |
playerRef.current = dashjs.MediaPlayer().create(); | |
} catch (error) { | |
console.error("Failed to initialize dash.js player:", error); | |
} | |
} | |
if (url && audioRef.current) { | |
try { | |
playerRef.current?.initialize(audioRef.current, url, true); | |
} catch (error) { | |
console.error("Failed to initialize media player:", error); | |
} | |
} | |
return () => { | |
try { | |
if (playerRef.current) { | |
playerRef.current.destroy(); | |
playerRef.current = null; | |
} | |
} catch (error) { | |
console.error("Failed to destroy dash.js player:", error); | |
} | |
}; | |
}, [url]); | |
/** | |
* Handle click event on radio list items | |
* @param {SyntheticEvent} e - The click event | |
* @returns {void} | |
* @description This function prevents the default action and stops the event from propagating. | |
* It retrieves the ID of the clicked radio station from the data attribute and sets the URL for the media player. | |
* The URL is constructed using the Akamai URL template and the station ID. | |
* The media player is then initialized with the new URL. | |
* The function is memoized using useCallback to prevent unnecessary re-renders. | |
* @see {@link https://reactjs.org/docs/hooks-reference.html#usecallback} | |
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault} | |
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation} | |
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement/dataset} | |
*/ | |
const handleClick = useCallback((e: SyntheticEvent): void => { | |
e.preventDefault(); | |
e.stopPropagation(); | |
const target = e.target as HTMLAnchorElement; | |
const id = target.dataset.id; | |
if (id) { | |
setUrl(AKAMAI.replace("{SID}", id)); | |
} else { | |
console.warn("No data-id found on the clicked element."); | |
} | |
}, []); | |
return ( | |
<> | |
<RadioList onClick={handleClick} /> | |
</> | |
); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { SyntheticEvent } from "react"; | |
const radioStations = [ | |
{ id: "bbc_radio_one", label: "BBC Radio 1" }, | |
{ id: "bbc_radio_one_anthems", label: "BBC Radio 1 Anthems" }, | |
{ id: "bbc_radio_one_dance", label: "BBC Radio 1 Dance" }, | |
{ id: "bbc_1xtra", label: "BBC Radio 1Xtra" }, | |
{ id: "bbc_radio_two", label: "BBC Radio 2" }, | |
{ id: "bbc_6music", label: "BBC Radio 6 Music" }, | |
{ id: "bbc_london", label: "BBC Radio London" }, | |
{ id: "bbc_world_service", label: "BBC World Service" }, | |
{ id: "bbc_sounds_news", label: "BBC Live News" }, | |
]; | |
export default function RadioList({ | |
onClick, | |
}: { | |
onClick: (e: SyntheticEvent) => void; | |
}) { | |
return ( | |
<ul id="radio-list" role="list" onClick={onClick}> | |
{radioStations.map((station) => ( | |
<li key={station.id} role="listitem"> | |
<a | |
data-id={station.id} | |
href="#" | |
aria-label={station.label} | |
role="button" | |
tabIndex={0} | |
onKeyDown={(e) => { | |
if (e.key === "Enter" || e.key === " ") { | |
e.preventDefault(); | |
onClick(e); | |
} | |
}} | |
> | |
{station.label} | |
</a> | |
</li> | |
))} | |
</ul> | |
); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment