From d68698fdb792fa44658a0dd3c22075b61fe2620f Mon Sep 17 00:00:00 2001 From: Christian Pillsbury Date: Wed, 15 Jul 2026 13:56:05 -0700 Subject: [PATCH] refactor(spf): fold getMax/getMinBufferedEnd into one reduce base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Near-identical loops differing only by comparator → a shared getBufferedEnd(buffers, isEndMatch). getMinBufferedEnd drops its abort-on-empty→undefined path (unused: recover-end-stall consumes it only behind an msEnded guard, where every buffer has ranges), so it skips empty buffers and floors at 0 like getMaxBufferedEnd. Tests updated to match. Co-Authored-By: Claude Opus 4.8 (1M context) --- packages/spf/src/media/dom/mse/duration.ts | 45 +++++++++---------- .../src/media/dom/mse/tests/duration.test.ts | 8 ++-- 2 files changed, 24 insertions(+), 29 deletions(-) diff --git a/packages/spf/src/media/dom/mse/duration.ts b/packages/spf/src/media/dom/mse/duration.ts index 89a5ee0d..6590bcf5 100644 --- a/packages/spf/src/media/dom/mse/duration.ts +++ b/packages/spf/src/media/dom/mse/duration.ts @@ -34,25 +34,31 @@ export function canUpdateDuration( return !!(mediaSource && presentation && hasPresentationDuration(presentation)); } +export function getBufferedEnd( + buffers: SourceBufferIterable, + isEndMatch: (next: number, current: number) => boolean +): number { + return ( + ([...buffers].reduce((endMatch: number | undefined, buffer) => { + const { buffered } = buffer; + if (!buffered.length) return endMatch; + const end = buffered.end(buffered.length - 1); + if (!endMatch) return end; + return isEndMatch(end, endMatch) ? end : endMatch; + }, undefined) as number) ?? 0 + ); +} + +const isGreaterThan = (x: number, y: number) => x > y; +const isLessThan = (x: number, y: number) => x < y; + /** * Get the maximum buffered end time across an iterable of SourceBuffers * (typically `mediaSource.sourceBuffers`). Returns `0` when the collection is * empty or no buffer has any buffered ranges. */ export function getMaxBufferedEnd(buffers: SourceBufferIterable): number { - let maxEnd = 0; - - for (const buffer of buffers) { - const { buffered } = buffer; - if (buffered.length > 0) { - const end = buffered.end(buffered.length - 1); - if (end > maxEnd) { - maxEnd = end; - } - } - } - - return maxEnd; + return getBufferedEnd(buffers, isGreaterThan); } /** @@ -67,18 +73,7 @@ export function getMaxBufferedEnd(buffers: SourceBufferIterable): number { * when tracks end at slightly different times (e.g. skewed A/V near end-of-stream). */ export function getMinBufferedEnd(buffers: SourceBufferIterable): number | undefined { - let minEnd: number | undefined; - - for (const buffer of buffers) { - const { buffered } = buffer; - if (buffered.length === 0) return undefined; - const end = buffered.end(buffered.length - 1); - if (minEnd === undefined || end < minEnd) { - minEnd = end; - } - } - - return minEnd; + return getBufferedEnd(buffers, isLessThan); } /** diff --git a/packages/spf/src/media/dom/mse/tests/duration.test.ts b/packages/spf/src/media/dom/mse/tests/duration.test.ts index fdbcde7a..04da4bbd 100644 --- a/packages/spf/src/media/dom/mse/tests/duration.test.ts +++ b/packages/spf/src/media/dom/mse/tests/duration.test.ts @@ -138,8 +138,8 @@ describe('getMaxBufferedEnd', () => { }); describe('getMinBufferedEnd', () => { - it('returns undefined when the buffer list is empty', () => { - expect(getMinBufferedEnd([])).toBeUndefined(); + it('returns 0 when the buffer list is empty', () => { + expect(getMinBufferedEnd([])).toBe(0); }); it('returns the min last-range end across buffers (the reachable/intersection end)', () => { @@ -154,7 +154,7 @@ describe('getMinBufferedEnd', () => { expect(getMinBufferedEnd([video, audio])).toBe(600.0); }); - it('returns undefined when any buffer has no buffered ranges (no common reachable point)', () => { + it('skips buffers with no ranges (min across the buffers that have ranges)', () => { const empty = { buffered: { length: 0, start: () => 0, end: () => 0 } as TimeRanges, } as unknown as SourceBuffer; @@ -162,7 +162,7 @@ describe('getMinBufferedEnd', () => { buffered: { length: 1, start: () => 0, end: () => 30 } as TimeRanges, } as unknown as SourceBuffer; - expect(getMinBufferedEnd([empty, buffered])).toBeUndefined(); + expect(getMinBufferedEnd([empty, buffered])).toBe(30); }); it('uses the last range end when a buffer has multiple (gapped) ranges', () => {