@editframe/elements 0.16.7-beta.0 → 0.17.6-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 (101) hide show
  1. package/README.md +30 -0
  2. package/dist/DecoderResetFrequency.test.d.ts +1 -0
  3. package/dist/DecoderResetRecovery.test.d.ts +1 -0
  4. package/dist/DelayedLoadingState.d.ts +48 -0
  5. package/dist/DelayedLoadingState.integration.test.d.ts +1 -0
  6. package/dist/DelayedLoadingState.js +113 -0
  7. package/dist/DelayedLoadingState.test.d.ts +1 -0
  8. package/dist/EF_FRAMEGEN.d.ts +10 -1
  9. package/dist/EF_FRAMEGEN.js +199 -179
  10. package/dist/EF_INTERACTIVE.js +2 -6
  11. package/dist/EF_RENDERING.js +1 -3
  12. package/dist/JitTranscodingClient.browsertest.d.ts +1 -0
  13. package/dist/JitTranscodingClient.d.ts +167 -0
  14. package/dist/JitTranscodingClient.js +373 -0
  15. package/dist/JitTranscodingClient.test.d.ts +1 -0
  16. package/dist/LoadingDebounce.test.d.ts +1 -0
  17. package/dist/LoadingIndicator.browsertest.d.ts +0 -0
  18. package/dist/ManualScrubTest.test.d.ts +1 -0
  19. package/dist/ScrubResolvedFlashing.test.d.ts +1 -0
  20. package/dist/ScrubTrackIntegration.test.d.ts +1 -0
  21. package/dist/ScrubTrackManager.d.ts +96 -0
  22. package/dist/ScrubTrackManager.js +216 -0
  23. package/dist/ScrubTrackManager.test.d.ts +1 -0
  24. package/dist/SegmentSwitchLoading.test.d.ts +1 -0
  25. package/dist/VideoSeekFlashing.browsertest.d.ts +0 -0
  26. package/dist/VideoStuckDiagnostic.test.d.ts +1 -0
  27. package/dist/elements/CrossUpdateController.js +13 -15
  28. package/dist/elements/EFAudio.browsertest.d.ts +0 -0
  29. package/dist/elements/EFAudio.d.ts +1 -1
  30. package/dist/elements/EFAudio.js +30 -43
  31. package/dist/elements/EFCaptions.js +337 -373
  32. package/dist/elements/EFImage.js +64 -90
  33. package/dist/elements/EFMedia.d.ts +98 -33
  34. package/dist/elements/EFMedia.js +1169 -678
  35. package/dist/elements/EFSourceMixin.js +31 -48
  36. package/dist/elements/EFTemporal.d.ts +1 -0
  37. package/dist/elements/EFTemporal.js +266 -360
  38. package/dist/elements/EFTimegroup.d.ts +3 -1
  39. package/dist/elements/EFTimegroup.js +262 -323
  40. package/dist/elements/EFVideo.browsertest.d.ts +0 -0
  41. package/dist/elements/EFVideo.d.ts +90 -2
  42. package/dist/elements/EFVideo.js +408 -111
  43. package/dist/elements/EFWaveform.js +375 -411
  44. package/dist/elements/FetchMixin.js +14 -24
  45. package/dist/elements/MediaController.d.ts +30 -0
  46. package/dist/elements/TargetController.js +130 -156
  47. package/dist/elements/TimegroupController.js +17 -19
  48. package/dist/elements/durationConverter.js +15 -4
  49. package/dist/elements/parseTimeToMs.js +4 -10
  50. package/dist/elements/printTaskStatus.d.ts +2 -0
  51. package/dist/elements/printTaskStatus.js +11 -0
  52. package/dist/elements/updateAnimations.js +39 -59
  53. package/dist/getRenderInfo.js +58 -67
  54. package/dist/gui/ContextMixin.js +203 -288
  55. package/dist/gui/EFConfiguration.js +27 -43
  56. package/dist/gui/EFFilmstrip.js +440 -620
  57. package/dist/gui/EFFitScale.js +112 -135
  58. package/dist/gui/EFFocusOverlay.js +45 -61
  59. package/dist/gui/EFPreview.js +30 -49
  60. package/dist/gui/EFScrubber.js +78 -99
  61. package/dist/gui/EFTimeDisplay.js +49 -70
  62. package/dist/gui/EFToggleLoop.js +17 -34
  63. package/dist/gui/EFTogglePlay.js +37 -58
  64. package/dist/gui/EFWorkbench.js +66 -88
  65. package/dist/gui/TWMixin.js +2 -48
  66. package/dist/gui/TWMixin2.js +31 -0
  67. package/dist/gui/efContext.js +2 -6
  68. package/dist/gui/fetchContext.js +1 -3
  69. package/dist/gui/focusContext.js +1 -3
  70. package/dist/gui/focusedElementContext.js +2 -6
  71. package/dist/gui/playingContext.js +1 -4
  72. package/dist/index.js +5 -30
  73. package/dist/msToTimeCode.js +11 -13
  74. package/dist/style.css +2 -1
  75. package/package.json +3 -3
  76. package/src/elements/EFAudio.browsertest.ts +569 -0
  77. package/src/elements/EFAudio.ts +4 -6
  78. package/src/elements/EFCaptions.browsertest.ts +0 -1
  79. package/src/elements/EFImage.browsertest.ts +0 -1
  80. package/src/elements/EFMedia.browsertest.ts +147 -115
  81. package/src/elements/EFMedia.ts +1339 -307
  82. package/src/elements/EFTemporal.browsertest.ts +0 -1
  83. package/src/elements/EFTemporal.ts +11 -0
  84. package/src/elements/EFTimegroup.ts +73 -10
  85. package/src/elements/EFVideo.browsertest.ts +680 -0
  86. package/src/elements/EFVideo.ts +729 -50
  87. package/src/elements/EFWaveform.ts +4 -4
  88. package/src/elements/MediaController.ts +108 -0
  89. package/src/elements/__screenshots__/EFMedia.browsertest.ts/EFMedia-JIT-audio-playback-audioBufferTask-should-work-in-JIT-mode-without-URL-errors-1.png +0 -0
  90. package/src/elements/printTaskStatus.ts +16 -0
  91. package/src/elements/updateAnimations.ts +6 -0
  92. package/src/gui/TWMixin.ts +10 -3
  93. package/test/EFVideo.frame-tasks.browsertest.ts +524 -0
  94. package/test/EFVideo.framegen.browsertest.ts +118 -0
  95. package/test/createJitTestClips.ts +293 -0
  96. package/test/useAssetMSW.ts +49 -0
  97. package/test/useMSW.ts +31 -0
  98. package/types.json +1 -1
  99. package/dist/gui/TWMixin.css.js +0 -4
  100. /package/dist/elements/{TargetController.test.d.ts → TargetController.browsertest.d.ts} +0 -0
  101. /package/src/elements/{TargetController.test.ts → TargetController.browsertest.ts} +0 -0
@@ -47,7 +47,6 @@ describe("sourcein and sourceout", () => {
47
47
  const element = document.createElement("ten-seconds");
48
48
  element.sourceInMs = 5_000;
49
49
  element.sourceOutMs = 1_000;
50
- console.log(element.sourceInMs, element.sourceOutMs, element.durationMs);
51
50
  expect(element.durationMs).toBe(0);
52
51
  });
53
52
  });
@@ -292,6 +292,10 @@ const resetStartTimeMsCache = () => {
292
292
  };
293
293
  resetStartTimeMsCache();
294
294
 
295
+ export const flushStartTimeMsCache = () => {
296
+ startTimeMsCache = new WeakMap();
297
+ };
298
+
295
299
  export const EFTemporal = <T extends Constructor<LitElement>>(
296
300
  superClass: T,
297
301
  ) => {
@@ -536,12 +540,19 @@ export const EFTemporal = <T extends Constructor<LitElement>>(
536
540
  const ownIndex = siblingTemorals?.indexOf(
537
541
  this as InstanceType<Constructor<TemporalMixinInterface> & T>,
538
542
  );
543
+ if (ownIndex === -1) {
544
+ return 0;
545
+ }
539
546
  if (ownIndex === 0) {
540
547
  startTimeMsCache.set(this, parentTimegroup.startTimeMs);
541
548
  return parentTimegroup.startTimeMs;
542
549
  }
543
550
  const previous = siblingTemorals?.[(ownIndex ?? 0) - 1];
544
551
  if (!previous) {
552
+ console.log("Previous temporal element not found", {
553
+ ownIndex,
554
+ siblingTemorals,
555
+ });
545
556
  throw new Error("Previous temporal element not found");
546
557
  }
547
558
  startTimeMsCache.set(
@@ -1,5 +1,5 @@
1
1
  import { provide } from "@lit/context";
2
- import { Task } from "@lit/task";
2
+ import { Task, TaskStatus } from "@lit/task";
3
3
  import debug from "debug";
4
4
  import { LitElement, type PropertyValueMap, css, html } from "lit";
5
5
  import { customElement, property } from "lit/decorators.js";
@@ -9,6 +9,8 @@ import { isContextMixin } from "../gui/ContextMixin.js";
9
9
  import { deepGetMediaElements } from "./EFMedia.js";
10
10
  import {
11
11
  EFTemporal,
12
+ deepGetElementsWithFrameTasks,
13
+ flushStartTimeMsCache,
12
14
  shallowGetTemporalElements,
13
15
  timegroupContext,
14
16
  } from "./EFTemporal.js";
@@ -110,7 +112,7 @@ export class EFTimegroup extends EFTemporal(LitElement) {
110
112
  super.connectedCallback();
111
113
  if (this.id) {
112
114
  this.waitForMediaDurations().then(() => {
113
- this.#currentTime = this.maybeLoadTimeFromLocalStorage();
115
+ this.currentTime = this.maybeLoadTimeFromLocalStorage();
114
116
  });
115
117
  }
116
118
 
@@ -197,6 +199,30 @@ export class EFTimegroup extends EFTemporal(LitElement) {
197
199
  }
198
200
  }
199
201
 
202
+ async getPendingFrameTasks() {
203
+ await this.updateComplete;
204
+ const temporals = deepGetElementsWithFrameTasks(this);
205
+ return temporals
206
+ .map((temporal) => temporal.frameTask)
207
+ .filter((task) => task.status < TaskStatus.COMPLETE);
208
+ }
209
+
210
+ async waitForFrameTasks() {
211
+ const limit = 10;
212
+ let step = 0;
213
+ await this.updateComplete;
214
+ while (step < limit) {
215
+ step++;
216
+ let pendingTasks = await this.getPendingFrameTasks();
217
+ await Promise.all(pendingTasks.map((task) => task.taskComplete));
218
+ await this.updateComplete;
219
+ pendingTasks = await this.getPendingFrameTasks();
220
+ if (pendingTasks.length === 0) {
221
+ break;
222
+ }
223
+ }
224
+ }
225
+
200
226
  /**
201
227
  * Wait for all media elements to load their initial segments.
202
228
  * Ideally we would only need the extracted index json data, but
@@ -204,10 +230,32 @@ export class EFTimegroup extends EFTemporal(LitElement) {
204
230
  * in calculations and it was not clear why.
205
231
  */
206
232
  async waitForMediaDurations() {
233
+ // We must await updateComplete to ensure all media elements inside this are connected
234
+ // and will match deepGetMediaElements
235
+ await this.updateComplete;
207
236
  const mediaElements = deepGetMediaElements(this);
208
- return await Promise.all(
209
- mediaElements.map((m) => m.trackFragmentIndexLoader.taskComplete),
237
+ // Then, we must await the fragmentIndexTask to ensure all media elements have their
238
+ // fragment index loaded, which is where their duration is parsed from.
239
+ await Promise.all(
240
+ mediaElements.map((m) => m.fragmentIndexTask.taskComplete),
210
241
  );
242
+
243
+ // After waiting for durations, we must force some updates to cascade and ensure all temporal elements
244
+ // have correct durations and start times. It is not ideal that we have to do this inside here,
245
+ // but it is the best current way to ensure that all temporal elements have correct durations and start times.
246
+
247
+ // Next, we must flush the startTimeMs cache to ensure all media elements have their
248
+ // startTimeMs parsed fresh, otherwise the startTimeMs is cached per animation frame.
249
+ flushStartTimeMsCache();
250
+
251
+ // Request an update to the currentTime of this group, ensuring that time updates will cascade
252
+ // down to children, forcing sequence groups to arrange correctly.
253
+ // This also makes the filmstrip update correctly.
254
+ this.requestUpdate("currentTime");
255
+ // Finally, we must await updateComplete to ensure all temporal elements have their
256
+ // currentTime updated and all animations have run.
257
+
258
+ await this.updateComplete;
211
259
  }
212
260
 
213
261
  get childTemporals() {
@@ -328,13 +376,28 @@ export class EFTimegroup extends EFTemporal(LitElement) {
328
376
 
329
377
  async renderAudio(fromMs: number, toMs: number) {
330
378
  const durationMs = toMs - fromMs;
331
- const audioContext = new OfflineAudioContext(
332
- 2,
333
- Math.round((48000 * durationMs) / 1000),
334
- 48000,
335
- );
379
+ const contextSize = Math.round((48000 * durationMs) / 1000);
380
+
381
+ // Debug logging for audio duration calculations
382
+ if (contextSize <= 0) {
383
+ throw new Error(
384
+ `Duration must be greater than 0 when rendering audio. ${contextSize}ms`,
385
+ );
386
+ }
387
+
388
+ let audioContext: OfflineAudioContext;
389
+ try {
390
+ audioContext = new OfflineAudioContext(2, contextSize, 48000);
391
+ } catch (error) {
392
+ throw new Error(
393
+ `[EFTimegroup.renderAudio] Failed to create OfflineAudioContext(2, ${contextSize}, 48000) for renderAudio(${fromMs}, ${toMs}) with contextSize=${contextSize}: ${error instanceof Error ? error.message : String(error)}. This typically happens when audio parameters are invalid (e.g., contextSize <= 0).`,
394
+ );
395
+ }
396
+
336
397
  await this.#addAudioToContext(audioContext, fromMs, toMs);
337
- return await audioContext.startRendering();
398
+ const renderedBuffer = await audioContext.startRendering();
399
+
400
+ return renderedBuffer;
338
401
  }
339
402
 
340
403
  async loadMd5Sums() {