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