@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.
- package/package.json +2 -2
- package/scripts/build-css.js +3 -3
- package/tsdown.config.ts +1 -1
- package/src/elements/ContextProxiesController.ts +0 -124
- package/src/elements/CrossUpdateController.ts +0 -22
- package/src/elements/EFAudio.browsertest.ts +0 -706
- package/src/elements/EFAudio.ts +0 -56
- package/src/elements/EFCaptions.browsertest.ts +0 -1960
- package/src/elements/EFCaptions.ts +0 -823
- package/src/elements/EFImage.browsertest.ts +0 -120
- package/src/elements/EFImage.ts +0 -113
- package/src/elements/EFMedia/AssetIdMediaEngine.test.ts +0 -224
- package/src/elements/EFMedia/AssetIdMediaEngine.ts +0 -110
- package/src/elements/EFMedia/AssetMediaEngine.browsertest.ts +0 -140
- package/src/elements/EFMedia/AssetMediaEngine.ts +0 -385
- package/src/elements/EFMedia/BaseMediaEngine.browsertest.ts +0 -400
- package/src/elements/EFMedia/BaseMediaEngine.ts +0 -505
- package/src/elements/EFMedia/BufferedSeekingInput.browsertest.ts +0 -386
- package/src/elements/EFMedia/BufferedSeekingInput.ts +0 -430
- package/src/elements/EFMedia/JitMediaEngine.browsertest.ts +0 -226
- package/src/elements/EFMedia/JitMediaEngine.ts +0 -256
- package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.browsertest.ts +0 -679
- package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.ts +0 -117
- package/src/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.ts +0 -246
- package/src/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.browsertest.ts +0 -59
- package/src/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.ts +0 -27
- package/src/elements/EFMedia/audioTasks/makeAudioInputTask.browsertest.ts +0 -55
- package/src/elements/EFMedia/audioTasks/makeAudioInputTask.ts +0 -53
- package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.chunkboundary.regression.browsertest.ts +0 -207
- package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.ts +0 -72
- package/src/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.ts +0 -32
- package/src/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.ts +0 -29
- package/src/elements/EFMedia/audioTasks/makeAudioTasksVideoOnly.browsertest.ts +0 -95
- package/src/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.ts +0 -184
- package/src/elements/EFMedia/shared/AudioSpanUtils.ts +0 -129
- package/src/elements/EFMedia/shared/BufferUtils.ts +0 -342
- package/src/elements/EFMedia/shared/GlobalInputCache.ts +0 -77
- package/src/elements/EFMedia/shared/MediaTaskUtils.ts +0 -44
- package/src/elements/EFMedia/shared/PrecisionUtils.ts +0 -46
- package/src/elements/EFMedia/shared/RenditionHelpers.browsertest.ts +0 -246
- package/src/elements/EFMedia/shared/RenditionHelpers.ts +0 -56
- package/src/elements/EFMedia/shared/ThumbnailExtractor.ts +0 -227
- package/src/elements/EFMedia/tasks/makeMediaEngineTask.browsertest.ts +0 -167
- package/src/elements/EFMedia/tasks/makeMediaEngineTask.ts +0 -88
- package/src/elements/EFMedia/videoTasks/MainVideoInputCache.ts +0 -76
- package/src/elements/EFMedia/videoTasks/ScrubInputCache.ts +0 -61
- package/src/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.ts +0 -114
- package/src/elements/EFMedia/videoTasks/makeScrubVideoInitSegmentFetchTask.ts +0 -35
- package/src/elements/EFMedia/videoTasks/makeScrubVideoInputTask.ts +0 -52
- package/src/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.ts +0 -124
- package/src/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.ts +0 -44
- package/src/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.ts +0 -32
- package/src/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.ts +0 -370
- package/src/elements/EFMedia/videoTasks/makeVideoBufferTask.ts +0 -109
- package/src/elements/EFMedia.browsertest.ts +0 -872
- package/src/elements/EFMedia.ts +0 -341
- package/src/elements/EFSourceMixin.ts +0 -60
- package/src/elements/EFSurface.browsertest.ts +0 -151
- package/src/elements/EFSurface.ts +0 -142
- package/src/elements/EFTemporal.browsertest.ts +0 -215
- package/src/elements/EFTemporal.ts +0 -800
- package/src/elements/EFThumbnailStrip.browsertest.ts +0 -585
- package/src/elements/EFThumbnailStrip.media-engine.browsertest.ts +0 -714
- package/src/elements/EFThumbnailStrip.ts +0 -906
- package/src/elements/EFTimegroup.browsertest.ts +0 -934
- package/src/elements/EFTimegroup.ts +0 -882
- package/src/elements/EFVideo.browsertest.ts +0 -1482
- package/src/elements/EFVideo.ts +0 -564
- package/src/elements/EFWaveform.ts +0 -547
- package/src/elements/FetchContext.browsertest.ts +0 -401
- package/src/elements/FetchMixin.ts +0 -38
- package/src/elements/SampleBuffer.ts +0 -94
- package/src/elements/TargetController.browsertest.ts +0 -230
- package/src/elements/TargetController.ts +0 -224
- package/src/elements/TimegroupController.ts +0 -26
- package/src/elements/durationConverter.ts +0 -35
- package/src/elements/parseTimeToMs.ts +0 -9
- package/src/elements/printTaskStatus.ts +0 -16
- package/src/elements/renderTemporalAudio.ts +0 -108
- package/src/elements/updateAnimations.browsertest.ts +0 -1884
- package/src/elements/updateAnimations.ts +0 -217
- package/src/elements/util.ts +0 -24
- package/src/gui/ContextMixin.browsertest.ts +0 -860
- package/src/gui/ContextMixin.ts +0 -562
- package/src/gui/Controllable.browsertest.ts +0 -258
- package/src/gui/Controllable.ts +0 -41
- package/src/gui/EFConfiguration.ts +0 -40
- package/src/gui/EFControls.browsertest.ts +0 -389
- package/src/gui/EFControls.ts +0 -195
- package/src/gui/EFDial.browsertest.ts +0 -84
- package/src/gui/EFDial.ts +0 -172
- package/src/gui/EFFilmstrip.browsertest.ts +0 -712
- package/src/gui/EFFilmstrip.ts +0 -1349
- package/src/gui/EFFitScale.ts +0 -152
- package/src/gui/EFFocusOverlay.ts +0 -79
- package/src/gui/EFPause.browsertest.ts +0 -202
- package/src/gui/EFPause.ts +0 -73
- package/src/gui/EFPlay.browsertest.ts +0 -202
- package/src/gui/EFPlay.ts +0 -73
- package/src/gui/EFPreview.ts +0 -74
- package/src/gui/EFResizableBox.browsertest.ts +0 -79
- package/src/gui/EFResizableBox.ts +0 -898
- package/src/gui/EFScrubber.ts +0 -151
- package/src/gui/EFTimeDisplay.browsertest.ts +0 -237
- package/src/gui/EFTimeDisplay.ts +0 -55
- package/src/gui/EFToggleLoop.ts +0 -35
- package/src/gui/EFTogglePlay.ts +0 -70
- package/src/gui/EFWorkbench.ts +0 -115
- package/src/gui/PlaybackController.ts +0 -527
- package/src/gui/TWMixin.css +0 -6
- package/src/gui/TWMixin.ts +0 -61
- package/src/gui/TargetOrContextMixin.ts +0 -185
- package/src/gui/currentTimeContext.ts +0 -5
- package/src/gui/durationContext.ts +0 -3
- package/src/gui/efContext.ts +0 -6
- package/src/gui/fetchContext.ts +0 -5
- package/src/gui/focusContext.ts +0 -7
- package/src/gui/focusedElementContext.ts +0 -5
- package/src/gui/playingContext.ts +0 -5
- package/src/otel/BridgeSpanExporter.ts +0 -150
- package/src/otel/setupBrowserTracing.ts +0 -73
- package/src/otel/tracingHelpers.ts +0 -251
- package/src/transcoding/cache/RequestDeduplicator.test.ts +0 -170
- package/src/transcoding/cache/RequestDeduplicator.ts +0 -65
- package/src/transcoding/cache/URLTokenDeduplicator.test.ts +0 -182
- package/src/transcoding/cache/URLTokenDeduplicator.ts +0 -101
- package/src/transcoding/types/index.ts +0 -312
- package/src/transcoding/utils/MediaUtils.ts +0 -63
- package/src/transcoding/utils/UrlGenerator.ts +0 -68
- package/src/transcoding/utils/constants.ts +0 -36
- package/src/utils/LRUCache.test.ts +0 -274
- package/src/utils/LRUCache.ts +0 -696
|
@@ -1,714 +0,0 @@
|
|
|
1
|
-
import { beforeAll, beforeEach, describe, vi } from "vitest";
|
|
2
|
-
import { test as baseTest } from "../../test/useMSW.js";
|
|
3
|
-
import type { EFTimegroup } from "./EFTimegroup.js";
|
|
4
|
-
import type { EFVideo } from "./EFVideo.js";
|
|
5
|
-
import "./EFVideo.js";
|
|
6
|
-
import "./EFTimegroup.js";
|
|
7
|
-
import "./EFThumbnailStrip.js"; // Import to register the custom element
|
|
8
|
-
import "../gui/EFWorkbench.js";
|
|
9
|
-
import "../gui/EFPreview.js";
|
|
10
|
-
import type { EFConfiguration } from "../gui/EFConfiguration.js";
|
|
11
|
-
import { AssetMediaEngine } from "./EFMedia/AssetMediaEngine.js";
|
|
12
|
-
import { JitMediaEngine } from "./EFMedia/JitMediaEngine.js";
|
|
13
|
-
|
|
14
|
-
beforeAll(async () => {
|
|
15
|
-
console.clear();
|
|
16
|
-
await fetch("/@ef-clear-cache", {
|
|
17
|
-
method: "DELETE",
|
|
18
|
-
});
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
beforeEach(() => {
|
|
22
|
-
localStorage.clear();
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
const test = baseTest.extend<{
|
|
26
|
-
configuration: EFConfiguration;
|
|
27
|
-
timegroup: EFTimegroup;
|
|
28
|
-
jitVideo: EFVideo;
|
|
29
|
-
assetVideo: EFVideo;
|
|
30
|
-
}>({
|
|
31
|
-
configuration: async ({ expect }, use) => {
|
|
32
|
-
const configuration = document.createElement("ef-configuration");
|
|
33
|
-
configuration.innerHTML = `<h1 style="font: 10px monospace">${expect.getState().currentTestName}</h1>`;
|
|
34
|
-
// Use integrated proxy server (same host/port as test runner)
|
|
35
|
-
const apiHost = `${window.location.protocol}//${window.location.host}`;
|
|
36
|
-
configuration.setAttribute("api-host", apiHost);
|
|
37
|
-
configuration.apiHost = apiHost;
|
|
38
|
-
configuration.signingURL = "";
|
|
39
|
-
document.body.appendChild(configuration);
|
|
40
|
-
await use(configuration);
|
|
41
|
-
},
|
|
42
|
-
timegroup: async ({}, use) => {
|
|
43
|
-
const timegroup = document.createElement("ef-timegroup");
|
|
44
|
-
timegroup.setAttribute("mode", "contain");
|
|
45
|
-
await use(timegroup);
|
|
46
|
-
},
|
|
47
|
-
jitVideo: async ({ configuration, timegroup }, use) => {
|
|
48
|
-
const video = document.createElement("ef-video");
|
|
49
|
-
video.src = "http://web:3000/head-moov-480p.mp4";
|
|
50
|
-
timegroup.appendChild(video);
|
|
51
|
-
configuration.appendChild(timegroup);
|
|
52
|
-
await video.mediaEngineTask.run();
|
|
53
|
-
await use(video);
|
|
54
|
-
},
|
|
55
|
-
assetVideo: async ({ configuration, timegroup }, use) => {
|
|
56
|
-
const video = document.createElement("ef-video");
|
|
57
|
-
video.src = "bars-n-tone.mp4";
|
|
58
|
-
timegroup.appendChild(video);
|
|
59
|
-
configuration.appendChild(timegroup);
|
|
60
|
-
await video.mediaEngineTask.run();
|
|
61
|
-
await use(video);
|
|
62
|
-
},
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
describe("MediaEngine Thumbnail Extraction", () => {
|
|
66
|
-
describe("JitMediaEngine", () => {
|
|
67
|
-
test("initializes with JitMediaEngine", async ({ jitVideo, expect }) => {
|
|
68
|
-
const mediaEngine = jitVideo.mediaEngineTask.value;
|
|
69
|
-
expect(mediaEngine).toBeInstanceOf(JitMediaEngine);
|
|
70
|
-
expect(jitVideo.intrinsicDurationMs).toBe(10_000);
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
test("extracts single thumbnail at timestamp", async ({
|
|
74
|
-
jitVideo,
|
|
75
|
-
expect,
|
|
76
|
-
}) => {
|
|
77
|
-
const mediaEngine = jitVideo.mediaEngineTask.value!;
|
|
78
|
-
expect(mediaEngine).toBeInstanceOf(JitMediaEngine);
|
|
79
|
-
|
|
80
|
-
const timestamps = [2000]; // 2 seconds
|
|
81
|
-
const thumbnails = await mediaEngine.extractThumbnails(timestamps);
|
|
82
|
-
|
|
83
|
-
expect(thumbnails).toHaveLength(1);
|
|
84
|
-
expect(thumbnails[0]).toBeTruthy();
|
|
85
|
-
expect(thumbnails[0]?.timestamp).toBe(2000);
|
|
86
|
-
expect(thumbnails[0]?.thumbnail).toBeDefined();
|
|
87
|
-
|
|
88
|
-
// Verify it's a valid canvas
|
|
89
|
-
const canvas = thumbnails[0]!.thumbnail;
|
|
90
|
-
expect(
|
|
91
|
-
canvas instanceof HTMLCanvasElement ||
|
|
92
|
-
canvas instanceof OffscreenCanvas,
|
|
93
|
-
).toBe(true);
|
|
94
|
-
expect(canvas.width).toBeGreaterThan(0);
|
|
95
|
-
expect(canvas.height).toBeGreaterThan(0);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
test("extracts multiple thumbnails in batch", async ({
|
|
99
|
-
jitVideo,
|
|
100
|
-
expect,
|
|
101
|
-
}) => {
|
|
102
|
-
const mediaEngine = jitVideo.mediaEngineTask.value!;
|
|
103
|
-
expect(mediaEngine).toBeInstanceOf(JitMediaEngine);
|
|
104
|
-
|
|
105
|
-
const timestamps = [1000, 3000, 5000, 7000]; // Multiple timestamps
|
|
106
|
-
const thumbnails = await mediaEngine.extractThumbnails(timestamps);
|
|
107
|
-
|
|
108
|
-
expect(thumbnails).toHaveLength(4);
|
|
109
|
-
|
|
110
|
-
for (let i = 0; i < timestamps.length; i++) {
|
|
111
|
-
const thumbnail = thumbnails[i];
|
|
112
|
-
expect(thumbnail).toBeTruthy();
|
|
113
|
-
expect(thumbnail?.timestamp).toBe(timestamps[i]);
|
|
114
|
-
|
|
115
|
-
const canvas = thumbnail!.thumbnail;
|
|
116
|
-
expect(
|
|
117
|
-
canvas instanceof HTMLCanvasElement ||
|
|
118
|
-
canvas instanceof OffscreenCanvas,
|
|
119
|
-
).toBe(true);
|
|
120
|
-
expect(canvas.width).toBeGreaterThan(0);
|
|
121
|
-
expect(canvas.height).toBeGreaterThan(0);
|
|
122
|
-
}
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
test("handles timestamps in same segment efficiently", async ({
|
|
126
|
-
jitVideo,
|
|
127
|
-
expect,
|
|
128
|
-
}) => {
|
|
129
|
-
const mediaEngine = jitVideo.mediaEngineTask.value as JitMediaEngine;
|
|
130
|
-
expect(mediaEngine).toBeInstanceOf(JitMediaEngine);
|
|
131
|
-
|
|
132
|
-
// Get segment duration to ensure timestamps are in same segment
|
|
133
|
-
const videoRendition =
|
|
134
|
-
mediaEngine.getScrubVideoRendition() || mediaEngine.getVideoRendition();
|
|
135
|
-
expect(videoRendition).toBeDefined();
|
|
136
|
-
const segmentDurationMs = videoRendition!.segmentDurationMs || 2000;
|
|
137
|
-
|
|
138
|
-
// Pick timestamps within the first segment - avoid edge cases near boundaries
|
|
139
|
-
const timestamps = [
|
|
140
|
-
100,
|
|
141
|
-
500,
|
|
142
|
-
1000,
|
|
143
|
-
Math.min(1500, segmentDurationMs - 200),
|
|
144
|
-
];
|
|
145
|
-
const thumbnails = await mediaEngine.extractThumbnails(timestamps);
|
|
146
|
-
|
|
147
|
-
expect(thumbnails).toHaveLength(4);
|
|
148
|
-
|
|
149
|
-
// Most should succeed since they're in the same segment
|
|
150
|
-
const successfulThumbnails = thumbnails.filter((t) => t !== null);
|
|
151
|
-
expect(successfulThumbnails.length).toBeGreaterThan(2); // At least 3 out of 4
|
|
152
|
-
|
|
153
|
-
for (const thumbnail of successfulThumbnails) {
|
|
154
|
-
expect(thumbnail!.thumbnail).toBeDefined();
|
|
155
|
-
}
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
test("handles timestamps across different segments", async ({
|
|
159
|
-
jitVideo,
|
|
160
|
-
expect,
|
|
161
|
-
}) => {
|
|
162
|
-
const mediaEngine = jitVideo.mediaEngineTask.value as JitMediaEngine;
|
|
163
|
-
expect(mediaEngine).toBeInstanceOf(JitMediaEngine);
|
|
164
|
-
|
|
165
|
-
// Pick timestamps that span multiple segments
|
|
166
|
-
const timestamps = [500, 2500, 4500, 6500, 8500]; // Across different 2s segments
|
|
167
|
-
const thumbnails = await mediaEngine.extractThumbnails(timestamps);
|
|
168
|
-
|
|
169
|
-
expect(thumbnails).toHaveLength(5);
|
|
170
|
-
|
|
171
|
-
// All should succeed
|
|
172
|
-
for (let i = 0; i < timestamps.length; i++) {
|
|
173
|
-
const thumbnail = thumbnails[i];
|
|
174
|
-
expect(thumbnail).toBeTruthy();
|
|
175
|
-
expect(thumbnail?.timestamp).toBe(timestamps[i]);
|
|
176
|
-
}
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
test("handles invalid timestamps gracefully", async ({
|
|
180
|
-
jitVideo,
|
|
181
|
-
expect,
|
|
182
|
-
}) => {
|
|
183
|
-
const mediaEngine = jitVideo.mediaEngineTask.value!;
|
|
184
|
-
expect(mediaEngine).toBeInstanceOf(JitMediaEngine);
|
|
185
|
-
|
|
186
|
-
const timestamps = [-1000, 15000]; // Before start and after end
|
|
187
|
-
const thumbnails = await mediaEngine.extractThumbnails(timestamps);
|
|
188
|
-
|
|
189
|
-
expect(thumbnails).toHaveLength(2);
|
|
190
|
-
|
|
191
|
-
// Invalid timestamps should return null
|
|
192
|
-
expect(thumbnails[0]).toBeNull();
|
|
193
|
-
expect(thumbnails[1]).toBeNull();
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
test("handles mix of valid and invalid timestamps", async ({
|
|
197
|
-
jitVideo,
|
|
198
|
-
expect,
|
|
199
|
-
}) => {
|
|
200
|
-
const mediaEngine = jitVideo.mediaEngineTask.value!;
|
|
201
|
-
expect(mediaEngine).toBeInstanceOf(JitMediaEngine);
|
|
202
|
-
|
|
203
|
-
const timestamps = [-1000, 2000, 15000, 5000]; // Invalid, valid, invalid, valid
|
|
204
|
-
const thumbnails = await mediaEngine.extractThumbnails(timestamps);
|
|
205
|
-
|
|
206
|
-
expect(thumbnails).toHaveLength(4);
|
|
207
|
-
|
|
208
|
-
expect(thumbnails[0]).toBeNull(); // Invalid
|
|
209
|
-
expect(thumbnails[1]).toBeTruthy(); // Valid
|
|
210
|
-
expect(thumbnails[1]?.timestamp).toBe(2000);
|
|
211
|
-
expect(thumbnails[2]).toBeNull(); // Invalid
|
|
212
|
-
expect(thumbnails[3]).toBeTruthy(); // Valid
|
|
213
|
-
expect(thumbnails[3]?.timestamp).toBe(5000);
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
test("handles empty timestamp array", async ({ jitVideo, expect }) => {
|
|
217
|
-
const mediaEngine = jitVideo.mediaEngineTask.value!;
|
|
218
|
-
expect(mediaEngine).toBeInstanceOf(JitMediaEngine);
|
|
219
|
-
|
|
220
|
-
const timestamps: number[] = [];
|
|
221
|
-
const thumbnails = await mediaEngine.extractThumbnails(timestamps);
|
|
222
|
-
|
|
223
|
-
expect(thumbnails).toHaveLength(0);
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
test("uses scrub rendition when available", async ({
|
|
227
|
-
jitVideo,
|
|
228
|
-
expect,
|
|
229
|
-
}) => {
|
|
230
|
-
const mediaEngine = jitVideo.mediaEngineTask.value!;
|
|
231
|
-
expect(mediaEngine).toBeInstanceOf(JitMediaEngine);
|
|
232
|
-
|
|
233
|
-
// Check if scrub rendition exists
|
|
234
|
-
const scrubRendition = mediaEngine.getScrubVideoRendition();
|
|
235
|
-
const mainRendition = mediaEngine.getVideoRendition();
|
|
236
|
-
|
|
237
|
-
expect(scrubRendition).toBeDefined();
|
|
238
|
-
expect(mainRendition).toBeDefined();
|
|
239
|
-
|
|
240
|
-
// Extract thumbnail to ensure it works with scrub rendition
|
|
241
|
-
const timestamps = [3000];
|
|
242
|
-
const thumbnails = await mediaEngine.extractThumbnails(timestamps);
|
|
243
|
-
|
|
244
|
-
expect(thumbnails).toHaveLength(1);
|
|
245
|
-
expect(thumbnails[0]).toBeTruthy();
|
|
246
|
-
});
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
describe("AssetMediaEngine", () => {
|
|
250
|
-
test("initializes with AssetMediaEngine", async ({
|
|
251
|
-
assetVideo,
|
|
252
|
-
expect,
|
|
253
|
-
}) => {
|
|
254
|
-
const mediaEngine = assetVideo.mediaEngineTask.value;
|
|
255
|
-
expect(mediaEngine).toBeInstanceOf(AssetMediaEngine);
|
|
256
|
-
expect(assetVideo.intrinsicDurationMs).toBeGreaterThan(0);
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
test("attempts thumbnail extraction (currently has implementation issues)", async ({
|
|
260
|
-
assetVideo,
|
|
261
|
-
expect,
|
|
262
|
-
}) => {
|
|
263
|
-
const mediaEngine = assetVideo.mediaEngineTask.value!;
|
|
264
|
-
expect(mediaEngine).toBeInstanceOf(AssetMediaEngine);
|
|
265
|
-
|
|
266
|
-
const timestamps = [2000]; // 2 seconds
|
|
267
|
-
const thumbnails = await mediaEngine.extractThumbnails(timestamps);
|
|
268
|
-
|
|
269
|
-
expect(thumbnails).toHaveLength(1);
|
|
270
|
-
|
|
271
|
-
// NOTE: AssetMediaEngine thumbnail extraction currently has issues
|
|
272
|
-
// This test documents the current behavior for the refactor
|
|
273
|
-
if (thumbnails[0]) {
|
|
274
|
-
expect(thumbnails[0].timestamp).toBe(2000);
|
|
275
|
-
expect(thumbnails[0].thumbnail).toBeDefined();
|
|
276
|
-
|
|
277
|
-
const canvas = thumbnails[0].thumbnail;
|
|
278
|
-
expect(
|
|
279
|
-
canvas instanceof HTMLCanvasElement ||
|
|
280
|
-
canvas instanceof OffscreenCanvas,
|
|
281
|
-
).toBe(true);
|
|
282
|
-
expect(canvas.width).toBeGreaterThan(0);
|
|
283
|
-
expect(canvas.height).toBeGreaterThan(0);
|
|
284
|
-
}
|
|
285
|
-
// If it returns null, that's also acceptable given current implementation issues
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
test("attempts batch thumbnail extraction (documents current behavior)", async ({
|
|
289
|
-
assetVideo,
|
|
290
|
-
expect,
|
|
291
|
-
}) => {
|
|
292
|
-
const mediaEngine = assetVideo.mediaEngineTask.value!;
|
|
293
|
-
expect(mediaEngine).toBeInstanceOf(AssetMediaEngine);
|
|
294
|
-
|
|
295
|
-
const timestamps = [1000, 3000, 5000, 7000]; // Multiple timestamps
|
|
296
|
-
const thumbnails = await mediaEngine.extractThumbnails(timestamps);
|
|
297
|
-
|
|
298
|
-
expect(thumbnails).toHaveLength(4);
|
|
299
|
-
|
|
300
|
-
// Document current behavior - some may be null due to implementation issues
|
|
301
|
-
let successCount = 0;
|
|
302
|
-
for (let i = 0; i < timestamps.length; i++) {
|
|
303
|
-
const thumbnail = thumbnails[i];
|
|
304
|
-
if (thumbnail) {
|
|
305
|
-
successCount++;
|
|
306
|
-
expect(thumbnail.timestamp).toBe(timestamps[i]);
|
|
307
|
-
|
|
308
|
-
const canvas = thumbnail.thumbnail;
|
|
309
|
-
expect(
|
|
310
|
-
canvas instanceof HTMLCanvasElement ||
|
|
311
|
-
canvas instanceof OffscreenCanvas,
|
|
312
|
-
).toBe(true);
|
|
313
|
-
expect(canvas.width).toBeGreaterThan(0);
|
|
314
|
-
expect(canvas.height).toBeGreaterThan(0);
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
// Track success rate for refactor planning
|
|
319
|
-
console.log(
|
|
320
|
-
`AssetMediaEngine batch extraction: ${successCount}/${timestamps.length} successful`,
|
|
321
|
-
);
|
|
322
|
-
});
|
|
323
|
-
|
|
324
|
-
test("documents that AssetMediaEngine is not yet supported", async ({
|
|
325
|
-
assetVideo,
|
|
326
|
-
expect,
|
|
327
|
-
}) => {
|
|
328
|
-
const mediaEngine = assetVideo.mediaEngineTask.value as AssetMediaEngine;
|
|
329
|
-
expect(mediaEngine).toBeInstanceOf(AssetMediaEngine);
|
|
330
|
-
|
|
331
|
-
// AssetMediaEngine now properly returns nulls for all requests
|
|
332
|
-
const timestamps = [500, 2500, 4500, 6500];
|
|
333
|
-
const thumbnails = await mediaEngine.extractThumbnails(timestamps);
|
|
334
|
-
|
|
335
|
-
expect(thumbnails).toHaveLength(4);
|
|
336
|
-
|
|
337
|
-
// All should be null since AssetMediaEngine is not yet supported
|
|
338
|
-
const successfulThumbnails = thumbnails.filter((t) => t !== null);
|
|
339
|
-
expect(successfulThumbnails.length).toBe(0); // Consistent behavior now
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
test("handles invalid timestamps (reveals current boundary issues)", async ({
|
|
343
|
-
assetVideo,
|
|
344
|
-
expect,
|
|
345
|
-
}) => {
|
|
346
|
-
const mediaEngine = assetVideo.mediaEngineTask.value!;
|
|
347
|
-
expect(mediaEngine).toBeInstanceOf(AssetMediaEngine);
|
|
348
|
-
|
|
349
|
-
const timestamps = [-1000, 50000]; // Before start and well after end
|
|
350
|
-
const thumbnails = await mediaEngine.extractThumbnails(timestamps);
|
|
351
|
-
|
|
352
|
-
expect(thumbnails).toHaveLength(2);
|
|
353
|
-
|
|
354
|
-
// Document current behavior - there seem to be boundary checking issues
|
|
355
|
-
// that should be fixed in the refactor
|
|
356
|
-
console.log(
|
|
357
|
-
`Invalid timestamps result: ${thumbnails[0] ? "non-null" : "null"}, ${thumbnails[1] ? "non-null" : "null"}`,
|
|
358
|
-
);
|
|
359
|
-
|
|
360
|
-
// At minimum, negative timestamps should be null
|
|
361
|
-
expect(thumbnails[0]).toBeNull();
|
|
362
|
-
|
|
363
|
-
// The second one might unexpectedly succeed due to current implementation issues
|
|
364
|
-
// This documents the current behavior for the refactor
|
|
365
|
-
if (thumbnails[1]) {
|
|
366
|
-
console.log(
|
|
367
|
-
"WARNING: Timestamp 50000ms unexpectedly returned a thumbnail - boundary checking issue",
|
|
368
|
-
);
|
|
369
|
-
}
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
test("no scrub rendition fallback to main video (documents current behavior)", async ({
|
|
373
|
-
assetVideo,
|
|
374
|
-
expect,
|
|
375
|
-
}) => {
|
|
376
|
-
const mediaEngine = assetVideo.mediaEngineTask.value as AssetMediaEngine;
|
|
377
|
-
expect(mediaEngine).toBeInstanceOf(AssetMediaEngine);
|
|
378
|
-
|
|
379
|
-
// AssetMediaEngine doesn't have scrub rendition
|
|
380
|
-
const scrubRendition = mediaEngine.getScrubVideoRendition();
|
|
381
|
-
expect(scrubRendition).toBeUndefined();
|
|
382
|
-
|
|
383
|
-
// Attempt to extract thumbnails using main video rendition
|
|
384
|
-
const timestamps = [2000];
|
|
385
|
-
const thumbnails = await mediaEngine.extractThumbnails(timestamps);
|
|
386
|
-
|
|
387
|
-
expect(thumbnails).toHaveLength(1);
|
|
388
|
-
|
|
389
|
-
// Document current behavior - may return null due to implementation issues
|
|
390
|
-
if (thumbnails[0]) {
|
|
391
|
-
expect(thumbnails[0].timestamp).toBe(2000);
|
|
392
|
-
expect(thumbnails[0].thumbnail).toBeDefined();
|
|
393
|
-
} else {
|
|
394
|
-
console.log(
|
|
395
|
-
"AssetMediaEngine fallback to main video rendition currently returns null",
|
|
396
|
-
);
|
|
397
|
-
}
|
|
398
|
-
});
|
|
399
|
-
|
|
400
|
-
test("documents segment boundary behavior for refactor", async ({
|
|
401
|
-
assetVideo,
|
|
402
|
-
expect,
|
|
403
|
-
}) => {
|
|
404
|
-
const mediaEngine = assetVideo.mediaEngineTask.value as AssetMediaEngine;
|
|
405
|
-
expect(mediaEngine).toBeInstanceOf(AssetMediaEngine);
|
|
406
|
-
|
|
407
|
-
// Test around known segment boundaries from bars-n-tone.mp4
|
|
408
|
-
// These are approximate - the actual boundaries depend on the asset
|
|
409
|
-
const timestamps = [2066, 4033, 6066, 8033];
|
|
410
|
-
const thumbnails = await mediaEngine.extractThumbnails(timestamps);
|
|
411
|
-
|
|
412
|
-
expect(thumbnails).toHaveLength(4);
|
|
413
|
-
|
|
414
|
-
// Document current success rate for refactor planning
|
|
415
|
-
const successfulThumbnails = thumbnails.filter((t) => t !== null);
|
|
416
|
-
console.log(
|
|
417
|
-
`AssetMediaEngine segment boundary test: ${successfulThumbnails.length}/${timestamps.length} successful`,
|
|
418
|
-
);
|
|
419
|
-
|
|
420
|
-
// Current implementation may have issues - document for refactor
|
|
421
|
-
// In an ideal implementation, most of these should succeed
|
|
422
|
-
for (const thumbnail of successfulThumbnails) {
|
|
423
|
-
expect(thumbnail.thumbnail).toBeDefined();
|
|
424
|
-
const canvas = thumbnail.thumbnail;
|
|
425
|
-
expect(
|
|
426
|
-
canvas instanceof HTMLCanvasElement ||
|
|
427
|
-
canvas instanceof OffscreenCanvas,
|
|
428
|
-
).toBe(true);
|
|
429
|
-
}
|
|
430
|
-
});
|
|
431
|
-
});
|
|
432
|
-
|
|
433
|
-
describe("AssetMediaEngine Incompatibility Warning", () => {
|
|
434
|
-
test("logs warning when EFThumbnailStrip targets AssetMediaEngine", async ({
|
|
435
|
-
assetVideo,
|
|
436
|
-
expect,
|
|
437
|
-
}) => {
|
|
438
|
-
// Spy on console.warn to capture the warning
|
|
439
|
-
const consoleSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
440
|
-
|
|
441
|
-
// Create a thumbnail strip and add it to DOM so it gets properly initialized
|
|
442
|
-
const thumbnailStrip = document.createElement("ef-thumbnail-strip");
|
|
443
|
-
thumbnailStrip.thumbnailWidth = 80;
|
|
444
|
-
document.body.appendChild(thumbnailStrip);
|
|
445
|
-
|
|
446
|
-
// Wait for both elements to complete their setup
|
|
447
|
-
await Promise.all([
|
|
448
|
-
assetVideo.updateComplete,
|
|
449
|
-
thumbnailStrip.updateComplete,
|
|
450
|
-
]);
|
|
451
|
-
|
|
452
|
-
// Directly set the target element to bypass TargetController complexity in tests
|
|
453
|
-
assetVideo.id = "asset-video"; // For the warning message
|
|
454
|
-
thumbnailStrip.targetElement = assetVideo;
|
|
455
|
-
|
|
456
|
-
// Trigger the layout task through the normal flow by setting stripWidth
|
|
457
|
-
// This mimics what ResizeObserver would do and triggers the warning
|
|
458
|
-
(thumbnailStrip as any).stripWidth = 400;
|
|
459
|
-
|
|
460
|
-
// Wait for the warning to be logged using vi.waitFor for event-driven testing
|
|
461
|
-
await vi.waitFor(
|
|
462
|
-
() => {
|
|
463
|
-
expect(consoleSpy).toHaveBeenCalledWith(
|
|
464
|
-
expect.stringContaining(
|
|
465
|
-
"AssetMediaEngine: extractThumbnails not properly implemented",
|
|
466
|
-
),
|
|
467
|
-
);
|
|
468
|
-
},
|
|
469
|
-
{ timeout: 2000 },
|
|
470
|
-
);
|
|
471
|
-
|
|
472
|
-
// Clean up
|
|
473
|
-
thumbnailStrip.remove();
|
|
474
|
-
|
|
475
|
-
// Restore console.warn
|
|
476
|
-
consoleSpy.mockRestore();
|
|
477
|
-
});
|
|
478
|
-
|
|
479
|
-
test("does NOT log warning when EFThumbnailStrip targets JitMediaEngine", async ({
|
|
480
|
-
jitVideo,
|
|
481
|
-
expect,
|
|
482
|
-
}) => {
|
|
483
|
-
// Spy on console.warn to ensure no warning is logged
|
|
484
|
-
const consoleSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
485
|
-
|
|
486
|
-
// Create a thumbnail strip and add it to DOM so it gets properly initialized
|
|
487
|
-
const thumbnailStrip = document.createElement("ef-thumbnail-strip");
|
|
488
|
-
thumbnailStrip.thumbnailWidth = 80;
|
|
489
|
-
document.body.appendChild(thumbnailStrip);
|
|
490
|
-
|
|
491
|
-
// Wait for elements to complete setup
|
|
492
|
-
await Promise.all([
|
|
493
|
-
jitVideo.updateComplete,
|
|
494
|
-
thumbnailStrip.updateComplete,
|
|
495
|
-
]);
|
|
496
|
-
|
|
497
|
-
// Directly set the target element to bypass TargetController complexity in tests
|
|
498
|
-
jitVideo.id = "jit-video"; // For consistency
|
|
499
|
-
thumbnailStrip.targetElement = jitVideo;
|
|
500
|
-
|
|
501
|
-
// Trigger the layout task through the normal flow by setting stripWidth
|
|
502
|
-
(thumbnailStrip as any).stripWidth = 400;
|
|
503
|
-
|
|
504
|
-
// Wait for the layout task to complete using vi.waitFor
|
|
505
|
-
await vi.waitFor(
|
|
506
|
-
() => {
|
|
507
|
-
// @ts-expect-error testing private task
|
|
508
|
-
const layout = thumbnailStrip.thumbnailLayoutTask?.value;
|
|
509
|
-
expect(layout?.count).toBeGreaterThan(0);
|
|
510
|
-
},
|
|
511
|
-
{ timeout: 2000 },
|
|
512
|
-
);
|
|
513
|
-
|
|
514
|
-
// Check that NO AssetMediaEngine warning was logged
|
|
515
|
-
const warningCalls = consoleSpy.mock.calls.filter((call) =>
|
|
516
|
-
call[0].includes("AssetMediaEngine is not currently supported"),
|
|
517
|
-
);
|
|
518
|
-
expect(warningCalls).toHaveLength(0);
|
|
519
|
-
|
|
520
|
-
// Clean up
|
|
521
|
-
thumbnailStrip.remove();
|
|
522
|
-
|
|
523
|
-
// Restore console.warn
|
|
524
|
-
consoleSpy.mockRestore();
|
|
525
|
-
});
|
|
526
|
-
});
|
|
527
|
-
|
|
528
|
-
describe("Caching Behavior", () => {
|
|
529
|
-
test("global input cache is accessible for debugging", async ({
|
|
530
|
-
expect,
|
|
531
|
-
}) => {
|
|
532
|
-
// Verify that the global Input cache is accessible
|
|
533
|
-
expect((globalThis as any).debugInputCache).toBeDefined();
|
|
534
|
-
|
|
535
|
-
const cache = (globalThis as any).debugInputCache;
|
|
536
|
-
expect(cache.getStats).toBeDefined();
|
|
537
|
-
expect(cache.clear).toBeDefined();
|
|
538
|
-
});
|
|
539
|
-
|
|
540
|
-
test("input instances are cached globally for efficiency", async ({
|
|
541
|
-
jitVideo,
|
|
542
|
-
expect,
|
|
543
|
-
}) => {
|
|
544
|
-
const mediaEngine = jitVideo.mediaEngineTask.value as JitMediaEngine;
|
|
545
|
-
expect(mediaEngine).toBeInstanceOf(JitMediaEngine);
|
|
546
|
-
|
|
547
|
-
// Clear cache to start fresh
|
|
548
|
-
(globalThis as any).debugInputCache.clear();
|
|
549
|
-
|
|
550
|
-
// Extract thumbnails from same segment multiple times
|
|
551
|
-
const firstBatch = await mediaEngine.extractThumbnails([1000, 1500]);
|
|
552
|
-
const secondBatch = await mediaEngine.extractThumbnails([1200, 1800]); // Same segment
|
|
553
|
-
|
|
554
|
-
expect(firstBatch).toHaveLength(2);
|
|
555
|
-
expect(secondBatch).toHaveLength(2);
|
|
556
|
-
|
|
557
|
-
// All should succeed
|
|
558
|
-
expect(firstBatch.every((t) => t !== null)).toBe(true);
|
|
559
|
-
expect(secondBatch.every((t) => t !== null)).toBe(true);
|
|
560
|
-
|
|
561
|
-
// Verify that Input objects are being cached globally
|
|
562
|
-
const cacheStats = (globalThis as any).debugInputCache.getStats();
|
|
563
|
-
expect(cacheStats.size).toBeGreaterThan(0);
|
|
564
|
-
console.log("Global Input cache stats:", cacheStats);
|
|
565
|
-
});
|
|
566
|
-
|
|
567
|
-
test("different segments create separate input cache entries", async ({
|
|
568
|
-
jitVideo,
|
|
569
|
-
expect,
|
|
570
|
-
}) => {
|
|
571
|
-
const mediaEngine = jitVideo.mediaEngineTask.value as JitMediaEngine;
|
|
572
|
-
expect(mediaEngine).toBeInstanceOf(JitMediaEngine);
|
|
573
|
-
|
|
574
|
-
// Extract thumbnails from different segments
|
|
575
|
-
const segment1 = await mediaEngine.extractThumbnails([1000]);
|
|
576
|
-
const segment2 = await mediaEngine.extractThumbnails([3000]); // Different segment
|
|
577
|
-
const segment3 = await mediaEngine.extractThumbnails([5000]); // Different segment
|
|
578
|
-
|
|
579
|
-
expect(segment1).toHaveLength(1);
|
|
580
|
-
expect(segment2).toHaveLength(1);
|
|
581
|
-
expect(segment3).toHaveLength(1);
|
|
582
|
-
|
|
583
|
-
// All should succeed
|
|
584
|
-
expect(segment1[0]).toBeTruthy();
|
|
585
|
-
expect(segment2[0]).toBeTruthy();
|
|
586
|
-
expect(segment3[0]).toBeTruthy();
|
|
587
|
-
});
|
|
588
|
-
});
|
|
589
|
-
|
|
590
|
-
describe("Error Handling", () => {
|
|
591
|
-
test("handles media engine without video track", async ({ expect }) => {
|
|
592
|
-
// Create a video element but don't wait for it to fully load
|
|
593
|
-
const video = document.createElement("ef-video");
|
|
594
|
-
video.src = "nonexistent.mp4";
|
|
595
|
-
document.body.appendChild(video);
|
|
596
|
-
|
|
597
|
-
try {
|
|
598
|
-
await video.mediaEngineTask.run();
|
|
599
|
-
const mediaEngine = video.mediaEngineTask.value;
|
|
600
|
-
|
|
601
|
-
if (mediaEngine) {
|
|
602
|
-
const timestamps = [1000];
|
|
603
|
-
const thumbnails = await mediaEngine.extractThumbnails(timestamps);
|
|
604
|
-
|
|
605
|
-
// Should handle gracefully with nulls
|
|
606
|
-
expect(thumbnails).toHaveLength(1);
|
|
607
|
-
expect(thumbnails[0]).toBeNull();
|
|
608
|
-
}
|
|
609
|
-
} catch (error) {
|
|
610
|
-
// Media engine creation might fail for nonexistent file - that's expected
|
|
611
|
-
expect(error).toBeDefined();
|
|
612
|
-
} finally {
|
|
613
|
-
video.remove();
|
|
614
|
-
}
|
|
615
|
-
});
|
|
616
|
-
|
|
617
|
-
test("handles concurrent thumbnail extraction requests", async ({
|
|
618
|
-
jitVideo,
|
|
619
|
-
expect,
|
|
620
|
-
}) => {
|
|
621
|
-
const mediaEngine = jitVideo.mediaEngineTask.value!;
|
|
622
|
-
expect(mediaEngine).toBeInstanceOf(JitMediaEngine);
|
|
623
|
-
|
|
624
|
-
// Start multiple concurrent extractions
|
|
625
|
-
const promise1 = mediaEngine.extractThumbnails([1000, 2000]);
|
|
626
|
-
const promise2 = mediaEngine.extractThumbnails([3000, 4000]);
|
|
627
|
-
const promise3 = mediaEngine.extractThumbnails([5000, 6000]);
|
|
628
|
-
|
|
629
|
-
const [result1, result2, result3] = await Promise.all([
|
|
630
|
-
promise1,
|
|
631
|
-
promise2,
|
|
632
|
-
promise3,
|
|
633
|
-
]);
|
|
634
|
-
|
|
635
|
-
expect(result1).toHaveLength(2);
|
|
636
|
-
expect(result2).toHaveLength(2);
|
|
637
|
-
expect(result3).toHaveLength(2);
|
|
638
|
-
|
|
639
|
-
// All should succeed
|
|
640
|
-
expect(result1.every((t) => t !== null)).toBe(true);
|
|
641
|
-
expect(result2.every((t) => t !== null)).toBe(true);
|
|
642
|
-
expect(result3.every((t) => t !== null)).toBe(true);
|
|
643
|
-
});
|
|
644
|
-
});
|
|
645
|
-
|
|
646
|
-
describe("Performance Characteristics", () => {
|
|
647
|
-
test("batch extraction is more efficient than individual calls", async ({
|
|
648
|
-
jitVideo,
|
|
649
|
-
expect,
|
|
650
|
-
}) => {
|
|
651
|
-
const mediaEngine = jitVideo.mediaEngineTask.value!;
|
|
652
|
-
expect(mediaEngine).toBeInstanceOf(JitMediaEngine);
|
|
653
|
-
|
|
654
|
-
const timestamps = [1000, 2000, 3000, 4000];
|
|
655
|
-
|
|
656
|
-
// Time batch extraction
|
|
657
|
-
const batchStart = performance.now();
|
|
658
|
-
const batchResults = await mediaEngine.extractThumbnails(timestamps);
|
|
659
|
-
const batchEnd = performance.now();
|
|
660
|
-
|
|
661
|
-
// Time individual extractions
|
|
662
|
-
const individualStart = performance.now();
|
|
663
|
-
const individualResults = [];
|
|
664
|
-
for (const timestamp of timestamps) {
|
|
665
|
-
const result = await mediaEngine.extractThumbnails([timestamp]);
|
|
666
|
-
individualResults.push(result[0]);
|
|
667
|
-
}
|
|
668
|
-
const individualEnd = performance.now();
|
|
669
|
-
|
|
670
|
-
const batchTime = batchEnd - batchStart;
|
|
671
|
-
const individualTime = individualEnd - individualStart;
|
|
672
|
-
|
|
673
|
-
expect(batchResults).toHaveLength(4);
|
|
674
|
-
expect(individualResults).toHaveLength(4);
|
|
675
|
-
|
|
676
|
-
// Results should be equivalent
|
|
677
|
-
for (let i = 0; i < timestamps.length; i++) {
|
|
678
|
-
expect(batchResults[i]?.timestamp).toBe(
|
|
679
|
-
individualResults[i]?.timestamp,
|
|
680
|
-
);
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
console.log(
|
|
684
|
-
`Batch time: ${batchTime.toFixed(2)}ms, Individual time: ${individualTime.toFixed(2)}ms`,
|
|
685
|
-
);
|
|
686
|
-
|
|
687
|
-
// Batch should generally be faster (though this might vary in test environments)
|
|
688
|
-
// We don't enforce this as a hard requirement since test timing can be variable
|
|
689
|
-
expect(batchTime).toBeGreaterThan(0);
|
|
690
|
-
expect(individualTime).toBeGreaterThan(0);
|
|
691
|
-
});
|
|
692
|
-
|
|
693
|
-
test("segment grouping optimizes cross-segment extraction", async ({
|
|
694
|
-
jitVideo,
|
|
695
|
-
expect,
|
|
696
|
-
}) => {
|
|
697
|
-
const mediaEngine = jitVideo.mediaEngineTask.value!;
|
|
698
|
-
expect(mediaEngine).toBeInstanceOf(JitMediaEngine);
|
|
699
|
-
|
|
700
|
-
// Extract thumbnails that span multiple segments but in an order
|
|
701
|
-
// that would be inefficient without segment grouping
|
|
702
|
-
const timestamps = [1000, 5000, 1500, 5500, 2000, 6000]; // Alternating segments
|
|
703
|
-
const thumbnails = await mediaEngine.extractThumbnails(timestamps);
|
|
704
|
-
|
|
705
|
-
expect(thumbnails).toHaveLength(6);
|
|
706
|
-
|
|
707
|
-
// All should succeed despite the inefficient ordering
|
|
708
|
-
for (let i = 0; i < timestamps.length; i++) {
|
|
709
|
-
expect(thumbnails[i]).toBeTruthy();
|
|
710
|
-
expect(thumbnails[i]?.timestamp).toBe(timestamps[i]);
|
|
711
|
-
}
|
|
712
|
-
});
|
|
713
|
-
});
|
|
714
|
-
});
|