Files
Shadowbroker/frontend/src/components/HlsVideo.tsx
T
BigBodyCobain e1060193d0 Improve v0.9.7 startup and runtime reliability
Prioritize cached first-paint data, defer heavyweight feed synthesis, make MeshChat activation explicit, improve CCTV media handling, and tighten desktop runtime packaging filters.
2026-05-02 17:31:54 -06:00

74 lines
1.9 KiB
TypeScript

'use client';
import { useEffect, useRef, forwardRef, useImperativeHandle } from 'react';
export interface HlsVideoHandle {
play(): void;
pause(): void;
get paused(): boolean;
}
const HlsVideo = forwardRef<
HlsVideoHandle,
{ url: string; className?: string; onError?: () => void; onLoaded?: () => void }
>(
({ url, className, onError, onLoaded }, ref) => {
const videoRef = useRef<HTMLVideoElement>(null);
useImperativeHandle(ref, () => ({
play: () => videoRef.current?.play(),
pause: () => videoRef.current?.pause(),
get paused() {
return videoRef.current?.paused ?? true;
},
}));
useEffect(() => {
const video = videoRef.current;
if (!video || !url) return;
let hlsInstance: { destroy(): void } | null = null;
let cancelled = false;
(async () => {
const { default: Hls } = await import('hls.js');
if (cancelled) return;
if (Hls.isSupported()) {
const hls = new Hls({ enableWorker: false, lowLatencyMode: true });
hls.on(Hls.Events.ERROR, (_e: unknown, data: { fatal?: boolean }) => {
if (data.fatal) onError?.();
});
hls.on(Hls.Events.MANIFEST_PARSED, () => onLoaded?.());
hls.loadSource(url);
hls.attachMedia(video);
hlsInstance = hls;
} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
video.src = url;
}
})();
return () => {
cancelled = true;
hlsInstance?.destroy();
};
}, [url, onError, onLoaded]);
return (
<video
ref={videoRef}
autoPlay
muted
playsInline
onError={() => onError?.()}
onCanPlay={() => onLoaded?.()}
onLoadedData={() => onLoaded?.()}
onPlaying={() => onLoaded?.()}
className={className}
/>
);
},
);
HlsVideo.displayName = 'HlsVideo';
export default HlsVideo;