Skip to content

Instantly share code, notes, and snippets.

@simonespa
Created March 31, 2025 18:39
Show Gist options
  • Save simonespa/16cc9f87b4629dbc1c65e212670aed64 to your computer and use it in GitHub Desktop.
Save simonespa/16cc9f87b4629dbc1c65e212670aed64 to your computer and use it in GitHub Desktop.
Dash.js player with react
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} />
</>
);
}
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