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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/dist/elements/EFTimegroup.js +7 -2
  2. package/dist/elements/EFTimegroup.js.map +1 -1
  3. package/package.json +2 -2
  4. package/scripts/build-css.js +3 -3
  5. package/tsdown.config.ts +1 -1
  6. package/types.json +1 -1
  7. package/src/elements/ContextProxiesController.ts +0 -124
  8. package/src/elements/CrossUpdateController.ts +0 -22
  9. package/src/elements/EFAudio.browsertest.ts +0 -706
  10. package/src/elements/EFAudio.ts +0 -56
  11. package/src/elements/EFCaptions.browsertest.ts +0 -1960
  12. package/src/elements/EFCaptions.ts +0 -823
  13. package/src/elements/EFImage.browsertest.ts +0 -120
  14. package/src/elements/EFImage.ts +0 -113
  15. package/src/elements/EFMedia/AssetIdMediaEngine.test.ts +0 -224
  16. package/src/elements/EFMedia/AssetIdMediaEngine.ts +0 -110
  17. package/src/elements/EFMedia/AssetMediaEngine.browsertest.ts +0 -140
  18. package/src/elements/EFMedia/AssetMediaEngine.ts +0 -385
  19. package/src/elements/EFMedia/BaseMediaEngine.browsertest.ts +0 -400
  20. package/src/elements/EFMedia/BaseMediaEngine.ts +0 -505
  21. package/src/elements/EFMedia/BufferedSeekingInput.browsertest.ts +0 -386
  22. package/src/elements/EFMedia/BufferedSeekingInput.ts +0 -430
  23. package/src/elements/EFMedia/JitMediaEngine.browsertest.ts +0 -226
  24. package/src/elements/EFMedia/JitMediaEngine.ts +0 -256
  25. package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.browsertest.ts +0 -679
  26. package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.ts +0 -117
  27. package/src/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.ts +0 -246
  28. package/src/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.browsertest.ts +0 -59
  29. package/src/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.ts +0 -27
  30. package/src/elements/EFMedia/audioTasks/makeAudioInputTask.browsertest.ts +0 -55
  31. package/src/elements/EFMedia/audioTasks/makeAudioInputTask.ts +0 -53
  32. package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.chunkboundary.regression.browsertest.ts +0 -207
  33. package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.ts +0 -72
  34. package/src/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.ts +0 -32
  35. package/src/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.ts +0 -29
  36. package/src/elements/EFMedia/audioTasks/makeAudioTasksVideoOnly.browsertest.ts +0 -95
  37. package/src/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.ts +0 -184
  38. package/src/elements/EFMedia/shared/AudioSpanUtils.ts +0 -129
  39. package/src/elements/EFMedia/shared/BufferUtils.ts +0 -342
  40. package/src/elements/EFMedia/shared/GlobalInputCache.ts +0 -77
  41. package/src/elements/EFMedia/shared/MediaTaskUtils.ts +0 -44
  42. package/src/elements/EFMedia/shared/PrecisionUtils.ts +0 -46
  43. package/src/elements/EFMedia/shared/RenditionHelpers.browsertest.ts +0 -246
  44. package/src/elements/EFMedia/shared/RenditionHelpers.ts +0 -56
  45. package/src/elements/EFMedia/shared/ThumbnailExtractor.ts +0 -227
  46. package/src/elements/EFMedia/tasks/makeMediaEngineTask.browsertest.ts +0 -167
  47. package/src/elements/EFMedia/tasks/makeMediaEngineTask.ts +0 -88
  48. package/src/elements/EFMedia/videoTasks/MainVideoInputCache.ts +0 -76
  49. package/src/elements/EFMedia/videoTasks/ScrubInputCache.ts +0 -61
  50. package/src/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.ts +0 -114
  51. package/src/elements/EFMedia/videoTasks/makeScrubVideoInitSegmentFetchTask.ts +0 -35
  52. package/src/elements/EFMedia/videoTasks/makeScrubVideoInputTask.ts +0 -52
  53. package/src/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.ts +0 -124
  54. package/src/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.ts +0 -44
  55. package/src/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.ts +0 -32
  56. package/src/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.ts +0 -370
  57. package/src/elements/EFMedia/videoTasks/makeVideoBufferTask.ts +0 -109
  58. package/src/elements/EFMedia.browsertest.ts +0 -872
  59. package/src/elements/EFMedia.ts +0 -341
  60. package/src/elements/EFSourceMixin.ts +0 -60
  61. package/src/elements/EFSurface.browsertest.ts +0 -151
  62. package/src/elements/EFSurface.ts +0 -142
  63. package/src/elements/EFTemporal.browsertest.ts +0 -215
  64. package/src/elements/EFTemporal.ts +0 -800
  65. package/src/elements/EFThumbnailStrip.browsertest.ts +0 -585
  66. package/src/elements/EFThumbnailStrip.media-engine.browsertest.ts +0 -714
  67. package/src/elements/EFThumbnailStrip.ts +0 -906
  68. package/src/elements/EFTimegroup.browsertest.ts +0 -870
  69. package/src/elements/EFTimegroup.ts +0 -878
  70. package/src/elements/EFVideo.browsertest.ts +0 -1482
  71. package/src/elements/EFVideo.ts +0 -564
  72. package/src/elements/EFWaveform.ts +0 -547
  73. package/src/elements/FetchContext.browsertest.ts +0 -401
  74. package/src/elements/FetchMixin.ts +0 -38
  75. package/src/elements/SampleBuffer.ts +0 -94
  76. package/src/elements/TargetController.browsertest.ts +0 -230
  77. package/src/elements/TargetController.ts +0 -224
  78. package/src/elements/TimegroupController.ts +0 -26
  79. package/src/elements/durationConverter.ts +0 -35
  80. package/src/elements/parseTimeToMs.ts +0 -9
  81. package/src/elements/printTaskStatus.ts +0 -16
  82. package/src/elements/renderTemporalAudio.ts +0 -108
  83. package/src/elements/updateAnimations.browsertest.ts +0 -1884
  84. package/src/elements/updateAnimations.ts +0 -217
  85. package/src/elements/util.ts +0 -24
  86. package/src/gui/ContextMixin.browsertest.ts +0 -860
  87. package/src/gui/ContextMixin.ts +0 -562
  88. package/src/gui/Controllable.browsertest.ts +0 -258
  89. package/src/gui/Controllable.ts +0 -41
  90. package/src/gui/EFConfiguration.ts +0 -40
  91. package/src/gui/EFControls.browsertest.ts +0 -389
  92. package/src/gui/EFControls.ts +0 -195
  93. package/src/gui/EFDial.browsertest.ts +0 -84
  94. package/src/gui/EFDial.ts +0 -172
  95. package/src/gui/EFFilmstrip.browsertest.ts +0 -712
  96. package/src/gui/EFFilmstrip.ts +0 -1349
  97. package/src/gui/EFFitScale.ts +0 -152
  98. package/src/gui/EFFocusOverlay.ts +0 -79
  99. package/src/gui/EFPause.browsertest.ts +0 -202
  100. package/src/gui/EFPause.ts +0 -73
  101. package/src/gui/EFPlay.browsertest.ts +0 -202
  102. package/src/gui/EFPlay.ts +0 -73
  103. package/src/gui/EFPreview.ts +0 -74
  104. package/src/gui/EFResizableBox.browsertest.ts +0 -79
  105. package/src/gui/EFResizableBox.ts +0 -898
  106. package/src/gui/EFScrubber.ts +0 -151
  107. package/src/gui/EFTimeDisplay.browsertest.ts +0 -237
  108. package/src/gui/EFTimeDisplay.ts +0 -55
  109. package/src/gui/EFToggleLoop.ts +0 -35
  110. package/src/gui/EFTogglePlay.ts +0 -70
  111. package/src/gui/EFWorkbench.ts +0 -115
  112. package/src/gui/PlaybackController.ts +0 -527
  113. package/src/gui/TWMixin.css +0 -6
  114. package/src/gui/TWMixin.ts +0 -61
  115. package/src/gui/TargetOrContextMixin.ts +0 -185
  116. package/src/gui/currentTimeContext.ts +0 -5
  117. package/src/gui/durationContext.ts +0 -3
  118. package/src/gui/efContext.ts +0 -6
  119. package/src/gui/fetchContext.ts +0 -5
  120. package/src/gui/focusContext.ts +0 -7
  121. package/src/gui/focusedElementContext.ts +0 -5
  122. package/src/gui/playingContext.ts +0 -5
  123. package/src/otel/BridgeSpanExporter.ts +0 -150
  124. package/src/otel/setupBrowserTracing.ts +0 -73
  125. package/src/otel/tracingHelpers.ts +0 -251
  126. package/src/transcoding/cache/RequestDeduplicator.test.ts +0 -170
  127. package/src/transcoding/cache/RequestDeduplicator.ts +0 -65
  128. package/src/transcoding/cache/URLTokenDeduplicator.test.ts +0 -182
  129. package/src/transcoding/cache/URLTokenDeduplicator.ts +0 -101
  130. package/src/transcoding/types/index.ts +0 -312
  131. package/src/transcoding/utils/MediaUtils.ts +0 -63
  132. package/src/transcoding/utils/UrlGenerator.ts +0 -68
  133. package/src/transcoding/utils/constants.ts +0 -36
  134. package/src/utils/LRUCache.test.ts +0 -274
  135. package/src/utils/LRUCache.ts +0 -696
@@ -1,585 +0,0 @@
1
- import { html, render } from "lit";
2
- import { beforeEach, describe } from "vitest";
3
-
4
- import { test as baseTest } from "../../test/useMSW.js";
5
- import "../gui/EFPreview.js";
6
- import "../gui/EFWorkbench.js";
7
- import "./EFThumbnailStrip.js";
8
- import type { EFThumbnailStrip } from "./EFThumbnailStrip.js";
9
- import "./EFTimegroup.js";
10
- import "./EFVideo.js";
11
-
12
- import type { EFTimegroup } from "./EFTimegroup.js";
13
- import type { EFVideo } from "./EFVideo.js";
14
-
15
- beforeEach(async () => {
16
- localStorage.clear();
17
- await fetch("/@ef-clear-cache", { method: "DELETE" });
18
- });
19
-
20
- // IMPLEMENTATION GUIDELINES: Test initial thumbnail loading without requiring resize events
21
-
22
- interface ThumbnailStripFixture {
23
- video: EFVideo;
24
- thumbnailStrip: EFThumbnailStrip;
25
- timegroup: EFTimegroup;
26
- container: HTMLElement;
27
- }
28
-
29
- const test = baseTest.extend<{
30
- thumbnailStripSetup: ThumbnailStripFixture;
31
- alternateSetup: ThumbnailStripFixture;
32
- }>({
33
- thumbnailStripSetup: async ({}, use) => {
34
- const container = document.createElement("div");
35
- render(
36
- html`
37
- <ef-configuration api-host="http://localhost:63315" signing-url="">
38
- <div style="width: 600px; height: 400px;">
39
- <ef-preview class="w-[600px] h-[300px]">
40
- <ef-timegroup mode="contain" class="w-full h-full bg-black">
41
- <ef-video src="http://web:3000/head-moov-480p.mp4" id="test-video" class="size-full object-contain"></ef-video>
42
- </ef-timegroup>
43
- </ef-preview>
44
- <ef-thumbnail-strip
45
- target="test-video"
46
- thumbnail-width="80"
47
- class="w-full"
48
- style="height: 48px;"
49
- ></ef-thumbnail-strip>
50
- </div>
51
- </ef-configuration>
52
- `,
53
- container,
54
- );
55
- document.body.appendChild(container);
56
-
57
- const timegroup = container.querySelector("ef-timegroup") as EFTimegroup;
58
- const video = container.querySelector("ef-video") as EFVideo;
59
- const thumbnailStrip = container.querySelector(
60
- "ef-thumbnail-strip",
61
- ) as EFThumbnailStrip;
62
-
63
- await Promise.all([
64
- timegroup.updateComplete,
65
- video.updateComplete,
66
- thumbnailStrip.updateComplete,
67
- ]);
68
-
69
- await use({ video, thumbnailStrip, timegroup, container });
70
- container.remove();
71
- },
72
- alternateSetup: async ({}, use) => {
73
- const container = document.createElement("div");
74
- render(
75
- html`
76
- <ef-configuration api-host="http://localhost:63315" signing-url="">
77
- <div style="width: 400px; height: 300px;">
78
- <ef-preview class="w-full h-[200px]">
79
- <ef-timegroup mode="contain" class="w-full h-full bg-black">
80
- <ef-video src="http://web:3000/head-moov-480p.mp4" id="alt-video" class="size-full object-contain"></ef-video>
81
- </ef-timegroup>
82
- </ef-preview>
83
- <ef-thumbnail-strip
84
- target="alt-video"
85
- thumbnail-width="80"
86
- class="w-full"
87
- style="height: 48px;"
88
- ></ef-thumbnail-strip>
89
- </div>
90
- </ef-configuration>
91
- `,
92
- container,
93
- );
94
- document.body.appendChild(container);
95
-
96
- const timegroup = container.querySelector("ef-timegroup") as EFTimegroup;
97
- const video = container.querySelector("ef-video") as EFVideo;
98
- const thumbnailStrip = container.querySelector(
99
- "ef-thumbnail-strip",
100
- ) as EFThumbnailStrip;
101
-
102
- await Promise.all([
103
- timegroup.updateComplete,
104
- video.updateComplete,
105
- thumbnailStrip.updateComplete,
106
- ]);
107
-
108
- await use({ video, thumbnailStrip, timegroup, container });
109
- container.remove();
110
- },
111
- });
112
-
113
- const awaitThumbnailLayout = async (thumbnailStrip: EFThumbnailStrip) => {
114
- // @ts-expect-error missing implementation
115
- await thumbnailStrip.thumbnailLayoutTask.taskComplete;
116
- };
117
-
118
- describe("EFThumbnailStrip", () => {
119
- describe("initialization", () => {
120
- test("should detect dimensions and target element on connection", async ({
121
- expect,
122
- thumbnailStripSetup,
123
- }) => {
124
- const { video, thumbnailStrip } = thumbnailStripSetup;
125
-
126
- await video.mediaEngineTask.taskComplete;
127
-
128
- expect(thumbnailStrip.targetElement).toBe(video);
129
- // @ts-expect-error testing private property
130
- expect(thumbnailStrip.stripWidth).toBeGreaterThan(0);
131
-
132
- const canvas = thumbnailStrip.shadowRoot?.querySelector("canvas");
133
- expect(canvas).toBeTruthy();
134
- expect(canvas?.width).toBeGreaterThan(0);
135
- expect(canvas?.height).toBeGreaterThan(0);
136
- }, 1000);
137
-
138
- test("should select target element by ID", async ({
139
- expect,
140
- thumbnailStripSetup,
141
- }) => {
142
- const { video, thumbnailStrip } = thumbnailStripSetup;
143
-
144
- expect(thumbnailStrip.targetElement).toBe(video);
145
- expect(thumbnailStrip.target).toBe("test-video");
146
- }, 1000);
147
- });
148
-
149
- describe("trimmed duration behavior", () => {
150
- test("should show thumbnails from trimmed time range by default", async ({
151
- expect,
152
- thumbnailStripSetup,
153
- }) => {
154
- const { video, thumbnailStrip } = thumbnailStripSetup;
155
-
156
- // Set trim properties on the video
157
- video.setAttribute("trimstart", "2s");
158
- video.setAttribute("trimend", "1s");
159
- await video.updateComplete;
160
- await video.mediaEngineTask.taskComplete;
161
-
162
- // Wait for thumbnail layout to complete
163
- await awaitThumbnailLayout(thumbnailStrip);
164
-
165
- // Video should have trimmed duration
166
- expect(video.sourceStartMs).toBe(2000); // trimstart 2s
167
- expect(video.durationMs).toBe(7000); // 10s - 2s trimstart - 1s trimend
168
-
169
- // Thumbnails should be generated for the trimmed range by default (not intrinsic)
170
- expect(thumbnailStrip.useIntrinsicDuration).toBe(false);
171
- }, 1000);
172
-
173
- test("should recalculate thumbnails when trim properties change dynamically", async ({
174
- expect,
175
- thumbnailStripSetup,
176
- }) => {
177
- const { video, thumbnailStrip } = thumbnailStripSetup;
178
-
179
- await video.mediaEngineTask.taskComplete;
180
-
181
- // Start with no trimming - should use full duration
182
- expect(video.sourceStartMs).toBe(0);
183
- expect(video.durationMs).toBe(10000); // full 10s duration
184
-
185
- // Wait for initial thumbnail layout
186
- await awaitThumbnailLayout(thumbnailStrip);
187
-
188
- // Now add trim properties dynamically
189
- video.setAttribute("trimstart", "3s");
190
- video.setAttribute("trimend", "2s");
191
- await video.updateComplete;
192
-
193
- // @ts-expect-error testing private task
194
- await thumbnailStrip.thumbnailLayoutTask.taskComplete;
195
-
196
- // Wait for the thumbnail update to complete
197
- await awaitThumbnailLayout(thumbnailStrip);
198
-
199
- // Video should now have updated trimmed values
200
- expect(video.sourceStartMs).toBe(3000); // trimstart 3s
201
- expect(video.durationMs).toBe(5000); // 10s - 3s trimstart - 2s trimend
202
-
203
- // Change trim properties again
204
- video.setAttribute("trimstart", "1s");
205
- video.setAttribute("trimend", "1s");
206
- await video.updateComplete;
207
-
208
- // Wait for the second thumbnail update
209
- await awaitThumbnailLayout(thumbnailStrip);
210
-
211
- // Video should reflect the new trim values
212
- expect(video.sourceStartMs).toBe(1000); // trimstart 1s
213
- expect(video.durationMs).toBe(8000); // 10s - 1s trimstart - 1s trimend
214
- }, 2000);
215
-
216
- test("should recalculate thumbnails when sourcein/sourceout properties change", async ({
217
- expect,
218
- thumbnailStripSetup,
219
- }) => {
220
- const { video, thumbnailStrip } = thumbnailStripSetup;
221
-
222
- await video.mediaEngineTask.taskComplete;
223
-
224
- // Start with sourcein/sourceout properties
225
- video.setAttribute("sourcein", "2s");
226
- video.setAttribute("sourceout", "8s");
227
- await video.updateComplete;
228
-
229
- // Wait for thumbnail layout
230
- await awaitThumbnailLayout(thumbnailStrip);
231
-
232
- // Video should have source-based duration
233
- expect(video.sourceStartMs).toBe(2000); // sourcein 2s
234
- expect(video.durationMs).toBe(6000); // sourceout 8s - sourcein 2s
235
-
236
- // Change the source properties
237
- video.setAttribute("sourcein", "1s");
238
- video.setAttribute("sourceout", "9s");
239
- await video.updateComplete;
240
-
241
- // Wait for thumbnail update
242
- await awaitThumbnailLayout(thumbnailStrip);
243
-
244
- // Video should reflect new source values
245
- expect(video.sourceStartMs).toBe(1000); // sourcein 1s
246
- expect(video.durationMs).toBe(8000); // sourceout 9s - sourcein 1s
247
- }, 2000);
248
-
249
- test("should show thumbnails from full duration when useIntrinsicDuration is true", async ({
250
- expect,
251
- thumbnailStripSetup,
252
- }) => {
253
- const { video, thumbnailStrip } = thumbnailStripSetup;
254
-
255
- // Set trim properties and useIntrinsicDuration
256
- video.setAttribute("trimstart", "2s");
257
- video.setAttribute("trimend", "1s");
258
- thumbnailStrip.setAttribute("use-intrinsic-duration", "true");
259
-
260
- await Promise.all([video.updateComplete, thumbnailStrip.updateComplete]);
261
- await video.mediaEngineTask.taskComplete;
262
-
263
- // Wait for thumbnail layout to complete
264
- await awaitThumbnailLayout(thumbnailStrip);
265
-
266
- // Video should have trimmed duration but thumbnail strip uses intrinsic
267
- expect(video.sourceStartMs).toBe(2000); // trimstart 2s
268
- expect(video.durationMs).toBe(7000); // trimmed duration (10s - 2s - 1s)
269
- expect(video.intrinsicDurationMs).toBe(10000); // full duration
270
- expect(thumbnailStrip.useIntrinsicDuration).toBe(true);
271
- }, 1000);
272
-
273
- test("should ignore all trim properties when useIntrinsicDuration is true", async ({
274
- expect,
275
- thumbnailStripSetup,
276
- }) => {
277
- const { video, thumbnailStrip } = thumbnailStripSetup;
278
-
279
- await video.mediaEngineTask.taskComplete;
280
-
281
- // First verify default trimmed behavior
282
- video.setAttribute("trimstart", "3s");
283
- video.setAttribute("trimend", "2s");
284
- await video.updateComplete;
285
-
286
- await awaitThumbnailLayout(thumbnailStrip);
287
-
288
- // Should respect trim by default
289
- expect(video.sourceStartMs).toBe(3000); // trimstart 3s
290
- expect(video.durationMs).toBe(5000); // 10s - 3s - 2s
291
-
292
- // Now enable useIntrinsicDuration
293
- thumbnailStrip.setAttribute("use-intrinsic-duration", "true");
294
- await thumbnailStrip.updateComplete;
295
-
296
- await awaitThumbnailLayout(thumbnailStrip);
297
-
298
- // Video properties should still reflect trim settings
299
- expect(video.sourceStartMs).toBe(3000); // trimstart 3s
300
- expect(video.durationMs).toBe(5000); // trimmed duration
301
- expect(video.intrinsicDurationMs).toBe(10000); // full duration
302
-
303
- // But thumbnail strip should ignore trims and use full duration
304
- expect(thumbnailStrip.useIntrinsicDuration).toBe(true);
305
-
306
- // The layout calculation should use 0 to intrinsicDurationMs instead of trimmed range
307
- // We can verify this by checking that the implementation correctly handles the flag
308
- }, 2000);
309
-
310
- test("should handle custom start-time-ms/end-time-ms relative to correct timeline", async ({
311
- expect,
312
- thumbnailStripSetup,
313
- }) => {
314
- const { video, thumbnailStrip } = thumbnailStripSetup;
315
-
316
- // Set up trim: source 0-10s becomes trimmed 0-7s (source 2-9s)
317
- video.setAttribute("trimstart", "2s");
318
- video.setAttribute("trimend", "1s");
319
- await video.updateComplete;
320
- // CRITICAL: Wait for media engine task completion AFTER setting trim attributes
321
- await video.mediaEngineTask.taskComplete;
322
-
323
- // Verify base setup
324
- expect(video.sourceStartMs).toBe(2000); // Trim starts at 2s in source
325
- expect(video.durationMs).toBe(7000); // 7s trimmed duration
326
-
327
- // Test custom time range in TRIMMED mode (default)
328
- // start-time-ms="1000" should mean 1s into the trimmed portion = 3s in source
329
- // end-time-ms="5000" should mean 5s into the trimmed portion = 7s in source
330
- thumbnailStrip.setAttribute("start-time-ms", "1000"); // 1s into trimmed timeline
331
- thumbnailStrip.setAttribute("end-time-ms", "5000"); // 5s into trimmed timeline
332
- await thumbnailStrip.updateComplete;
333
-
334
- // Verify the properties were set
335
- expect(thumbnailStrip.startTimeMs).toBe(1000);
336
- expect(thumbnailStrip.endTimeMs).toBe(5000);
337
-
338
- // Force thumbnail layout to run with new properties
339
- // @ts-expect-error missing implementation
340
- thumbnailStrip.thumbnailLayoutTask.run();
341
- await awaitThumbnailLayout(thumbnailStrip);
342
-
343
- // Get layout and check timestamps
344
- // @ts-expect-error missing implementation
345
- const layout = thumbnailStrip.thumbnailLayoutTask.value;
346
- expect(layout).toBeTruthy();
347
-
348
- if (layout) {
349
- const allTimestamps = layout.segments.flatMap((segment) =>
350
- segment.thumbnails.map((thumb) => thumb.timeMs),
351
- );
352
-
353
- expect(allTimestamps.length).toBeGreaterThan(0);
354
-
355
- // Thumbnails should be from 3000ms to 7000ms in source timeline
356
- // (trimstart 2000ms + custom start 1000ms = 3000ms to trimstart 2000ms + custom end 5000ms = 7000ms)
357
- const minTime = Math.min(...allTimestamps);
358
- const maxTime = Math.max(...allTimestamps);
359
-
360
- expect(minTime).toBeGreaterThanOrEqual(3000); // Should start from 3s in source
361
- expect(minTime).toBeLessThan(3500); // Should be close to 3s
362
- expect(maxTime).toBeLessThanOrEqual(7000); // Should end at 7s in source
363
- expect(maxTime).toBeGreaterThan(6500); // Should be close to 7s
364
- }
365
-
366
- // Test INTRINSIC mode with custom times
367
- // start-time-ms="1000" should mean 1s in source timeline
368
- // end-time-ms="8000" should mean 8s in source timeline
369
- thumbnailStrip.setAttribute("use-intrinsic-duration", "true");
370
- thumbnailStrip.setAttribute("start-time-ms", "1000"); // 1s in source timeline
371
- thumbnailStrip.setAttribute("end-time-ms", "8000"); // 8s in source timeline
372
- await thumbnailStrip.updateComplete;
373
-
374
- // Verify the properties were set correctly for intrinsic mode
375
- expect(thumbnailStrip.useIntrinsicDuration).toBe(true);
376
- expect(thumbnailStrip.startTimeMs).toBe(1000);
377
- expect(thumbnailStrip.endTimeMs).toBe(8000);
378
-
379
- // Force thumbnail layout to run with intrinsic mode properties
380
- // @ts-expect-error missing implementation
381
- thumbnailStrip.thumbnailLayoutTask.run();
382
- await awaitThumbnailLayout(thumbnailStrip);
383
-
384
- // @ts-expect-error missing implementation
385
- const intrinsicLayout = thumbnailStrip.thumbnailLayoutTask.value;
386
- expect(intrinsicLayout).toBeTruthy();
387
-
388
- if (intrinsicLayout) {
389
- const intrinsicTimestamps = intrinsicLayout.segments.flatMap(
390
- (segment) => segment.thumbnails.map((thumb) => thumb.timeMs),
391
- );
392
-
393
- expect(intrinsicTimestamps.length).toBeGreaterThan(0);
394
-
395
- // In intrinsic mode, should be 1000ms to 8000ms directly in source timeline
396
- const intrinsicMin = Math.min(...intrinsicTimestamps);
397
- const intrinsicMax = Math.max(...intrinsicTimestamps);
398
-
399
- expect(intrinsicMin).toBeGreaterThanOrEqual(1000); // Should start from 1s in source
400
- expect(intrinsicMin).toBeLessThan(1500); // Should be close to 1s
401
- expect(intrinsicMax).toBeLessThanOrEqual(8000); // Should end at 8s in source
402
- expect(intrinsicMax).toBeGreaterThan(7500); // Should be close to 8s
403
- }
404
- }, 3000);
405
-
406
- test("should correctly parse use-intrinsic-duration string values", async ({
407
- expect,
408
- thumbnailStripSetup,
409
- }) => {
410
- const { thumbnailStrip } = thumbnailStripSetup;
411
-
412
- // Test default value
413
- expect(thumbnailStrip.useIntrinsicDuration).toBe(false);
414
-
415
- // Test "true" string value
416
- thumbnailStrip.setAttribute("use-intrinsic-duration", "true");
417
- await thumbnailStrip.updateComplete;
418
- expect(thumbnailStrip.useIntrinsicDuration).toBe(true);
419
-
420
- // Test "false" string value (this is the key fix)
421
- thumbnailStrip.setAttribute("use-intrinsic-duration", "false");
422
- await thumbnailStrip.updateComplete;
423
- expect(thumbnailStrip.useIntrinsicDuration).toBe(false); // Should be false, not true!
424
-
425
- // Test removing attribute
426
- thumbnailStrip.removeAttribute("use-intrinsic-duration");
427
- await thumbnailStrip.updateComplete;
428
- expect(thumbnailStrip.useIntrinsicDuration).toBe(false);
429
-
430
- // Test setting via property
431
- thumbnailStrip.useIntrinsicDuration = true;
432
- await thumbnailStrip.updateComplete;
433
- expect(thumbnailStrip.getAttribute("use-intrinsic-duration")).toBe(
434
- "true",
435
- );
436
-
437
- thumbnailStrip.useIntrinsicDuration = false;
438
- await thumbnailStrip.updateComplete;
439
- expect(thumbnailStrip.hasAttribute("use-intrinsic-duration")).toBe(false);
440
- }, 1000);
441
-
442
- test("should show trimmed thumbnails when use-intrinsic-duration='false'", async ({
443
- expect,
444
- thumbnailStripSetup,
445
- }) => {
446
- const { video, thumbnailStrip } = thumbnailStripSetup;
447
-
448
- // Set trim and explicitly set use-intrinsic-duration="false"
449
- video.setAttribute("trimstart", "2s");
450
- thumbnailStrip.setAttribute("use-intrinsic-duration", "false"); // This should be false!
451
-
452
- await video.updateComplete;
453
- await thumbnailStrip.updateComplete;
454
- await video.mediaEngineTask.taskComplete;
455
-
456
- await awaitThumbnailLayout(thumbnailStrip);
457
-
458
- // Verify the boolean parsing worked correctly
459
- expect(thumbnailStrip.useIntrinsicDuration).toBe(false); // Should be false, not true!
460
- expect(thumbnailStrip.getAttribute("use-intrinsic-duration")).toBe(
461
- "false",
462
- );
463
-
464
- // Verify thumbnail behavior: should use trimmed timeline, starting from 2s
465
- expect(video.sourceStartMs).toBe(2000); // trimstart 2s
466
- expect(video.durationMs).toBe(8000); // 10s - 2s = 8s trimmed duration
467
-
468
- // @ts-expect-error testing private task
469
- const layout = thumbnailStrip.thumbnailLayoutTask.value;
470
- if (layout) {
471
- const allTimestamps = layout.segments.flatMap((segment) =>
472
- segment.thumbnails.map((thumb) => thumb.timeMs),
473
- );
474
- expect(allTimestamps.length).toBeGreaterThan(0);
475
-
476
- // Thumbnails should start from 2000ms (trimstart), not 0ms
477
- const firstTimestamp = Math.min(...allTimestamps);
478
- expect(firstTimestamp).toBeGreaterThanOrEqual(2000);
479
- expect(firstTimestamp).toBeLessThan(2500);
480
- }
481
- }, 1000);
482
-
483
- test("should align first thumbnail with video currentTime=0 frame", async ({
484
- expect,
485
- thumbnailStripSetup,
486
- }) => {
487
- const { video, thumbnailStrip } = thumbnailStripSetup;
488
-
489
- // Set trim to 2s - both video and thumbnails should show same frame
490
- video.setAttribute("trimstart", "2s");
491
- video.setAttribute("current-time", "0"); // 0 in trimmed timeline = 2s in source
492
-
493
- await video.updateComplete;
494
- await video.mediaEngineTask.taskComplete;
495
-
496
- // Wait for thumbnail calculations
497
- // @ts-expect-error testing private task
498
- await thumbnailStrip.thumbnailLayoutTask.taskComplete;
499
-
500
- // Get the video's current source time (what frame it's showing)
501
- const videoSourceTime = video.sourceStartMs + (video.currentTimeMs || 0);
502
- expect(videoSourceTime).toBe(2000); // Should be at 2s in source (frame 61)
503
-
504
- // Get the first thumbnail timestamp
505
- // @ts-expect-error testing private task
506
- const layout = thumbnailStrip.thumbnailLayoutTask.value;
507
- if (layout) {
508
- const allTimestamps = layout.segments.flatMap((segment) =>
509
- segment.thumbnails.map((thumb) => thumb.timeMs),
510
- );
511
- expect(allTimestamps.length).toBeGreaterThan(0);
512
-
513
- const firstThumbnailTime = Math.min(...allTimestamps);
514
-
515
- // The first thumbnail should be at exactly the same source time as video currentTime=0
516
- // This ensures frame 61 shows in both video and thumbnail
517
- expect(firstThumbnailTime).toBe(videoSourceTime); // Should be exactly 2000ms
518
- }
519
- }, 1000);
520
- });
521
-
522
- describe("layout behavior", () => {
523
- test("should calculate layout immediately after initialization", async ({
524
- expect,
525
- alternateSetup,
526
- }) => {
527
- const { video, thumbnailStrip } = alternateSetup;
528
-
529
- await video.mediaEngineTask.taskComplete;
530
-
531
- // @ts-expect-error testing private property
532
- expect(thumbnailStrip.stripWidth).toBe(400); // Uses container inner width
533
- expect(thumbnailStrip.targetElement).toBe(video);
534
- }, 1000);
535
-
536
- test("should remain stable when video properties change", async ({
537
- expect,
538
- thumbnailStripSetup,
539
- }) => {
540
- const { video, thumbnailStrip } = thumbnailStripSetup;
541
-
542
- await video.mediaEngineTask.taskComplete;
543
-
544
- const initialState = {
545
- // @ts-expect-error testing private property
546
- stripWidth: thumbnailStrip.stripWidth,
547
- targetElement: thumbnailStrip.targetElement,
548
- };
549
-
550
- video.setAttribute("trimstart", "2s");
551
- video.setAttribute("trimend", "2s");
552
- await video.updateComplete;
553
-
554
- // @ts-expect-error testing private property
555
- expect(thumbnailStrip.stripWidth).toBe(initialState.stripWidth);
556
- expect(thumbnailStrip.targetElement).toBe(initialState.targetElement);
557
- expect(video.getAttribute("trimstart")).toBe("2s");
558
- expect(video.getAttribute("trimend")).toBe("2s");
559
- }, 1000);
560
-
561
- test("should update dimensions when container resizes", async ({
562
- expect,
563
- alternateSetup,
564
- }) => {
565
- const { video, thumbnailStrip } = alternateSetup;
566
-
567
- await video.mediaEngineTask.taskComplete;
568
-
569
- // @ts-expect-error testing private property
570
- const initialWidth = thumbnailStrip.stripWidth;
571
- expect(initialWidth).toBe(400); // Container inner width
572
-
573
- // Wait for any pending thumbnail layout tasks to complete
574
- await awaitThumbnailLayout(thumbnailStrip);
575
-
576
- // Simulate resize by directly setting the internal width and triggering update
577
- (thumbnailStrip as any)._stripWidth = 800;
578
- // @ts-expect-error testing private property
579
- const finalWidth = thumbnailStrip.stripWidth;
580
-
581
- expect(finalWidth).toBe(800);
582
- expect(finalWidth).toBeGreaterThan(initialWidth);
583
- }, 1000);
584
- });
585
- });