@editframe/elements 0.18.22-beta.0 → 0.18.26-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 (99) hide show
  1. package/dist/elements/EFMedia/AssetMediaEngine.d.ts +2 -1
  2. package/dist/elements/EFMedia/AssetMediaEngine.js +3 -0
  3. package/dist/elements/EFMedia/BaseMediaEngine.d.ts +9 -0
  4. package/dist/elements/EFMedia/BaseMediaEngine.js +31 -0
  5. package/dist/elements/EFMedia/JitMediaEngine.d.ts +1 -0
  6. package/dist/elements/EFMedia/JitMediaEngine.js +12 -0
  7. package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.js +11 -5
  8. package/dist/elements/EFMedia/shared/BufferUtils.d.ts +19 -18
  9. package/dist/elements/EFMedia/shared/BufferUtils.js +24 -44
  10. package/dist/elements/EFMedia/tasks/makeMediaEngineTask.browsertest.d.ts +8 -0
  11. package/dist/elements/EFMedia/tasks/makeMediaEngineTask.js +5 -5
  12. package/dist/elements/EFMedia/videoTasks/ScrubInputCache.d.ts +25 -0
  13. package/dist/elements/EFMedia/videoTasks/ScrubInputCache.js +42 -0
  14. package/dist/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.d.ts +8 -0
  15. package/dist/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.js +70 -0
  16. package/dist/elements/EFMedia/videoTasks/{makeVideoInitSegmentFetchTask.d.ts → makeScrubVideoInitSegmentFetchTask.d.ts} +1 -1
  17. package/dist/elements/EFMedia/videoTasks/makeScrubVideoInitSegmentFetchTask.js +21 -0
  18. package/dist/elements/EFMedia/videoTasks/{makeVideoInputTask.d.ts → makeScrubVideoInputTask.d.ts} +1 -1
  19. package/dist/elements/EFMedia/videoTasks/makeScrubVideoInputTask.js +27 -0
  20. package/dist/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.d.ts +6 -0
  21. package/dist/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.js +52 -0
  22. package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.d.ts +4 -0
  23. package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.js +23 -0
  24. package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.d.ts +4 -0
  25. package/dist/elements/EFMedia/videoTasks/{makeVideoSegmentIdTask.js → makeScrubVideoSegmentIdTask.js} +9 -4
  26. package/dist/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.d.ts +6 -0
  27. package/dist/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.js +112 -0
  28. package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.js +11 -5
  29. package/dist/elements/EFMedia.d.ts +0 -10
  30. package/dist/elements/EFMedia.js +1 -17
  31. package/dist/elements/EFVideo.d.ts +11 -9
  32. package/dist/elements/EFVideo.js +31 -23
  33. package/dist/gui/EFConfiguration.d.ts +1 -0
  34. package/dist/gui/EFConfiguration.js +5 -0
  35. package/dist/gui/EFFilmstrip.d.ts +1 -1
  36. package/dist/index.d.ts +1 -1
  37. package/dist/transcoding/types/index.d.ts +11 -0
  38. package/package.json +2 -2
  39. package/src/elements/EFCaptions.ts +1 -1
  40. package/src/elements/EFImage.ts +1 -1
  41. package/src/elements/EFMedia/AssetMediaEngine.ts +6 -0
  42. package/src/elements/EFMedia/BaseMediaEngine.ts +60 -0
  43. package/src/elements/EFMedia/JitMediaEngine.ts +18 -0
  44. package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.browsertest.ts +185 -59
  45. package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.ts +19 -6
  46. package/src/elements/EFMedia/shared/BufferUtils.ts +71 -85
  47. package/src/elements/EFMedia/tasks/makeMediaEngineTask.browsertest.ts +151 -112
  48. package/src/elements/EFMedia/tasks/makeMediaEngineTask.ts +12 -5
  49. package/src/elements/EFMedia/videoTasks/ScrubInputCache.ts +61 -0
  50. package/src/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.ts +113 -0
  51. package/src/elements/EFMedia/videoTasks/{makeVideoInitSegmentFetchTask.ts → makeScrubVideoInitSegmentFetchTask.ts} +15 -3
  52. package/src/elements/EFMedia/videoTasks/{makeVideoInputTask.ts → makeScrubVideoInputTask.ts} +11 -10
  53. package/src/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.ts +118 -0
  54. package/src/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.ts +44 -0
  55. package/src/elements/EFMedia/videoTasks/{makeVideoSegmentIdTask.ts → makeScrubVideoSegmentIdTask.ts} +14 -6
  56. package/src/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.ts +258 -0
  57. package/src/elements/EFMedia/videoTasks/makeVideoBufferTask.ts +19 -5
  58. package/src/elements/EFMedia.browsertest.ts +74 -11
  59. package/src/elements/EFMedia.ts +1 -23
  60. package/src/elements/EFVideo.browsertest.ts +204 -80
  61. package/src/elements/EFVideo.ts +38 -26
  62. package/src/elements/TargetController.browsertest.ts +1 -1
  63. package/src/gui/EFConfiguration.ts +4 -1
  64. package/src/gui/EFFilmstrip.ts +4 -4
  65. package/src/gui/EFFocusOverlay.ts +1 -1
  66. package/src/gui/EFPreview.ts +3 -4
  67. package/src/gui/EFScrubber.ts +1 -1
  68. package/src/gui/EFTimeDisplay.ts +1 -1
  69. package/src/gui/EFToggleLoop.ts +1 -1
  70. package/src/gui/EFTogglePlay.ts +1 -1
  71. package/src/gui/EFWorkbench.ts +1 -1
  72. package/src/transcoding/types/index.ts +16 -0
  73. package/test/__cache__/GET__api_v1_transcode_scrub_1_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__6ff5127ebeda578a679474347fbd6137/data.bin +0 -0
  74. package/test/__cache__/GET__api_v1_transcode_scrub_1_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__6ff5127ebeda578a679474347fbd6137/metadata.json +16 -0
  75. package/test/__cache__/GET__api_v1_transcode_scrub_init_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__f6d4793fc9ff854ee9a738917fb64a53/data.bin +0 -0
  76. package/test/__cache__/GET__api_v1_transcode_scrub_init_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__f6d4793fc9ff854ee9a738917fb64a53/metadata.json +16 -0
  77. package/test/cache-integration-verification.browsertest.ts +84 -0
  78. package/types.json +1 -1
  79. package/dist/elements/EFMedia/tasks/makeMediaEngineTask.test.d.ts +0 -1
  80. package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.browsertest.d.ts +0 -9
  81. package/dist/elements/EFMedia/videoTasks/makeVideoInitSegmentFetchTask.browsertest.d.ts +0 -9
  82. package/dist/elements/EFMedia/videoTasks/makeVideoInitSegmentFetchTask.js +0 -16
  83. package/dist/elements/EFMedia/videoTasks/makeVideoInputTask.browsertest.d.ts +0 -9
  84. package/dist/elements/EFMedia/videoTasks/makeVideoInputTask.js +0 -27
  85. package/dist/elements/EFMedia/videoTasks/makeVideoSeekTask.d.ts +0 -7
  86. package/dist/elements/EFMedia/videoTasks/makeVideoSeekTask.js +0 -34
  87. package/dist/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.browsertest.d.ts +0 -9
  88. package/dist/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.d.ts +0 -4
  89. package/dist/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.js +0 -28
  90. package/dist/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.browsertest.d.ts +0 -9
  91. package/dist/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.d.ts +0 -4
  92. package/src/elements/EFMedia/tasks/makeMediaEngineTask.test.ts +0 -233
  93. package/src/elements/EFMedia/videoTasks/makeVideoBufferTask.browsertest.ts +0 -555
  94. package/src/elements/EFMedia/videoTasks/makeVideoInitSegmentFetchTask.browsertest.ts +0 -59
  95. package/src/elements/EFMedia/videoTasks/makeVideoInputTask.browsertest.ts +0 -55
  96. package/src/elements/EFMedia/videoTasks/makeVideoSeekTask.ts +0 -65
  97. package/src/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.browsertest.ts +0 -57
  98. package/src/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.ts +0 -43
  99. package/src/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.browsertest.ts +0 -56
@@ -1,555 +0,0 @@
1
- import { TaskStatus } from "@lit/task";
2
- import { customElement } from "lit/decorators.js";
3
- import { afterEach, beforeEach, describe, vi } from "vitest";
4
- import { test as baseTest } from "../../../../test/useMSW.js";
5
- import type { VideoRendition } from "../../../transcoding/types";
6
- import { EFVideo } from "../../EFVideo";
7
- import {
8
- computeBufferQueue,
9
- computeSegmentRange,
10
- handleSeekTimeChange,
11
- type MediaBufferDependencies,
12
- manageMediaBuffer,
13
- } from "../shared/BufferUtils";
14
- import {
15
- makeVideoBufferTask,
16
- type VideoBufferConfig,
17
- type VideoBufferState,
18
- } from "./makeVideoBufferTask";
19
-
20
- @customElement("test-media-video-buffer")
21
- class TestMediaVideoBuffer extends EFVideo {}
22
-
23
- declare global {
24
- interface HTMLElementTagNameMap {
25
- "test-media-video-buffer": TestMediaVideoBuffer;
26
- }
27
- }
28
-
29
- const test = baseTest.extend<{
30
- element: TestMediaVideoBuffer;
31
- mockVideoRendition: VideoRendition;
32
- mockConfig: VideoBufferConfig;
33
- mockState: VideoBufferState;
34
- mockDeps: MediaBufferDependencies<VideoRendition>;
35
- mockSignal: AbortSignal;
36
- }>({
37
- element: async ({}, use) => {
38
- const element = document.createElement("test-media-video-buffer");
39
- await use(element);
40
- element.remove();
41
- },
42
-
43
- mockVideoRendition: async ({}, use) => {
44
- const rendition = {
45
- trackId: 1,
46
- src: "test-video.mp4",
47
- segmentDurationMs: 1000, // 1 second per segment
48
- };
49
- await use(rendition);
50
- },
51
-
52
- mockConfig: async ({}, use) => {
53
- const config = {
54
- bufferDurationMs: 5000, // 5 seconds
55
- maxParallelFetches: 2,
56
- enableBuffering: true,
57
- enableContinuousBuffering: false, // Disable for predictable testing
58
- };
59
- await use(config);
60
- },
61
-
62
- mockState: async ({}, use) => {
63
- const state = {
64
- currentSeekTimeMs: 0,
65
- activeRequests: new Set<number>(),
66
- cachedSegments: new Set<number>(),
67
- requestQueue: [],
68
- };
69
- await use(state);
70
- },
71
-
72
- mockDeps: async ({ mockVideoRendition }, use) => {
73
- const deps = {
74
- computeSegmentId: vi.fn(
75
- async (timeMs: number, rendition: VideoRendition) => {
76
- return Math.floor(timeMs / (rendition.segmentDurationMs || 1000));
77
- },
78
- ),
79
- fetchSegment: vi.fn().mockResolvedValue(new ArrayBuffer(1024)),
80
- getRendition: vi.fn().mockResolvedValue(mockVideoRendition),
81
- logError: vi.fn(),
82
- };
83
- await use(deps);
84
- },
85
-
86
- mockSignal: async ({}, use) => {
87
- const mockSignal = new AbortController().signal;
88
- await use(mockSignal);
89
- },
90
- });
91
-
92
- describe("computeSegmentRange", () => {
93
- test("computes segment range for time range", ({
94
- mockVideoRendition,
95
- expect,
96
- }) => {
97
- const syncComputeSegmentId = vi.fn(
98
- (timeMs: number, rendition: VideoRendition) => {
99
- return Math.floor(timeMs / (rendition.segmentDurationMs || 1000));
100
- },
101
- );
102
-
103
- const startTimeMs = 2000; // 2 seconds
104
- const endTimeMs = 6000; // 6 seconds
105
-
106
- const segments = computeSegmentRange(
107
- startTimeMs,
108
- endTimeMs,
109
- mockVideoRendition,
110
- syncComputeSegmentId,
111
- );
112
-
113
- expect(segments).toEqual([2, 3, 4, 5, 6]);
114
- expect(syncComputeSegmentId).toHaveBeenCalledWith(
115
- startTimeMs,
116
- mockVideoRendition,
117
- );
118
- expect(syncComputeSegmentId).toHaveBeenCalledWith(
119
- endTimeMs,
120
- mockVideoRendition,
121
- );
122
- });
123
-
124
- test("handles zero start time", ({ mockVideoRendition, expect }) => {
125
- const syncComputeSegmentId = (
126
- timeMs: number,
127
- rendition: VideoRendition,
128
- ) => {
129
- return Math.floor(timeMs / (rendition.segmentDurationMs || 1000));
130
- };
131
-
132
- const segments = computeSegmentRange(
133
- 0,
134
- 3000,
135
- mockVideoRendition,
136
- syncComputeSegmentId,
137
- );
138
- expect(segments).toEqual([0, 1, 2, 3]);
139
- });
140
-
141
- test("handles single segment range", ({ mockVideoRendition, expect }) => {
142
- const syncComputeSegmentId = (
143
- timeMs: number,
144
- rendition: VideoRendition,
145
- ) => {
146
- return Math.floor(timeMs / (rendition.segmentDurationMs || 1000));
147
- };
148
-
149
- const segments = computeSegmentRange(
150
- 2500,
151
- 2800,
152
- mockVideoRendition,
153
- syncComputeSegmentId,
154
- );
155
- expect(segments).toEqual([2]);
156
- });
157
-
158
- test("uses default segment duration when missing", ({ expect }) => {
159
- const syncComputeSegmentId = (
160
- timeMs: number,
161
- rendition: VideoRendition,
162
- ) => {
163
- return Math.floor(timeMs / (rendition.segmentDurationMs || 1000));
164
- };
165
- const renditionWithoutDuration = { trackId: 1, src: "test" };
166
-
167
- const segments = computeSegmentRange(
168
- 2000,
169
- 5000,
170
- renditionWithoutDuration as any,
171
- syncComputeSegmentId,
172
- );
173
-
174
- // Should use default 1000ms duration and call syncComputeSegmentId for segments 2, 3, 4, 5
175
- // Since syncComputeSegmentId always returns Math.floor(timeMs / 1000), result is [2, 3, 4, 5]
176
- expect(segments).toEqual([2, 3, 4, 5]);
177
- });
178
-
179
- test("returns empty array when computeSegmentId returns undefined", ({
180
- mockVideoRendition,
181
- expect,
182
- }) => {
183
- const computeSegmentId = vi.fn().mockReturnValue(undefined);
184
-
185
- const segments = computeSegmentRange(
186
- 0,
187
- 1000,
188
- mockVideoRendition,
189
- computeSegmentId,
190
- );
191
- expect(segments).toEqual([]);
192
- });
193
- });
194
-
195
- describe("computeBufferQueue", () => {
196
- test("filters out active requests and cached segments", ({ expect }) => {
197
- const desiredSegments = [1, 2, 3, 4, 5];
198
- const activeRequests = new Set([2, 4]);
199
- const cachedSegments = new Set([1]);
200
-
201
- const queue = computeBufferQueue(
202
- desiredSegments,
203
- activeRequests,
204
- cachedSegments,
205
- );
206
- expect(queue).toEqual([3, 5]);
207
- });
208
-
209
- test("returns empty queue when all segments are active or cached", ({
210
- expect,
211
- }) => {
212
- const desiredSegments = [1, 2, 3];
213
- const activeRequests = new Set([1, 2]);
214
- const cachedSegments = new Set([3]);
215
-
216
- const queue = computeBufferQueue(
217
- desiredSegments,
218
- activeRequests,
219
- cachedSegments,
220
- );
221
- expect(queue).toEqual([]);
222
- });
223
-
224
- test("returns all segments when none are active or cached", ({ expect }) => {
225
- const desiredSegments = [1, 2, 3, 4];
226
- const activeRequests = new Set<number>();
227
- const cachedSegments = new Set<number>();
228
-
229
- const queue = computeBufferQueue(
230
- desiredSegments,
231
- activeRequests,
232
- cachedSegments,
233
- );
234
- expect(queue).toEqual([1, 2, 3, 4]);
235
- });
236
- });
237
-
238
- describe("handleSeekTimeChange", () => {
239
- test("computes new queue and finds overlapping requests", ({
240
- mockVideoRendition,
241
- expect,
242
- }) => {
243
- // Use sync version with sync computeSegmentId for this test
244
- const syncComputeSegmentId = (
245
- timeMs: number,
246
- rendition: VideoRendition,
247
- ) => {
248
- return Math.floor(timeMs / (rendition.segmentDurationMs || 1000));
249
- };
250
-
251
- const currentState = {
252
- currentSeekTimeMs: 1000,
253
- activeRequests: new Set([2, 3]),
254
- cachedSegments: new Set([1]),
255
- requestQueue: [4, 5],
256
- };
257
-
258
- const result = handleSeekTimeChange(
259
- 3000, // new seek time
260
- 3000, // buffer 3 seconds
261
- mockVideoRendition,
262
- currentState,
263
- syncComputeSegmentId,
264
- );
265
-
266
- expect(result.newQueue).toEqual([4, 5, 6]); // segments 3 is active, 4,5,6 needed
267
- expect(result.overlappingRequests).toEqual([3]); // segment 3 is already being fetched
268
- });
269
-
270
- test("handles seek time change with no overlap", ({
271
- mockVideoRendition,
272
- expect,
273
- }) => {
274
- const syncComputeSegmentId = (
275
- timeMs: number,
276
- rendition: VideoRendition,
277
- ) => {
278
- return Math.floor(timeMs / (rendition.segmentDurationMs || 1000));
279
- };
280
-
281
- const currentState = {
282
- currentSeekTimeMs: 1000,
283
- activeRequests: new Set([1, 2]),
284
- cachedSegments: new Set<number>(),
285
- requestQueue: [],
286
- };
287
-
288
- const result = handleSeekTimeChange(
289
- 10000, // seek far ahead
290
- 2000, // buffer 2 seconds
291
- mockVideoRendition,
292
- currentState,
293
- syncComputeSegmentId,
294
- );
295
-
296
- expect(result.newQueue).toEqual([10, 11, 12]); // completely new segments
297
- expect(result.overlappingRequests).toEqual([]); // no overlap
298
- });
299
-
300
- test("handles backwards seek", ({ mockVideoRendition, expect }) => {
301
- const syncComputeSegmentId = (
302
- timeMs: number,
303
- rendition: VideoRendition,
304
- ) => {
305
- return Math.floor(timeMs / (rendition.segmentDurationMs || 1000));
306
- };
307
-
308
- const currentState = {
309
- currentSeekTimeMs: 5000,
310
- activeRequests: new Set([6, 7]),
311
- cachedSegments: new Set([5]),
312
- requestQueue: [],
313
- };
314
-
315
- const result = handleSeekTimeChange(
316
- 2000, // seek backwards
317
- 3000, // buffer 3 seconds
318
- mockVideoRendition,
319
- currentState,
320
- syncComputeSegmentId,
321
- );
322
-
323
- expect(result.newQueue).toEqual([2, 3, 4]); // segments 2,3,4,5 needed, 5 cached
324
- expect(result.overlappingRequests).toEqual([]); // no overlap with active requests
325
- });
326
- });
327
-
328
- describe("manageMediaBuffer (Video)", () => {
329
- test("manages buffer state successfully", async ({
330
- mockConfig,
331
- mockState,
332
- mockDeps,
333
- mockSignal,
334
- expect,
335
- }) => {
336
- const seekTimeMs = 3000;
337
-
338
- const newState = await manageMediaBuffer(
339
- seekTimeMs,
340
- mockConfig,
341
- mockState,
342
- 10000, // durationMs
343
- mockSignal,
344
- mockDeps,
345
- );
346
-
347
- expect(newState.currentSeekTimeMs).toBe(seekTimeMs);
348
- expect(mockDeps.getRendition).toHaveBeenCalled();
349
- expect(mockDeps.fetchSegment).toHaveBeenCalledTimes(2); // maxParallelFetches = 2
350
- });
351
-
352
- test("respects maxParallelFetches limit", async ({
353
- mockState,
354
- mockDeps,
355
- mockSignal,
356
- expect,
357
- }) => {
358
- const config = {
359
- bufferDurationMs: 10000, // 10 seconds = 10 segments
360
- maxParallelFetches: 3,
361
- enableBuffering: true,
362
- enableContinuousBuffering: false, // Disable for predictable testing
363
- };
364
-
365
- await manageMediaBuffer(
366
- 0,
367
- config,
368
- mockState,
369
- 10000, // durationMs
370
- mockSignal,
371
- mockDeps,
372
- );
373
-
374
- expect(mockDeps.fetchSegment).toHaveBeenCalledTimes(3); // Should only fetch 3 despite needing 10
375
- });
376
-
377
- test("does nothing when buffering disabled", async ({
378
- mockState,
379
- mockDeps,
380
- mockSignal,
381
- expect,
382
- }) => {
383
- const config = {
384
- bufferDurationMs: 5000,
385
- maxParallelFetches: 2,
386
- enableBuffering: false,
387
- };
388
-
389
- const newState = await manageMediaBuffer(
390
- 1000,
391
- config,
392
- mockState,
393
- 10000, // durationMs
394
- mockSignal,
395
- mockDeps,
396
- );
397
-
398
- expect(newState).toBe(mockState); // Should return same state
399
- expect(mockDeps.fetchSegment).not.toHaveBeenCalled();
400
- });
401
-
402
- test("skips segments already in active requests", async ({
403
- mockConfig,
404
- mockDeps,
405
- mockSignal,
406
- expect,
407
- }) => {
408
- const stateWithActiveRequests = {
409
- currentSeekTimeMs: 0,
410
- activeRequests: new Set([1, 2]), // segments 1,2 already being fetched
411
- cachedSegments: new Set<number>(),
412
- requestQueue: [],
413
- };
414
-
415
- await manageMediaBuffer(
416
- 1000, // seeks to time that would want segments 1,2,3,4,5
417
- mockConfig,
418
- stateWithActiveRequests,
419
- 10000, // durationMs
420
- mockSignal,
421
- mockDeps,
422
- );
423
-
424
- // Should only fetch segments 3,4 (maxParallelFetches=2, skipping 1,2)
425
- expect(mockDeps.fetchSegment).toHaveBeenCalledWith(3, expect.any(Object));
426
- expect(mockDeps.fetchSegment).toHaveBeenCalledWith(4, expect.any(Object));
427
- expect(mockDeps.fetchSegment).toHaveBeenCalledTimes(2);
428
- });
429
-
430
- test("skips segments already cached", async ({
431
- mockConfig,
432
- mockDeps,
433
- mockSignal,
434
- expect,
435
- }) => {
436
- const stateWithCache = {
437
- currentSeekTimeMs: 0,
438
- activeRequests: new Set<number>(),
439
- cachedSegments: new Set([1, 3]), // segments 1,3 already cached
440
- requestQueue: [],
441
- };
442
-
443
- await manageMediaBuffer(
444
- 1000, // seeks to time that would want segments 1,2,3,4,5
445
- mockConfig,
446
- stateWithCache,
447
- 10000, // durationMs
448
- mockSignal,
449
- mockDeps,
450
- );
451
-
452
- // Should only fetch segments 2,4 (skipping cached 1,3)
453
- expect(mockDeps.fetchSegment).toHaveBeenCalledWith(2, expect.any(Object));
454
- expect(mockDeps.fetchSegment).toHaveBeenCalledWith(4, expect.any(Object));
455
- expect(mockDeps.fetchSegment).toHaveBeenCalledTimes(2);
456
- });
457
- });
458
-
459
- describe("makeVideoBufferTask", () => {
460
- beforeEach(() => {
461
- // MSW setup is now handled by test fixtures
462
- });
463
-
464
- afterEach(() => {
465
- const elements = document.querySelectorAll("test-media-video-buffer");
466
- for (const element of elements) {
467
- element.remove();
468
- }
469
- vi.restoreAllMocks();
470
- });
471
-
472
- test("creates task with correct configuration", ({ element, expect }) => {
473
- const task = makeVideoBufferTask(element);
474
-
475
- expect(task).toBeDefined();
476
- expect(task.status).toBe(TaskStatus.INITIAL);
477
- expect(task.value).toBeUndefined();
478
- expect(task.error).toBeUndefined();
479
- });
480
-
481
- test("task integrates with element seek time", ({ element, expect }) => {
482
- element.desiredSeekTimeMs = 5000;
483
-
484
- const task = makeVideoBufferTask(element);
485
- expect(task).toBeDefined();
486
- expect(task.status).toBe(TaskStatus.INITIAL);
487
- });
488
- });
489
-
490
- describe("Continuous Buffering", () => {
491
- test("enables continuous segment loading when enabled", async ({
492
- mockState,
493
- mockDeps,
494
- mockSignal,
495
- expect,
496
- }) => {
497
- const configWithContinuous = {
498
- bufferDurationMs: 10000, // 10 seconds = 10 segments
499
- maxParallelFetches: 2,
500
- enableBuffering: true,
501
- enableContinuousBuffering: true, // Enable continuous buffering
502
- };
503
-
504
- let fetchCount = 0;
505
- const mockFetchWithDelay = vi.fn().mockImplementation(() => {
506
- fetchCount++;
507
- return Promise.resolve(new ArrayBuffer(1000));
508
- });
509
-
510
- const mockDepsWithContinuous = {
511
- ...mockDeps,
512
- fetchSegment: mockFetchWithDelay,
513
- };
514
-
515
- await manageMediaBuffer(
516
- 0,
517
- configWithContinuous,
518
- mockState,
519
- 10000, // durationMs
520
- mockSignal,
521
- mockDepsWithContinuous,
522
- );
523
-
524
- // Should start with initial maxParallelFetches (2) and continue with more requests
525
- // Continuous buffering should fetch more segments as previous ones complete
526
- expect(mockFetchWithDelay).toHaveBeenCalledTimes(4); // More than initial batch due to continuous buffering
527
- expect(fetchCount).toBe(4);
528
- });
529
-
530
- test("disabled when flag is false", async ({
531
- mockState,
532
- mockDeps,
533
- mockSignal,
534
- expect,
535
- }) => {
536
- const configWithoutContinuous = {
537
- bufferDurationMs: 10000, // 10 seconds = 10 segments
538
- maxParallelFetches: 2,
539
- enableBuffering: true,
540
- enableContinuousBuffering: false, // Disable continuous buffering
541
- };
542
-
543
- await manageMediaBuffer(
544
- 0,
545
- configWithoutContinuous,
546
- mockState,
547
- 10000, // durationMs
548
- mockSignal,
549
- mockDeps,
550
- );
551
-
552
- // Should only fetch initial maxParallelFetches and stop
553
- expect(mockDeps.fetchSegment).toHaveBeenCalledTimes(2);
554
- });
555
- });
@@ -1,59 +0,0 @@
1
- import { TaskStatus } from "@lit/task";
2
- import { customElement } from "lit/decorators.js";
3
- import { afterEach, beforeEach, describe, vi } from "vitest";
4
- import { test as baseTest } from "../../../../test/useMSW.js";
5
- import { EFVideo } from "../../EFVideo";
6
- import { makeVideoInitSegmentFetchTask } from "./makeVideoInitSegmentFetchTask";
7
-
8
- @customElement("test-media-video-init-segment-fetch")
9
- class TestMediaVideoInitSegmentFetch extends EFVideo {}
10
-
11
- declare global {
12
- interface HTMLElementTagNameMap {
13
- "test-media-video-init-segment-fetch": TestMediaVideoInitSegmentFetch;
14
- }
15
- }
16
-
17
- const test = baseTest.extend<{
18
- element: TestMediaVideoInitSegmentFetch;
19
- }>({
20
- element: async ({}, use) => {
21
- const element = document.createElement(
22
- "test-media-video-init-segment-fetch",
23
- );
24
- await use(element);
25
- element.remove();
26
- },
27
- });
28
-
29
- describe("makeVideoInitSegmentFetchTask", () => {
30
- beforeEach(() => {
31
- // MSW setup is now handled by test fixtures
32
- });
33
-
34
- afterEach(() => {
35
- const elements = document.querySelectorAll(
36
- "test-media-video-init-segment-fetch",
37
- );
38
- for (const element of elements) {
39
- element.remove();
40
- }
41
- vi.restoreAllMocks();
42
- });
43
-
44
- test("creates task with correct initial state", ({ element, expect }) => {
45
- const task = makeVideoInitSegmentFetchTask(element);
46
-
47
- expect(task).toBeDefined();
48
- expect(task.status).toBe(TaskStatus.INITIAL);
49
- expect(task.value).toBeUndefined();
50
- expect(task.error).toBeUndefined();
51
- });
52
-
53
- test("task integrates with element properties", ({ element, expect }) => {
54
- const task = makeVideoInitSegmentFetchTask(element);
55
-
56
- expect(task).toBeDefined();
57
- expect(task.status).toBe(TaskStatus.INITIAL);
58
- });
59
- });
@@ -1,55 +0,0 @@
1
- import { TaskStatus } from "@lit/task";
2
- import { customElement } from "lit/decorators.js";
3
- import { afterEach, beforeEach, describe, vi } from "vitest";
4
- import { test as baseTest } from "../../../../test/useMSW.js";
5
- import { EFVideo } from "../../EFVideo";
6
- import { makeVideoInputTask } from "./makeVideoInputTask";
7
-
8
- @customElement("test-media-video-input")
9
- class TestMediaVideoInput extends EFVideo {}
10
-
11
- declare global {
12
- interface HTMLElementTagNameMap {
13
- "test-media-video-input": TestMediaVideoInput;
14
- }
15
- }
16
-
17
- const test = baseTest.extend<{
18
- element: TestMediaVideoInput;
19
- }>({
20
- element: async ({}, use) => {
21
- const element = document.createElement("test-media-video-input");
22
- await use(element);
23
- element.remove();
24
- },
25
- });
26
-
27
- describe("makeVideoInputTask", () => {
28
- beforeEach(() => {
29
- // MSW setup is now handled by test fixtures
30
- });
31
-
32
- afterEach(() => {
33
- const elements = document.querySelectorAll("test-media-video-input");
34
- for (const element of elements) {
35
- element.remove();
36
- }
37
- vi.restoreAllMocks();
38
- });
39
-
40
- test("creates task with correct initial state", ({ element, expect }) => {
41
- const task = makeVideoInputTask(element);
42
-
43
- expect(task).toBeDefined();
44
- expect(task.status).toBe(TaskStatus.INITIAL);
45
- expect(task.value).toBeUndefined();
46
- expect(task.error).toBeUndefined();
47
- });
48
-
49
- test("task integrates with element properties", ({ element, expect }) => {
50
- const task = makeVideoInputTask(element);
51
-
52
- expect(task).toBeDefined();
53
- expect(task.status).toBe(TaskStatus.INITIAL);
54
- });
55
- });