feat(spf): Added autoplay support (#1880)

This commit is contained in:
Santiago Puppo
2026-07-30 14:43:33 -03:00
committed by GitHub
parent 0f4a9b4127
commit b16f28fb74
2 changed files with 33 additions and 6 deletions
@@ -21,9 +21,18 @@ function setupTrackLoadTriggers(initialState: LoadTriggersState = {}, initialCon
return { state, context, reactor };
}
/** Creates a video element with controllable `paused` and `seeking` state. */
function makeMediaElement({ paused = true, seeking = false }: { paused?: boolean; seeking?: boolean } = {}) {
/** Creates a video element with controllable `paused`, `seeking`, and `autoplay` state. */
function makeMediaElement({
paused = true,
seeking = false,
autoplay = false,
}: {
paused?: boolean;
seeking?: boolean;
autoplay?: boolean;
} = {}) {
const el = document.createElement('video');
el.autoplay = autoplay;
let pausedFlag = paused;
let seekingFlag = seeking;
Object.defineProperty(el, 'paused', { get: () => pausedFlag, configurable: true });
@@ -104,6 +113,24 @@ describe('trackLoadTriggers', () => {
reactor.destroy();
});
it('sets loadActivated to true immediately if element has autoplay on entry', async () => {
const { el } = makeMediaElement({ autoplay: true });
const addEventListenerSpy = vi.spyOn(el, 'addEventListener');
const { state, reactor } = setupTrackLoadTriggers(
{ presentation: { url: 'http://example.com/stream.m3u8' } },
{ mediaElement: el }
);
await flush();
// Autoplay commits to loading up front (it can't wait for a `play` event
// that preload='none' would never let fire), so it activates on entry and
// never attaches play/seeking listeners.
expect(state.loadActivated.get()).toBe(true);
expect(addEventListenerSpy.mock.calls.some(([type]) => type === 'play' || type === 'seeking')).toBe(false);
reactor.destroy();
});
it('preserves pre-existing true write (adapter wrote before setup)', async () => {
const { el } = makeMediaElement();
const addEventListenerSpy = vi.spyOn(el, 'addEventListener');
@@ -66,9 +66,9 @@ function deriveState(
*
* Writes `state.loadActivated = true` the first time a `play` or `seeking`
* event fires on the attached media element for the current source — or
* immediately on entry if the element is already in such a state
* (`!el.paused` or `el.seeking`), mirroring autoplay / native-controls /
* direct-DOM-`play()` scenarios.
* immediately on entry if the element is already committed to loading
* (`el.autoplay`, `!el.paused`, or `el.seeking`), covering autoplay,
* native-controls, and direct-DOM-`play()` scenarios.
*
* Sticky-true *within a source identity*: subsequent play/pause/seek
* cycles don't flip back. Source identity = (mediaElement, presentation
@@ -125,7 +125,7 @@ function trackLoadTriggersSetup({
const el = context.mediaElement.get()!;
const setLoadActivated = () => state.loadActivated.set(true);
if (!el.paused || el.seeking) {
if (el.autoplay || !el.paused || el.seeking) {
setLoadActivated();
return;
}