feat: add hls.js for m3u8 stream playback

This commit is contained in:
2026-06-14 21:37:55 +02:00
parent c891382efb
commit f9f3322797
3 changed files with 39 additions and 6 deletions

View File

@@ -1,25 +1,54 @@
import Hls from "hls.js";
import { state } from "./state";
import { absoluteTimeFromDisplay, displayTimeFromAbsolute, invalidateBounds } from "./timeline";
let hls: Hls | null = null;
const destroyHLS = (): void => {
hls?.destroy();
hls = null;
};
export const destroyVideoSource = (): void => {
destroyHLS();
state.video.pause();
state.video.removeAttribute("src");
state.video.load();
};
const shouldUseHLS = (type: string | undefined, url: string): boolean => {
if (type === "m3u8") return true;
try {
const parsed = new URL(url, window.location.href);
return parsed.pathname.toLowerCase().endsWith(".m3u8");
} catch {
return url.toLowerCase().includes(".m3u8");
}
};
/**
* Force-loads a new video source and preserves playback position.
*
* Some browsers can be flaky when switching between HLS URLs while playing.
* Clearing `src` first ensures the media element fully resets before the new URL is set.
*/
export const loadVideoSource = (url: string): void => {
export const loadVideoSource = (url: string, type?: string): void => {
if (!url) return;
const wasPlaying = !state.video.paused;
const prevDisplayTime = displayTimeFromAbsolute(state.video.currentTime);
// Fully reset the element before setting a new source.
state.video.pause();
state.video.removeAttribute("src");
state.video.load();
destroyVideoSource();
state.video.src = url;
state.video.load();
if (shouldUseHLS(type, url) && Hls.isSupported()) {
hls = new Hls();
hls.loadSource(url);
hls.attachMedia(state.video);
} else {
state.video.src = url;
state.video.load();
}
// Try an eager seek; if metadata isn't ready yet, main.ts will restore via pendingSeekTime.
state.pendingSeekTime = prevDisplayTime;