@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,401 +0,0 @@
|
|
|
1
|
-
import { html, render } from "lit";
|
|
2
|
-
import { describe } from "vitest";
|
|
3
|
-
|
|
4
|
-
import { test as baseTest } from "../../test/useMSW.js";
|
|
5
|
-
import type { EFVideo } from "./EFVideo.js";
|
|
6
|
-
import "./EFVideo.js";
|
|
7
|
-
import "../gui/EFWorkbench.js";
|
|
8
|
-
import "../gui/EFConfiguration.js";
|
|
9
|
-
|
|
10
|
-
const test = baseTest.extend({});
|
|
11
|
-
|
|
12
|
-
describe("URL Token Deduplication", () => {
|
|
13
|
-
test.skip("multiple EFMedia elements with same src should share URL tokens", async ({
|
|
14
|
-
expect,
|
|
15
|
-
}) => {
|
|
16
|
-
// TODO: This test is intentionally skipped because it documents a known issue where
|
|
17
|
-
// URL token requests are not properly deduplicated across multiple EFMedia elements
|
|
18
|
-
// with the same src. Currently makes 2 token requests instead of 1.
|
|
19
|
-
// Mock fetch to track token requests
|
|
20
|
-
const originalFetch = window.fetch;
|
|
21
|
-
const tokenRequests: string[] = [];
|
|
22
|
-
const mediaRequests: string[] = [];
|
|
23
|
-
|
|
24
|
-
window.fetch = async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
25
|
-
const url = input.toString();
|
|
26
|
-
|
|
27
|
-
if (url.includes("/api/v1/url-token") || url.includes("/@ef-sign-url")) {
|
|
28
|
-
console.log("Token request:", url, init?.body);
|
|
29
|
-
tokenRequests.push(url);
|
|
30
|
-
// Mock token response
|
|
31
|
-
return new Response(JSON.stringify({ token: "mock-token" }), {
|
|
32
|
-
status: 200,
|
|
33
|
-
headers: { "Content-Type": "application/json" },
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (url.includes("/api/v1/transcode/")) {
|
|
38
|
-
console.log("Media request:", url);
|
|
39
|
-
mediaRequests.push(url);
|
|
40
|
-
// Mock media response
|
|
41
|
-
return new Response("mock-media-data", { status: 200 });
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return originalFetch(input, init);
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
try {
|
|
48
|
-
const container = document.createElement("div");
|
|
49
|
-
// Create 3 EFVideo elements with the same source in a shared context
|
|
50
|
-
render(
|
|
51
|
-
html`
|
|
52
|
-
<ef-configuration api-host="http://localhost:3000" signing-url="/@ef-sign-url">
|
|
53
|
-
<ef-workbench>
|
|
54
|
-
<ef-video src="http://example.com/test.mp4"></ef-video>
|
|
55
|
-
<ef-video src="http://example.com/test.mp4"></ef-video>
|
|
56
|
-
<ef-video src="http://example.com/test.mp4"></ef-video>
|
|
57
|
-
</ef-workbench>
|
|
58
|
-
</ef-configuration>
|
|
59
|
-
`,
|
|
60
|
-
container,
|
|
61
|
-
);
|
|
62
|
-
document.body.appendChild(container);
|
|
63
|
-
|
|
64
|
-
const videos = container.querySelectorAll(
|
|
65
|
-
"ef-video",
|
|
66
|
-
) as NodeListOf<EFVideo>;
|
|
67
|
-
const workbench = container.querySelector("ef-workbench") as any;
|
|
68
|
-
|
|
69
|
-
await workbench.updateComplete;
|
|
70
|
-
await Promise.all(Array.from(videos).map((v) => v.updateComplete));
|
|
71
|
-
|
|
72
|
-
// Trigger manifest requests for all videos
|
|
73
|
-
await Promise.all(
|
|
74
|
-
Array.from(videos).map(async (video) => {
|
|
75
|
-
try {
|
|
76
|
-
await video.mediaEngineTask.run();
|
|
77
|
-
} catch (error) {
|
|
78
|
-
// Expected to fail since we're mocking, but should trigger token requests
|
|
79
|
-
console.log("Expected error:", error);
|
|
80
|
-
}
|
|
81
|
-
}),
|
|
82
|
-
);
|
|
83
|
-
|
|
84
|
-
console.log("Token requests:", tokenRequests);
|
|
85
|
-
console.log("Media requests:", mediaRequests);
|
|
86
|
-
|
|
87
|
-
// Should only have 1 token request since all videos share the same source
|
|
88
|
-
// This test will currently fail, demonstrating the issue
|
|
89
|
-
expect(tokenRequests.length).toBe(1);
|
|
90
|
-
|
|
91
|
-
container.remove();
|
|
92
|
-
} finally {
|
|
93
|
-
window.fetch = originalFetch;
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
test("multiple EFMedia elements in separate context providers should share tokens globally", async ({
|
|
98
|
-
expect,
|
|
99
|
-
}) => {
|
|
100
|
-
// This test verifies global token deduplication across separate context providers
|
|
101
|
-
const originalFetch = window.fetch;
|
|
102
|
-
const tokenRequests: { url: string; body: any; timestamp: number }[] = [];
|
|
103
|
-
|
|
104
|
-
window.fetch = async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
105
|
-
const url = input.toString();
|
|
106
|
-
|
|
107
|
-
if (url.includes("/api/v1/url-token") || url.includes("/@ef-sign-url")) {
|
|
108
|
-
const bodyData = init?.body ? JSON.parse(init.body as string) : null;
|
|
109
|
-
const timestamp = Date.now();
|
|
110
|
-
console.log("Token request:", url, bodyData, `at ${timestamp}`);
|
|
111
|
-
tokenRequests.push({ url, body: bodyData, timestamp });
|
|
112
|
-
|
|
113
|
-
// Add small delay to make timing issues more apparent
|
|
114
|
-
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
115
|
-
|
|
116
|
-
return new Response(JSON.stringify({ token: "mock-token" }), {
|
|
117
|
-
status: 200,
|
|
118
|
-
headers: { "Content-Type": "application/json" },
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
if (url.includes("/api/v1/transcode/")) {
|
|
123
|
-
return new Response(
|
|
124
|
-
JSON.stringify({
|
|
125
|
-
audioRenditions: [],
|
|
126
|
-
videoRenditions: [],
|
|
127
|
-
src: "test",
|
|
128
|
-
durationMs: 1000,
|
|
129
|
-
}),
|
|
130
|
-
{
|
|
131
|
-
status: 200,
|
|
132
|
-
headers: { "Content-Type": "application/json" },
|
|
133
|
-
},
|
|
134
|
-
);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
return originalFetch(input, init);
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
try {
|
|
141
|
-
const container = document.createElement("div");
|
|
142
|
-
// Create multiple elements with the same source, added concurrently
|
|
143
|
-
container.innerHTML = `
|
|
144
|
-
<ef-configuration api-host="http://localhost:3000" signing-url="/@ef-sign-url">
|
|
145
|
-
<ef-workbench>
|
|
146
|
-
<ef-video src="http://example.com/test.mp4"></ef-video>
|
|
147
|
-
</ef-workbench>
|
|
148
|
-
</ef-configuration>
|
|
149
|
-
<ef-configuration api-host="http://localhost:3000" signing-url="/@ef-sign-url">
|
|
150
|
-
<ef-workbench>
|
|
151
|
-
<ef-video src="http://example.com/test.mp4"></ef-video>
|
|
152
|
-
</ef-workbench>
|
|
153
|
-
</ef-configuration>
|
|
154
|
-
<ef-configuration api-host="http://localhost:3000" signing-url="/@ef-sign-url">
|
|
155
|
-
<ef-workbench>
|
|
156
|
-
<ef-video src="http://example.com/test.mp4"></ef-video>
|
|
157
|
-
</ef-workbench>
|
|
158
|
-
</ef-configuration>
|
|
159
|
-
`;
|
|
160
|
-
document.body.appendChild(container);
|
|
161
|
-
|
|
162
|
-
const videos = container.querySelectorAll(
|
|
163
|
-
"ef-video",
|
|
164
|
-
) as NodeListOf<EFVideo>;
|
|
165
|
-
const workbenches = container.querySelectorAll(
|
|
166
|
-
"ef-workbench",
|
|
167
|
-
) as NodeListOf<any>;
|
|
168
|
-
|
|
169
|
-
await Promise.all(Array.from(workbenches).map((w) => w.updateComplete));
|
|
170
|
-
await Promise.all(Array.from(videos).map((v) => v.updateComplete));
|
|
171
|
-
|
|
172
|
-
// Trigger all requests simultaneously to test race conditions
|
|
173
|
-
await Promise.all(
|
|
174
|
-
Array.from(videos).map(async (video) => {
|
|
175
|
-
try {
|
|
176
|
-
await video.mediaEngineTask.run();
|
|
177
|
-
} catch (_error) {
|
|
178
|
-
// Expected due to mocking
|
|
179
|
-
}
|
|
180
|
-
}),
|
|
181
|
-
);
|
|
182
|
-
|
|
183
|
-
console.log(`Total token requests: ${tokenRequests.length}`);
|
|
184
|
-
tokenRequests.forEach((req, i) => {
|
|
185
|
-
console.log(`Request ${i + 1}:`, req.body, `timing: ${req.timestamp}`);
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
// Log for debugging - the real issue might be in timing/concurrency
|
|
189
|
-
console.log(
|
|
190
|
-
"Token request details:",
|
|
191
|
-
tokenRequests.map((r) => ({
|
|
192
|
-
url: r.body?.url,
|
|
193
|
-
params: r.body?.params,
|
|
194
|
-
timing: r.timestamp,
|
|
195
|
-
})),
|
|
196
|
-
);
|
|
197
|
-
|
|
198
|
-
// With global token deduplication, should only make 1 token request
|
|
199
|
-
// even across separate context providers
|
|
200
|
-
expect(tokenRequests.length).toBe(1);
|
|
201
|
-
|
|
202
|
-
container.remove();
|
|
203
|
-
} finally {
|
|
204
|
-
window.fetch = originalFetch;
|
|
205
|
-
}
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
test("concurrent token requests are properly deduplicated globally", async ({
|
|
209
|
-
expect,
|
|
210
|
-
}) => {
|
|
211
|
-
// This test simulates the race condition where multiple elements initialize simultaneously
|
|
212
|
-
const originalFetch = window.fetch;
|
|
213
|
-
const tokenRequests: { url: string; body: any; timestamp: number }[] = [];
|
|
214
|
-
const concurrentRequestStarts: number[] = [];
|
|
215
|
-
|
|
216
|
-
window.fetch = async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
217
|
-
const url = input.toString();
|
|
218
|
-
|
|
219
|
-
if (url.includes("/api/v1/url-token") || url.includes("/@ef-sign-url")) {
|
|
220
|
-
const bodyData = init?.body ? JSON.parse(init.body as string) : null;
|
|
221
|
-
const timestamp = Date.now();
|
|
222
|
-
concurrentRequestStarts.push(timestamp);
|
|
223
|
-
console.log("Token request started:", url, bodyData, `at ${timestamp}`);
|
|
224
|
-
|
|
225
|
-
// Simulate network delay
|
|
226
|
-
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
227
|
-
|
|
228
|
-
tokenRequests.push({ url, body: bodyData, timestamp });
|
|
229
|
-
return new Response(JSON.stringify({ token: "mock-token" }), {
|
|
230
|
-
status: 200,
|
|
231
|
-
headers: { "Content-Type": "application/json" },
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
if (url.includes("/api/v1/transcode/")) {
|
|
236
|
-
return new Response(
|
|
237
|
-
JSON.stringify({
|
|
238
|
-
audioRenditions: [],
|
|
239
|
-
videoRenditions: [],
|
|
240
|
-
src: "test",
|
|
241
|
-
durationMs: 1000,
|
|
242
|
-
}),
|
|
243
|
-
{
|
|
244
|
-
status: 200,
|
|
245
|
-
headers: { "Content-Type": "application/json" },
|
|
246
|
-
},
|
|
247
|
-
);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
return originalFetch(input, init);
|
|
251
|
-
};
|
|
252
|
-
|
|
253
|
-
try {
|
|
254
|
-
// Create elements and trigger simultaneous token requests
|
|
255
|
-
const elements: any[] = [];
|
|
256
|
-
const containers: HTMLElement[] = [];
|
|
257
|
-
|
|
258
|
-
// Create 5 separate context providers with identical videos
|
|
259
|
-
for (let i = 0; i < 5; i++) {
|
|
260
|
-
const container = document.createElement("div");
|
|
261
|
-
container.innerHTML = `
|
|
262
|
-
<ef-configuration api-host="http://localhost:3000" signing-url="/@ef-sign-url">
|
|
263
|
-
<ef-workbench>
|
|
264
|
-
<ef-video src="http://example.com/concurrent-test.mp4"></ef-video>
|
|
265
|
-
</ef-workbench>
|
|
266
|
-
</ef-configuration>
|
|
267
|
-
`;
|
|
268
|
-
document.body.appendChild(container);
|
|
269
|
-
containers.push(container);
|
|
270
|
-
|
|
271
|
-
const video = container.querySelector("ef-video");
|
|
272
|
-
const workbench = container.querySelector("ef-workbench");
|
|
273
|
-
elements.push({ video, workbench });
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
// Wait for all elements to be ready
|
|
277
|
-
await Promise.all(
|
|
278
|
-
elements.map(async ({ video, workbench }) => {
|
|
279
|
-
await workbench.updateComplete;
|
|
280
|
-
await video.updateComplete;
|
|
281
|
-
}),
|
|
282
|
-
);
|
|
283
|
-
|
|
284
|
-
// Trigger all manifest requests simultaneously to create race condition
|
|
285
|
-
const startTime = Date.now();
|
|
286
|
-
await Promise.all(
|
|
287
|
-
elements.map(async ({ video }) => {
|
|
288
|
-
try {
|
|
289
|
-
await video.mediaEngineTask.run();
|
|
290
|
-
} catch (_error) {
|
|
291
|
-
// Expected due to mocking
|
|
292
|
-
}
|
|
293
|
-
}),
|
|
294
|
-
);
|
|
295
|
-
const endTime = Date.now();
|
|
296
|
-
|
|
297
|
-
console.log(`All requests completed in ${endTime - startTime}ms`);
|
|
298
|
-
console.log(`Concurrent starts: ${concurrentRequestStarts.length}`);
|
|
299
|
-
console.log(`Completed token requests: ${tokenRequests.length}`);
|
|
300
|
-
|
|
301
|
-
// Verify that despite 5 concurrent elements, only 1 token was actually fetched
|
|
302
|
-
expect(tokenRequests.length).toBe(1);
|
|
303
|
-
|
|
304
|
-
// Cleanup
|
|
305
|
-
containers.forEach((container) => {
|
|
306
|
-
container.remove();
|
|
307
|
-
});
|
|
308
|
-
} finally {
|
|
309
|
-
window.fetch = originalFetch;
|
|
310
|
-
}
|
|
311
|
-
});
|
|
312
|
-
|
|
313
|
-
test("ten identical videos should use single URL token (user reported issue)", async ({
|
|
314
|
-
expect,
|
|
315
|
-
}) => {
|
|
316
|
-
// This test specifically reproduces the user's reported issue:
|
|
317
|
-
// 10 identical videos creating 10 tokens instead of sharing 1 token
|
|
318
|
-
const originalFetch = window.fetch;
|
|
319
|
-
const tokenRequests: string[] = [];
|
|
320
|
-
|
|
321
|
-
window.fetch = async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
322
|
-
const url = input.toString();
|
|
323
|
-
|
|
324
|
-
if (url.includes("/api/v1/url-token") || url.includes("/@ef-sign-url")) {
|
|
325
|
-
console.log("Token request for:", JSON.parse(init?.body as string));
|
|
326
|
-
tokenRequests.push(url);
|
|
327
|
-
return new Response(JSON.stringify({ token: "shared-token-for-all" }), {
|
|
328
|
-
status: 200,
|
|
329
|
-
headers: { "Content-Type": "application/json" },
|
|
330
|
-
});
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
if (url.includes("/api/v1/transcode/")) {
|
|
334
|
-
return new Response(
|
|
335
|
-
JSON.stringify({
|
|
336
|
-
audioRenditions: [],
|
|
337
|
-
videoRenditions: [],
|
|
338
|
-
src: "test",
|
|
339
|
-
durationMs: 1000,
|
|
340
|
-
}),
|
|
341
|
-
{
|
|
342
|
-
status: 200,
|
|
343
|
-
headers: { "Content-Type": "application/json" },
|
|
344
|
-
},
|
|
345
|
-
);
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
return originalFetch(input, init);
|
|
349
|
-
};
|
|
350
|
-
|
|
351
|
-
try {
|
|
352
|
-
const container = document.createElement("div");
|
|
353
|
-
|
|
354
|
-
// Create exactly the scenario the user described: 10 identical videos on one page
|
|
355
|
-
const videoElements = Array.from(
|
|
356
|
-
{ length: 10 },
|
|
357
|
-
(_, i) =>
|
|
358
|
-
`<ef-video src="http://example.com/user-video.mp4" id="video-${i}"></ef-video>`,
|
|
359
|
-
).join("\n");
|
|
360
|
-
|
|
361
|
-
container.innerHTML = `
|
|
362
|
-
<ef-configuration api-host="http://localhost:3000" signing-url="/@ef-sign-url">
|
|
363
|
-
<ef-workbench>
|
|
364
|
-
${videoElements}
|
|
365
|
-
</ef-workbench>
|
|
366
|
-
</ef-configuration>
|
|
367
|
-
`;
|
|
368
|
-
document.body.appendChild(container);
|
|
369
|
-
|
|
370
|
-
const videos = container.querySelectorAll("ef-video");
|
|
371
|
-
const workbench = container.querySelector("ef-workbench") as any;
|
|
372
|
-
|
|
373
|
-
expect(videos.length).toBe(10);
|
|
374
|
-
|
|
375
|
-
await workbench.updateComplete;
|
|
376
|
-
await Promise.all(Array.from(videos).map((v: any) => v.updateComplete));
|
|
377
|
-
|
|
378
|
-
// Trigger media engine initialization for all 10 videos
|
|
379
|
-
await Promise.all(
|
|
380
|
-
Array.from(videos).map(async (video: any) => {
|
|
381
|
-
try {
|
|
382
|
-
await video.mediaEngineTask.run();
|
|
383
|
-
} catch (_error) {
|
|
384
|
-
// Expected due to mocking
|
|
385
|
-
}
|
|
386
|
-
}),
|
|
387
|
-
);
|
|
388
|
-
|
|
389
|
-
console.log(
|
|
390
|
-
`Total token requests for 10 identical videos: ${tokenRequests.length}`,
|
|
391
|
-
);
|
|
392
|
-
|
|
393
|
-
// This should now be 1 instead of 10, proving the deduplication works
|
|
394
|
-
expect(tokenRequests.length).toBe(1);
|
|
395
|
-
|
|
396
|
-
container.remove();
|
|
397
|
-
} finally {
|
|
398
|
-
window.fetch = originalFetch;
|
|
399
|
-
}
|
|
400
|
-
});
|
|
401
|
-
});
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import type { LitElement } from "lit";
|
|
2
|
-
|
|
3
|
-
export declare class FetchMixinInterface {
|
|
4
|
-
fetch: typeof fetch;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
type Constructor<T = {}> = new (...args: any[]) => T;
|
|
8
|
-
export function FetchMixin<T extends Constructor<LitElement>>(superClass: T) {
|
|
9
|
-
class FetchElement extends superClass {
|
|
10
|
-
fetch = (url: string, init?: RequestInit): Promise<Response> => {
|
|
11
|
-
try {
|
|
12
|
-
// Look for context providers up the DOM tree
|
|
13
|
-
const workbench = this.closest("ef-workbench") as any;
|
|
14
|
-
if (workbench?.fetch) {
|
|
15
|
-
return workbench.fetch(url, init);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const preview = this.closest("ef-preview") as any;
|
|
19
|
-
if (preview?.fetch) {
|
|
20
|
-
return preview.fetch(url, init);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const configuration = this.closest("ef-configuration") as any;
|
|
24
|
-
if (configuration?.fetch) {
|
|
25
|
-
return configuration.fetch(url, init);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// Fallback to window.fetch
|
|
29
|
-
return window.fetch(url, init);
|
|
30
|
-
} catch (error) {
|
|
31
|
-
console.error("FetchMixin error", url, error);
|
|
32
|
-
throw error;
|
|
33
|
-
}
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return FetchElement as Constructor<FetchMixinInterface> & T;
|
|
38
|
-
}
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import type { AudioSample, VideoSample } from "mediabunny";
|
|
2
|
-
import { roundToMilliseconds } from "./EFMedia/shared/PrecisionUtils";
|
|
3
|
-
export type MediaSample = VideoSample | AudioSample;
|
|
4
|
-
|
|
5
|
-
// Generic sample buffer that works with both VideoSample and AudioSample
|
|
6
|
-
export class SampleBuffer {
|
|
7
|
-
private buffer: MediaSample[] = [];
|
|
8
|
-
private bufferSize: number;
|
|
9
|
-
|
|
10
|
-
constructor(bufferSize = 10) {
|
|
11
|
-
this.bufferSize = bufferSize;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
push(sample: MediaSample) {
|
|
15
|
-
// Defensive copy to avoid concurrent modification during iteration
|
|
16
|
-
const currentBuffer = [...this.buffer];
|
|
17
|
-
currentBuffer.push(sample);
|
|
18
|
-
|
|
19
|
-
if (currentBuffer.length > this.bufferSize) {
|
|
20
|
-
const shifted = currentBuffer.shift();
|
|
21
|
-
if (shifted) {
|
|
22
|
-
try {
|
|
23
|
-
shifted.close();
|
|
24
|
-
} catch (_error) {
|
|
25
|
-
// Sample already closed, continue
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Update buffer atomically
|
|
31
|
-
this.buffer = currentBuffer;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
clear() {
|
|
35
|
-
// Get current buffer and clear atomically
|
|
36
|
-
const toClose = this.buffer;
|
|
37
|
-
this.buffer = [];
|
|
38
|
-
|
|
39
|
-
// Close samples after clearing to avoid holding references
|
|
40
|
-
for (const sample of toClose) {
|
|
41
|
-
try {
|
|
42
|
-
sample.close();
|
|
43
|
-
} catch (_error) {
|
|
44
|
-
// Sample already closed, continue
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
peek(): MediaSample | undefined {
|
|
50
|
-
// Defensive read - get current buffer state
|
|
51
|
-
const currentBuffer = this.buffer;
|
|
52
|
-
return currentBuffer[0];
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
find(desiredSeekTimeMs: number): MediaSample | undefined {
|
|
56
|
-
const currentBuffer = [...this.buffer];
|
|
57
|
-
|
|
58
|
-
if (currentBuffer.length === 0) return undefined;
|
|
59
|
-
|
|
60
|
-
// Use consistent precision handling across the entire pipeline
|
|
61
|
-
const targetTimeMs = roundToMilliseconds(desiredSeekTimeMs);
|
|
62
|
-
|
|
63
|
-
// Find the sample that contains the target time
|
|
64
|
-
for (const sample of currentBuffer) {
|
|
65
|
-
const sampleStartMs = roundToMilliseconds((sample.timestamp || 0) * 1000);
|
|
66
|
-
const sampleDurationMs = roundToMilliseconds(
|
|
67
|
-
(sample.duration || 0) * 1000,
|
|
68
|
-
);
|
|
69
|
-
const sampleEndMs = roundToMilliseconds(sampleStartMs + sampleDurationMs);
|
|
70
|
-
|
|
71
|
-
// Check if the desired time falls within this sample's time span [start, end], inclusive of end
|
|
72
|
-
if (targetTimeMs >= sampleStartMs && targetTimeMs < sampleEndMs) {
|
|
73
|
-
return sample;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return undefined; // No sample contains the target time
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
get length() {
|
|
81
|
-
return this.buffer.length;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
get firstTimestamp() {
|
|
85
|
-
// Defensive read - get current buffer state
|
|
86
|
-
const currentBuffer = this.buffer;
|
|
87
|
-
return currentBuffer[0]?.timestamp || 0;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
getContents(): MediaSample[] {
|
|
91
|
-
// Defensive copy to avoid concurrent modification during iteration
|
|
92
|
-
return [...this.buffer];
|
|
93
|
-
}
|
|
94
|
-
}
|