From 0f045476918d65ef758e0aa506328ca9d8cfca8e Mon Sep 17 00:00:00 2001 From: Bilawal Sidhu Date: Tue, 15 Sep 2026 11:24:10 -0500 Subject: [PATCH] fix(cctv): preserve looping for finite video feeds --- src/layers/cctv/videoPlayback.js | 2 ++ src/layers/cctv/videoPlayback.test.mjs | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/src/layers/cctv/videoPlayback.js b/src/layers/cctv/videoPlayback.js index 305d8e7fd..d59143b5a 100644 --- a/src/layers/cctv/videoPlayback.js +++ b/src/layers/cctv/videoPlayback.js @@ -9,6 +9,8 @@ export function attachCctvVideo( fetchImpl = globalThis.fetch, } = {}, ) { + // Preserve replay for existing finite video feeds; live HLS must not loop. + video.loop = feedType !== 'hls'; let disposed = false; let hls = null; const leaseId = feedType === 'hls' ? globalThis.crypto.randomUUID() : null; diff --git a/src/layers/cctv/videoPlayback.test.mjs b/src/layers/cctv/videoPlayback.test.mjs index af6f2b5ac..bc93f9ac4 100644 --- a/src/layers/cctv/videoPlayback.test.mjs +++ b/src/layers/cctv/videoPlayback.test.mjs @@ -68,3 +68,26 @@ test('native HLS releases its client lease without response-header access', asyn assert.equal(releases[0].url, requested); assert.equal(releases[0].init.method, 'DELETE'); }); + +test('finite video feeds retain looping while live HLS does not loop', async () => { + for (const feedType of ['mp4', 'webm', 'hls']) { + const source = video(); + source.loop = true; + source.canPlayType = () => 'probably'; + let imports = 0; + const playback = attachCctvVideo(source, '/api/cctv/media/a', feedType, { + loadHls: async () => { + imports++; + return { default: { isSupported: () => false } }; + }, + fetchImpl: async () => {}, + }); + try { + await playback.ready; + assert.equal(source.loop, feedType !== 'hls'); + assert.equal(imports, feedType === 'hls' ? 1 : 0); + } finally { + playback.dispose(); + } + } +});