Files
v10/apps/sandbox/templates/live-hls-engine/main.ts
T
Christian PillsburyandClaude Opus 4.8 4a58e85015 feat(spf): export ./live-hls + sandbox live-engine harness
Add the ./live-hls subpath export (build entry + package export) and a
LiveHlsEngineSignals type. Add a bare sandbox template (live-hls-engine) that
wires createLiveHlsEngine to a raw <video> — no player/skin — and logs playback
state, for end-to-end live-playback verification.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 09:59:21 -07:00

66 lines
2.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Bare harness for the experimental SPF live HLS engine — wires
* `createLiveHlsEngine` to a raw <video> (no player/skin) and logs playback
* state, to validate live CMAF/LL-HLS playback end-to-end.
*/
import { createLiveHlsEngine, type LiveHlsEngineSignals } from '@videojs/spf/live-hls';
const SRC = 'https://stream.mux.com/1DRguGQyA2K2TIelbV7rU7uePlZXHyYWR1LEMC8iTC4.m3u8';
const video = document.getElementById('video') as HTMLVideoElement;
const logEl = document.getElementById('log')!;
const stateEl = document.getElementById('state')!;
const log = (msg: string, cls = '') => {
const line = document.createElement('div');
if (cls) line.className = cls;
line.textContent = `${new Date().toISOString().slice(11, 23)} ${msg}`;
logEl.prepend(line);
};
const buffered = () =>
video.buffered.length
? `${video.buffered.start(0).toFixed(2)}${video.buffered.end(video.buffered.length - 1).toFixed(2)}`
: 'none';
function renderState() {
const rows: [string, string][] = [
['paused', String(video.paused)],
['currentTime', video.currentTime.toFixed(2)],
['readyState', String(video.readyState)],
['buffered', buffered()],
['duration', String(video.duration)],
['error', video.error ? `${video.error.code}` : 'none'],
];
stateEl.innerHTML = rows.map(([k, v]) => `<div class="state-key">${k}</div><div class="val">${v}</div>`).join('');
}
setInterval(renderState, 500);
for (const ev of ['loadedmetadata', 'durationchange', 'canplay', 'playing', 'waiting', 'stalled', 'seeked', 'ended']) {
video.addEventListener(ev, () =>
log(`${ev} t=${video.currentTime.toFixed(2)} buffered=${buffered()} ready=${video.readyState}`)
);
}
video.addEventListener('error', () => log(`video error: ${video.error?.code}`, 'err'));
let signals: LiveHlsEngineSignals | undefined;
const engine = createLiveHlsEngine({
onSignalsReady: (refs) => {
signals = refs;
},
});
if (!signals) throw new Error('live engine signals not ready');
video.preload = 'auto';
signals.context.mediaElement.set(video);
signals.state.presentation.set({ url: SRC });
log(`engine wired; presentation = ${SRC}`);
video.play().then(
() => log('play() resolved'),
(e) => log(`play() rejected: ${e}`, 'err')
);
// Expose for ad-hoc debugging from the console / Playwright.
Object.assign(window as unknown as Record<string, unknown>, { __live: { engine, signals, video } });