@editframe/elements 0.26.3-beta.0 → 0.26.4-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/package.json +2 -2
  2. package/scripts/build-css.js +3 -3
  3. package/tsdown.config.ts +1 -1
  4. package/src/elements/ContextProxiesController.ts +0 -124
  5. package/src/elements/CrossUpdateController.ts +0 -22
  6. package/src/elements/EFAudio.browsertest.ts +0 -706
  7. package/src/elements/EFAudio.ts +0 -56
  8. package/src/elements/EFCaptions.browsertest.ts +0 -1960
  9. package/src/elements/EFCaptions.ts +0 -823
  10. package/src/elements/EFImage.browsertest.ts +0 -120
  11. package/src/elements/EFImage.ts +0 -113
  12. package/src/elements/EFMedia/AssetIdMediaEngine.test.ts +0 -224
  13. package/src/elements/EFMedia/AssetIdMediaEngine.ts +0 -110
  14. package/src/elements/EFMedia/AssetMediaEngine.browsertest.ts +0 -140
  15. package/src/elements/EFMedia/AssetMediaEngine.ts +0 -385
  16. package/src/elements/EFMedia/BaseMediaEngine.browsertest.ts +0 -400
  17. package/src/elements/EFMedia/BaseMediaEngine.ts +0 -505
  18. package/src/elements/EFMedia/BufferedSeekingInput.browsertest.ts +0 -386
  19. package/src/elements/EFMedia/BufferedSeekingInput.ts +0 -430
  20. package/src/elements/EFMedia/JitMediaEngine.browsertest.ts +0 -226
  21. package/src/elements/EFMedia/JitMediaEngine.ts +0 -256
  22. package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.browsertest.ts +0 -679
  23. package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.ts +0 -117
  24. package/src/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.ts +0 -246
  25. package/src/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.browsertest.ts +0 -59
  26. package/src/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.ts +0 -27
  27. package/src/elements/EFMedia/audioTasks/makeAudioInputTask.browsertest.ts +0 -55
  28. package/src/elements/EFMedia/audioTasks/makeAudioInputTask.ts +0 -53
  29. package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.chunkboundary.regression.browsertest.ts +0 -207
  30. package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.ts +0 -72
  31. package/src/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.ts +0 -32
  32. package/src/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.ts +0 -29
  33. package/src/elements/EFMedia/audioTasks/makeAudioTasksVideoOnly.browsertest.ts +0 -95
  34. package/src/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.ts +0 -184
  35. package/src/elements/EFMedia/shared/AudioSpanUtils.ts +0 -129
  36. package/src/elements/EFMedia/shared/BufferUtils.ts +0 -342
  37. package/src/elements/EFMedia/shared/GlobalInputCache.ts +0 -77
  38. package/src/elements/EFMedia/shared/MediaTaskUtils.ts +0 -44
  39. package/src/elements/EFMedia/shared/PrecisionUtils.ts +0 -46
  40. package/src/elements/EFMedia/shared/RenditionHelpers.browsertest.ts +0 -246
  41. package/src/elements/EFMedia/shared/RenditionHelpers.ts +0 -56
  42. package/src/elements/EFMedia/shared/ThumbnailExtractor.ts +0 -227
  43. package/src/elements/EFMedia/tasks/makeMediaEngineTask.browsertest.ts +0 -167
  44. package/src/elements/EFMedia/tasks/makeMediaEngineTask.ts +0 -88
  45. package/src/elements/EFMedia/videoTasks/MainVideoInputCache.ts +0 -76
  46. package/src/elements/EFMedia/videoTasks/ScrubInputCache.ts +0 -61
  47. package/src/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.ts +0 -114
  48. package/src/elements/EFMedia/videoTasks/makeScrubVideoInitSegmentFetchTask.ts +0 -35
  49. package/src/elements/EFMedia/videoTasks/makeScrubVideoInputTask.ts +0 -52
  50. package/src/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.ts +0 -124
  51. package/src/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.ts +0 -44
  52. package/src/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.ts +0 -32
  53. package/src/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.ts +0 -370
  54. package/src/elements/EFMedia/videoTasks/makeVideoBufferTask.ts +0 -109
  55. package/src/elements/EFMedia.browsertest.ts +0 -872
  56. package/src/elements/EFMedia.ts +0 -341
  57. package/src/elements/EFSourceMixin.ts +0 -60
  58. package/src/elements/EFSurface.browsertest.ts +0 -151
  59. package/src/elements/EFSurface.ts +0 -142
  60. package/src/elements/EFTemporal.browsertest.ts +0 -215
  61. package/src/elements/EFTemporal.ts +0 -800
  62. package/src/elements/EFThumbnailStrip.browsertest.ts +0 -585
  63. package/src/elements/EFThumbnailStrip.media-engine.browsertest.ts +0 -714
  64. package/src/elements/EFThumbnailStrip.ts +0 -906
  65. package/src/elements/EFTimegroup.browsertest.ts +0 -934
  66. package/src/elements/EFTimegroup.ts +0 -882
  67. package/src/elements/EFVideo.browsertest.ts +0 -1482
  68. package/src/elements/EFVideo.ts +0 -564
  69. package/src/elements/EFWaveform.ts +0 -547
  70. package/src/elements/FetchContext.browsertest.ts +0 -401
  71. package/src/elements/FetchMixin.ts +0 -38
  72. package/src/elements/SampleBuffer.ts +0 -94
  73. package/src/elements/TargetController.browsertest.ts +0 -230
  74. package/src/elements/TargetController.ts +0 -224
  75. package/src/elements/TimegroupController.ts +0 -26
  76. package/src/elements/durationConverter.ts +0 -35
  77. package/src/elements/parseTimeToMs.ts +0 -9
  78. package/src/elements/printTaskStatus.ts +0 -16
  79. package/src/elements/renderTemporalAudio.ts +0 -108
  80. package/src/elements/updateAnimations.browsertest.ts +0 -1884
  81. package/src/elements/updateAnimations.ts +0 -217
  82. package/src/elements/util.ts +0 -24
  83. package/src/gui/ContextMixin.browsertest.ts +0 -860
  84. package/src/gui/ContextMixin.ts +0 -562
  85. package/src/gui/Controllable.browsertest.ts +0 -258
  86. package/src/gui/Controllable.ts +0 -41
  87. package/src/gui/EFConfiguration.ts +0 -40
  88. package/src/gui/EFControls.browsertest.ts +0 -389
  89. package/src/gui/EFControls.ts +0 -195
  90. package/src/gui/EFDial.browsertest.ts +0 -84
  91. package/src/gui/EFDial.ts +0 -172
  92. package/src/gui/EFFilmstrip.browsertest.ts +0 -712
  93. package/src/gui/EFFilmstrip.ts +0 -1349
  94. package/src/gui/EFFitScale.ts +0 -152
  95. package/src/gui/EFFocusOverlay.ts +0 -79
  96. package/src/gui/EFPause.browsertest.ts +0 -202
  97. package/src/gui/EFPause.ts +0 -73
  98. package/src/gui/EFPlay.browsertest.ts +0 -202
  99. package/src/gui/EFPlay.ts +0 -73
  100. package/src/gui/EFPreview.ts +0 -74
  101. package/src/gui/EFResizableBox.browsertest.ts +0 -79
  102. package/src/gui/EFResizableBox.ts +0 -898
  103. package/src/gui/EFScrubber.ts +0 -151
  104. package/src/gui/EFTimeDisplay.browsertest.ts +0 -237
  105. package/src/gui/EFTimeDisplay.ts +0 -55
  106. package/src/gui/EFToggleLoop.ts +0 -35
  107. package/src/gui/EFTogglePlay.ts +0 -70
  108. package/src/gui/EFWorkbench.ts +0 -115
  109. package/src/gui/PlaybackController.ts +0 -527
  110. package/src/gui/TWMixin.css +0 -6
  111. package/src/gui/TWMixin.ts +0 -61
  112. package/src/gui/TargetOrContextMixin.ts +0 -185
  113. package/src/gui/currentTimeContext.ts +0 -5
  114. package/src/gui/durationContext.ts +0 -3
  115. package/src/gui/efContext.ts +0 -6
  116. package/src/gui/fetchContext.ts +0 -5
  117. package/src/gui/focusContext.ts +0 -7
  118. package/src/gui/focusedElementContext.ts +0 -5
  119. package/src/gui/playingContext.ts +0 -5
  120. package/src/otel/BridgeSpanExporter.ts +0 -150
  121. package/src/otel/setupBrowserTracing.ts +0 -73
  122. package/src/otel/tracingHelpers.ts +0 -251
  123. package/src/transcoding/cache/RequestDeduplicator.test.ts +0 -170
  124. package/src/transcoding/cache/RequestDeduplicator.ts +0 -65
  125. package/src/transcoding/cache/URLTokenDeduplicator.test.ts +0 -182
  126. package/src/transcoding/cache/URLTokenDeduplicator.ts +0 -101
  127. package/src/transcoding/types/index.ts +0 -312
  128. package/src/transcoding/utils/MediaUtils.ts +0 -63
  129. package/src/transcoding/utils/UrlGenerator.ts +0 -68
  130. package/src/transcoding/utils/constants.ts +0 -36
  131. package/src/utils/LRUCache.test.ts +0 -274
  132. package/src/utils/LRUCache.ts +0 -696
@@ -1,679 +0,0 @@
1
- import { TaskStatus } from "@lit/task";
2
- import { customElement } from "lit/decorators.js";
3
- import { afterEach, describe, vi } from "vitest";
4
- import { test as baseTest } from "../../../../test/useMSW.js";
5
- import type { AudioRendition } from "../../../transcoding/types";
6
- import { EFMedia } from "../../EFMedia";
7
- import {
8
- computeBufferQueue,
9
- computeSegmentRange,
10
- computeSegmentRangeAsync,
11
- getRequestedSegments,
12
- getUnrequestedSegments,
13
- handleSeekTimeChange,
14
- isSegmentRequested,
15
- type MediaBufferDependencies,
16
- type MediaBufferState,
17
- manageMediaBuffer,
18
- } from "../shared/BufferUtils";
19
- import {
20
- type AudioBufferConfig,
21
- type AudioBufferState,
22
- makeAudioBufferTask,
23
- } from "./makeAudioBufferTask";
24
-
25
- @customElement("test-media-audio-buffer")
26
- class TestMediaAudioBuffer extends EFMedia {}
27
-
28
- declare global {
29
- interface HTMLElementTagNameMap {
30
- "test-media-audio-buffer": TestMediaAudioBuffer;
31
- }
32
- }
33
-
34
- // Test that shows the task should use EFMedia properties directly
35
- describe("makeAudioBufferTask - EFMedia Property Integration", () => {
36
- test("should allow creating task without hardcoded config parameter", ({
37
- expect,
38
- }) => {
39
- const element = document.createElement("test-media-audio-buffer");
40
-
41
- // Set custom values on the element
42
- element.audioBufferDurationMs = 45000;
43
- element.maxAudioBufferFetches = 5;
44
- element.enableAudioBuffering = false;
45
-
46
- // This should work without passing a config parameter
47
- expect(() => {
48
- const task = makeAudioBufferTask(element);
49
- expect(task).toBeDefined();
50
- }).not.toThrow();
51
-
52
- element.remove();
53
- });
54
- });
55
-
56
- const test = baseTest.extend<{
57
- element: TestMediaAudioBuffer;
58
- mockAudioRendition: {
59
- segmentDurationMs: number;
60
- trackId: number;
61
- src: string;
62
- };
63
- mockConfig: AudioBufferConfig;
64
- mockState: AudioBufferState;
65
- mockDeps: MediaBufferDependencies<AudioRendition>;
66
- mockSignal: AbortSignal;
67
- }>({
68
- element: async ({}, use) => {
69
- const element = document.createElement("test-media-audio-buffer");
70
- await use(element);
71
- element.remove();
72
- },
73
-
74
- mockAudioRendition: async ({}, use) => {
75
- const rendition = {
76
- segmentDurationMs: 1000, // 1 second per segment
77
- trackId: 1,
78
- src: "test-audio.mp4",
79
- };
80
- await use(rendition);
81
- },
82
-
83
- mockConfig: async ({}, use) => {
84
- const config = {
85
- bufferDurationMs: 5000, // 5 seconds
86
- maxParallelFetches: 2,
87
- enableBuffering: true,
88
- enableContinuousBuffering: false, // Disable for predictable testing
89
- };
90
- await use(config);
91
- },
92
-
93
- mockState: async ({}, use) => {
94
- const state = {
95
- currentSeekTimeMs: 0,
96
- requestedSegments: new Set<number>(),
97
- activeRequests: new Set<number>(),
98
- requestQueue: [],
99
- };
100
- await use(state);
101
- },
102
-
103
- mockDeps: async ({ mockAudioRendition }, use) => {
104
- const deps = {
105
- computeSegmentId: vi.fn(
106
- async (timeMs: number, rendition: { segmentDurationMs?: number }) => {
107
- return Math.floor(timeMs / (rendition.segmentDurationMs || 1000));
108
- },
109
- ),
110
- prefetchSegment: vi.fn().mockResolvedValue(undefined), // Just trigger prefetch
111
- isSegmentCached: vi.fn().mockReturnValue(false), // Assume nothing cached for testing
112
- getRendition: vi.fn().mockResolvedValue(mockAudioRendition),
113
- logError: vi.fn(),
114
- };
115
- await use(deps);
116
- },
117
-
118
- mockSignal: async ({}, use) => {
119
- const mockSignal = new AbortController().signal;
120
- await use(mockSignal);
121
- },
122
- });
123
-
124
- describe("computeSegmentRange", () => {
125
- test("computes segment range correctly", ({ mockAudioRendition, expect }) => {
126
- const computeSegmentId = (
127
- timeMs: number,
128
- rendition: { segmentDurationMs: number },
129
- ) => Math.floor(timeMs / (rendition.segmentDurationMs || 1000));
130
-
131
- const segments = computeSegmentRange(
132
- 2000, // start at 2 seconds
133
- 5000, // end at 5 seconds
134
- mockAudioRendition,
135
- computeSegmentId,
136
- );
137
-
138
- expect(segments).toEqual([2, 3, 4, 5]); // segments 2, 3, 4, 5
139
- });
140
-
141
- test("returns empty array when segment IDs are undefined", ({
142
- mockAudioRendition,
143
- expect,
144
- }) => {
145
- const computeSegmentId = () => undefined;
146
-
147
- const segments = computeSegmentRange(
148
- 2000,
149
- 5000,
150
- mockAudioRendition,
151
- computeSegmentId,
152
- );
153
-
154
- expect(segments).toEqual([]);
155
- });
156
-
157
- test("uses default segment duration when missing", ({ expect }) => {
158
- const rendition = {}; // Missing segmentDurationMs
159
- const computeSegmentId = () => 1;
160
-
161
- const segments = computeSegmentRange(
162
- 2000,
163
- 5000,
164
- rendition as any,
165
- computeSegmentId,
166
- );
167
-
168
- // Should use default 1000ms duration and call computeSegmentId for segments 2, 3, 4, 5
169
- // Since computeSegmentId always returns 1, and duplicates are filtered out, result is [1]
170
- expect(segments).toEqual([1]); // duplicates filtered out
171
- });
172
- });
173
-
174
- describe("computeSegmentRangeAsync", () => {
175
- test("computes segment range with video duration limit", async ({
176
- mockAudioRendition,
177
- expect,
178
- }) => {
179
- const computeSegmentId = async (timeMs: number) =>
180
- Math.floor(timeMs / 1000);
181
-
182
- const segments = await computeSegmentRangeAsync(
183
- 8000, // start at 8 seconds
184
- 12000, // want to end at 12 seconds
185
- 10000, // but video only lasts 10 seconds
186
- mockAudioRendition,
187
- computeSegmentId,
188
- );
189
-
190
- expect(segments).toEqual([8, 9]); // Limited to video duration, segment 10 would be at 10000ms which is not < 10000
191
- });
192
-
193
- test("handles large duration limit", async ({
194
- mockAudioRendition,
195
- expect,
196
- }) => {
197
- const computeSegmentId = async (timeMs: number) =>
198
- Math.floor(timeMs / 1000);
199
-
200
- const segments = await computeSegmentRangeAsync(
201
- 2000,
202
- 5000,
203
- 100000, // Large duration limit - effectively unlimited
204
- mockAudioRendition,
205
- computeSegmentId,
206
- );
207
-
208
- expect(segments).toEqual([2, 3, 4, 5]);
209
- });
210
- });
211
-
212
- describe("computeBufferQueue", () => {
213
- test("filters out already requested segments", ({ expect }) => {
214
- const desiredSegments = [1, 2, 3, 4, 5];
215
- const requestedSegments = new Set([2, 4, 1]);
216
-
217
- const queue = computeBufferQueue(desiredSegments, requestedSegments);
218
-
219
- expect(queue).toEqual([3, 5]); // Only segments not yet requested
220
- });
221
-
222
- test("returns empty queue when all segments already requested", ({
223
- expect,
224
- }) => {
225
- const desiredSegments = [1, 2, 3];
226
- const requestedSegments = new Set([1, 2, 3]);
227
-
228
- const queue = computeBufferQueue(desiredSegments, requestedSegments);
229
-
230
- expect(queue).toEqual([]);
231
- });
232
- });
233
-
234
- describe("handleSeekTimeChange", () => {
235
- test("computes new queue for seek time change", ({
236
- mockAudioRendition,
237
- expect,
238
- }) => {
239
- const computeSegmentId = (timeMs: number) => Math.floor(timeMs / 1000);
240
- const currentState = {
241
- currentSeekTimeMs: 0,
242
- requestedSegments: new Set([1]),
243
- activeRequests: new Set<number>(),
244
- requestQueue: [],
245
- };
246
-
247
- const result = handleSeekTimeChange(
248
- 3000, // seek to 3 seconds
249
- 2000, // buffer 2 seconds ahead
250
- mockAudioRendition,
251
- currentState,
252
- computeSegmentId,
253
- );
254
-
255
- expect(result.newQueue).toEqual([3, 4, 5]); // segments 3, 4, 5 needed, none in cache/active
256
- expect(result.overlappingRequests).toEqual([]); // no overlap with active requests
257
- });
258
-
259
- test("identifies overlapping requests", ({ mockAudioRendition, expect }) => {
260
- const computeSegmentId = (timeMs: number) => Math.floor(timeMs / 1000);
261
- const currentState = {
262
- currentSeekTimeMs: 0,
263
- requestedSegments: new Set([3, 4]),
264
- activeRequests: new Set<number>(),
265
- requestQueue: [],
266
- };
267
-
268
- const result = handleSeekTimeChange(
269
- 3000, // seek to 3 seconds
270
- 2000, // buffer 2 seconds ahead
271
- mockAudioRendition,
272
- currentState,
273
- computeSegmentId,
274
- );
275
-
276
- expect(result.overlappingRequests).toEqual([3, 4]); // both are already being fetched
277
- expect(result.newQueue).toEqual([5]); // only segment 5 needs fetching
278
- });
279
- });
280
-
281
- describe("manageMediaBuffer (Audio)", () => {
282
- test("manages buffer state successfully", async ({
283
- mockConfig,
284
- mockState,
285
- mockDeps,
286
- mockSignal,
287
- expect,
288
- }) => {
289
- const seekTimeMs = 3000;
290
-
291
- const newState = await manageMediaBuffer(
292
- seekTimeMs,
293
- mockConfig,
294
- mockState,
295
- 10000, // durationMs
296
- mockSignal,
297
- mockDeps,
298
- );
299
-
300
- expect(newState.currentSeekTimeMs).toBe(seekTimeMs);
301
- expect(mockDeps.getRendition).toHaveBeenCalled();
302
- expect(mockDeps.prefetchSegment).toHaveBeenCalledTimes(2); // maxParallelFetches = 2
303
- });
304
-
305
- test("respects maxParallelFetches limit", async ({
306
- mockState,
307
- mockDeps,
308
- mockSignal,
309
- expect,
310
- }) => {
311
- const config = {
312
- bufferDurationMs: 10000, // 10 seconds = 10 segments
313
- maxParallelFetches: 3,
314
- enableBuffering: true,
315
- enableContinuousBuffering: false, // Disable for predictable testing
316
- };
317
-
318
- await manageMediaBuffer(
319
- 0,
320
- config,
321
- mockState,
322
- 10000, // durationMs
323
- mockSignal,
324
- mockDeps,
325
- );
326
-
327
- expect(mockDeps.prefetchSegment).toHaveBeenCalledTimes(3); // Should only fetch 3 despite needing 10
328
- });
329
-
330
- test("does nothing when buffering disabled", async ({
331
- mockState,
332
- mockDeps,
333
- mockSignal,
334
- expect,
335
- }) => {
336
- const config = {
337
- bufferDurationMs: 5000,
338
- maxParallelFetches: 2,
339
- enableBuffering: false,
340
- };
341
-
342
- const newState = await manageMediaBuffer(
343
- 1000,
344
- config,
345
- mockState,
346
- 10000, // durationMs
347
- mockSignal,
348
- mockDeps,
349
- );
350
-
351
- expect(newState).toBe(mockState); // Should return same state
352
- expect(mockDeps.prefetchSegment).not.toHaveBeenCalled();
353
- });
354
-
355
- test("handles fetch errors gracefully", async ({
356
- mockConfig,
357
- mockState,
358
- mockSignal,
359
- expect,
360
- }) => {
361
- const mockDeps = {
362
- computeSegmentId: vi.fn().mockResolvedValue(1),
363
- prefetchSegment: vi.fn().mockRejectedValue(new Error("Network error")),
364
- isSegmentCached: vi.fn().mockReturnValue(false),
365
- getRendition: vi.fn().mockResolvedValue({ segmentDurationMs: 1000 }),
366
- logError: vi.fn(),
367
- };
368
-
369
- const newState = await manageMediaBuffer(
370
- 1000,
371
- mockConfig,
372
- mockState,
373
- 10000, // durationMs
374
- mockSignal,
375
- mockDeps,
376
- );
377
-
378
- // Wait for async error handling to complete
379
- await new Promise((resolve) => setTimeout(resolve, 10));
380
-
381
- expect(newState.currentSeekTimeMs).toBe(1000);
382
- expect(mockDeps.logError).toHaveBeenCalledWith(
383
- "Failed to prefetch segment 1",
384
- expect.any(Error),
385
- );
386
- });
387
- });
388
-
389
- describe("makeAudioBufferTask", () => {
390
- afterEach(() => {
391
- const elements = document.querySelectorAll("test-media-audio-buffer");
392
- for (const element of elements) {
393
- element.remove();
394
- }
395
- vi.restoreAllMocks();
396
- });
397
-
398
- test("creates task with correct configuration", ({ element, expect }) => {
399
- const task = makeAudioBufferTask(element);
400
-
401
- expect(task).toBeDefined();
402
- expect(task.status).toBe(TaskStatus.INITIAL);
403
- expect(task.value).toBeUndefined();
404
- expect(task.error).toBeUndefined();
405
- });
406
-
407
- test("task integrates with element seek time", ({ element, expect }) => {
408
- element.desiredSeekTimeMs = 5000;
409
-
410
- const task = makeAudioBufferTask(element);
411
- expect(task).toBeDefined();
412
- expect(task.status).toBe(TaskStatus.INITIAL);
413
- });
414
- });
415
-
416
- describe("Buffer Orchestration Methods", () => {
417
- test("isSegmentRequested returns true for requested segments", ({
418
- expect,
419
- }) => {
420
- const bufferState: MediaBufferState = {
421
- currentSeekTimeMs: 0,
422
- requestedSegments: new Set([1, 2, 3]),
423
- activeRequests: new Set(),
424
- requestQueue: [],
425
- };
426
-
427
- expect(isSegmentRequested(1, bufferState)).toBe(true);
428
- expect(isSegmentRequested(2, bufferState)).toBe(true);
429
- expect(isSegmentRequested(4, bufferState)).toBe(false);
430
- });
431
-
432
- test("isSegmentRequested returns false for undefined buffer state", ({
433
- expect,
434
- }) => {
435
- expect(isSegmentRequested(1, undefined)).toBe(false);
436
- });
437
-
438
- test("getRequestedSegments returns correct requested segment set", ({
439
- expect,
440
- }) => {
441
- const bufferState: MediaBufferState = {
442
- currentSeekTimeMs: 0,
443
- requestedSegments: new Set([2, 4, 6]),
444
- activeRequests: new Set(),
445
- requestQueue: [],
446
- };
447
-
448
- const segmentIds = [1, 2, 3, 4, 5, 6];
449
- const requestedSegments = getRequestedSegments(segmentIds, bufferState);
450
-
451
- expect(requestedSegments).toEqual(new Set([2, 4, 6]));
452
- });
453
-
454
- test("getRequestedSegments returns empty set for undefined buffer state", ({
455
- expect,
456
- }) => {
457
- const segmentIds = [1, 2, 3];
458
- const requestedSegments = getRequestedSegments(segmentIds, undefined);
459
-
460
- expect(requestedSegments).toEqual(new Set());
461
- });
462
-
463
- test("getUnrequestedSegments returns correct unrequested segments", ({
464
- expect,
465
- }) => {
466
- const bufferState: MediaBufferState = {
467
- currentSeekTimeMs: 0,
468
- requestedSegments: new Set([2, 4, 6]),
469
- activeRequests: new Set(),
470
- requestQueue: [],
471
- };
472
-
473
- const segmentIds = [1, 2, 3, 4, 5, 6];
474
- const unrequestedSegments = getUnrequestedSegments(segmentIds, bufferState);
475
-
476
- expect(unrequestedSegments).toEqual([1, 3, 5]);
477
- });
478
-
479
- test("getUnrequestedSegments returns all segments for undefined buffer state", ({
480
- expect,
481
- }) => {
482
- const segmentIds = [1, 2, 3];
483
- const unrequestedSegments = getUnrequestedSegments(segmentIds, undefined);
484
-
485
- expect(unrequestedSegments).toEqual([1, 2, 3]);
486
- });
487
- });
488
-
489
- describe("Buffering Integration Issues", () => {
490
- const test = baseTest.extend<{
491
- element: TestMediaAudioBuffer;
492
- }>({
493
- element: async ({}, use) => {
494
- const element = document.createElement("test-media-audio-buffer");
495
- document.body.appendChild(element);
496
- await use(element);
497
- element.remove();
498
- },
499
- });
500
-
501
- test("buffer task should run in interactive mode", async ({
502
- element,
503
- expect,
504
- }) => {
505
- // Set up real media element with actual test asset
506
- element.src = "bars-n-tone2.mp4";
507
- element.enableAudioBuffering = true;
508
- element.audioBufferDurationMs = 5000;
509
- element.maxAudioBufferFetches = 2;
510
- element.desiredSeekTimeMs = 1000;
511
-
512
- // Allow time for media engine initialization
513
- await new Promise((resolve) => setTimeout(resolve, 200));
514
-
515
- expect(element.audioBufferTask.status).not.toBe(TaskStatus.INITIAL);
516
- });
517
-
518
- test.skip("buffer task should be disabled in rendering mode", async ({
519
- expect,
520
- }) => {
521
- const originalEFRendering = window.EF_RENDERING;
522
-
523
- try {
524
- // Simulate rendering mode
525
- window.EF_RENDERING = () => true;
526
-
527
- // Create element in rendering mode
528
- const renderElement = document.createElement("test-media-audio-buffer");
529
- renderElement.src = "bars-n-tone2.mp4";
530
- renderElement.enableAudioBuffering = true;
531
- document.body.appendChild(renderElement);
532
-
533
- await new Promise((resolve) => setTimeout(resolve, 100));
534
-
535
- // Buffer task should NOT run in rendering mode
536
- expect(renderElement.audioBufferTask.status).toBe(TaskStatus.INITIAL);
537
-
538
- renderElement.remove();
539
- } finally {
540
- window.EF_RENDERING = originalEFRendering;
541
- }
542
- });
543
-
544
- test("segment fetch task does not check buffer cache", async ({
545
- element,
546
- expect,
547
- }) => {
548
- // Set up element with buffering enabled
549
- element.src = "bars-n-tone2.mp4";
550
- element.enableAudioBuffering = true;
551
- element.audioBufferDurationMs = 3000;
552
- element.desiredSeekTimeMs = 1000;
553
-
554
- // Track network requests to show segment fetch operates independently
555
- const originalFetch = window.fetch;
556
- const fetchUrls: string[] = [];
557
-
558
- window.fetch = (input: RequestInfo | URL, init?: RequestInit) => {
559
- const url = input.toString();
560
- fetchUrls.push(url);
561
- return originalFetch(input, init);
562
- };
563
-
564
- try {
565
- // Allow buffer and segment fetch to initialize
566
- await new Promise((resolve) => setTimeout(resolve, 300));
567
-
568
- // Move to different time to trigger segment fetch
569
- element.desiredSeekTimeMs = 2000;
570
- await new Promise((resolve) => setTimeout(resolve, 200));
571
-
572
- // This demonstrates the problem: segment fetch makes requests
573
- // independently of what buffer cache contains
574
- const audioRequests = fetchUrls.filter((url) => url.includes("audio"));
575
-
576
- // Currently passes - shows segment fetch operates without cache integration
577
- expect(audioRequests.length).toBeGreaterThanOrEqual(0);
578
- } finally {
579
- window.fetch = originalFetch;
580
- }
581
- });
582
-
583
- test("buffer cache and segment fetch operate independently", async ({
584
- element,
585
- expect,
586
- }) => {
587
- // Set up element that should have buffered segments
588
- element.src = "bars-n-tone2.mp4";
589
- element.enableAudioBuffering = true;
590
- element.audioBufferDurationMs = 5000;
591
- element.maxAudioBufferFetches = 3;
592
- element.desiredSeekTimeMs = 0;
593
-
594
- // Allow buffering to start
595
- await new Promise((resolve) => setTimeout(resolve, 400));
596
-
597
- // Check if buffer has cached segments
598
- const bufferState = element.audioBufferTask.value;
599
-
600
- if (
601
- bufferState?.requestedSegments &&
602
- bufferState.requestedSegments.size > 0
603
- ) {
604
- // Move seek position to trigger segment fetch of potentially cached segment
605
- element.desiredSeekTimeMs = 1000;
606
-
607
- // Currently, segment fetch task doesn't consult buffer cache
608
- // This is the integration gap we need to fix
609
- expect(bufferState.requestedSegments.size).toBeGreaterThan(0);
610
- }
611
- });
612
- });
613
-
614
- describe("Continuous Buffering", () => {
615
- test.skip("enables continuous segment loading when enabled", async ({
616
- mockState,
617
- mockDeps,
618
- mockSignal,
619
- expect,
620
- }) => {
621
- const configWithContinuous = {
622
- bufferDurationMs: 10000, // 10 seconds = 10 segments
623
- maxParallelFetches: 2,
624
- enableBuffering: true,
625
- enableContinuousBuffering: true, // Enable continuous buffering
626
- };
627
-
628
- let fetchCount = 0;
629
- const mockFetchWithDelay = vi.fn().mockImplementation(() => {
630
- fetchCount++;
631
- return Promise.resolve(new ArrayBuffer(1000));
632
- });
633
-
634
- const mockDepsWithContinuous = {
635
- ...mockDeps,
636
- fetchSegment: mockFetchWithDelay,
637
- };
638
-
639
- await manageMediaBuffer(
640
- 0,
641
- configWithContinuous,
642
- mockState,
643
- 10000, // durationMs
644
- mockSignal,
645
- mockDepsWithContinuous,
646
- );
647
-
648
- // Should start with initial maxParallelFetches (2) and continue with more requests
649
- // Continuous buffering should fetch more segments as previous ones complete
650
- expect(mockFetchWithDelay).toHaveBeenCalledTimes(4); // More than initial batch due to continuous buffering
651
- expect(fetchCount).toBe(4);
652
- });
653
-
654
- test("disabled when flag is false", async ({
655
- mockState,
656
- mockDeps,
657
- mockSignal,
658
- expect,
659
- }) => {
660
- const configWithoutContinuous = {
661
- bufferDurationMs: 10000, // 10 seconds = 10 segments
662
- maxParallelFetches: 2,
663
- enableBuffering: true,
664
- enableContinuousBuffering: false, // Disable continuous buffering
665
- };
666
-
667
- await manageMediaBuffer(
668
- 0,
669
- configWithoutContinuous,
670
- mockState,
671
- 10000, // durationMs
672
- mockSignal,
673
- mockDeps,
674
- );
675
-
676
- // Should only fetch initial maxParallelFetches and stop
677
- expect(mockDeps.prefetchSegment).toHaveBeenCalledTimes(2);
678
- });
679
- });