@editframe/elements 0.17.6-beta.0 → 0.18.3-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 (218) hide show
  1. package/dist/EF_FRAMEGEN.js +1 -1
  2. package/dist/ScrubTrackManager.d.ts +2 -2
  3. package/dist/elements/EFAudio.d.ts +21 -2
  4. package/dist/elements/EFAudio.js +41 -11
  5. package/dist/elements/EFImage.d.ts +1 -0
  6. package/dist/elements/EFImage.js +11 -3
  7. package/dist/elements/EFMedia/AssetIdMediaEngine.d.ts +18 -0
  8. package/dist/elements/EFMedia/AssetIdMediaEngine.js +41 -0
  9. package/dist/elements/EFMedia/AssetMediaEngine.d.ts +47 -0
  10. package/dist/elements/EFMedia/AssetMediaEngine.js +116 -0
  11. package/dist/elements/EFMedia/BaseMediaEngine.d.ts +55 -0
  12. package/dist/elements/EFMedia/BaseMediaEngine.js +96 -0
  13. package/dist/elements/EFMedia/BufferedSeekingInput.d.ts +43 -0
  14. package/dist/elements/EFMedia/BufferedSeekingInput.js +159 -0
  15. package/dist/elements/EFMedia/JitMediaEngine.browsertest.d.ts +0 -0
  16. package/dist/elements/EFMedia/JitMediaEngine.d.ts +31 -0
  17. package/dist/elements/EFMedia/JitMediaEngine.js +62 -0
  18. package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.browsertest.d.ts +9 -0
  19. package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.d.ts +16 -0
  20. package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.js +48 -0
  21. package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.d.ts +3 -0
  22. package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.js +138 -0
  23. package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.browsertest.d.ts +9 -0
  24. package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.d.ts +4 -0
  25. package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.js +16 -0
  26. package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.browsertest.d.ts +9 -0
  27. package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.d.ts +3 -0
  28. package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.js +22 -0
  29. package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.d.ts +7 -0
  30. package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.js +24 -0
  31. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.d.ts +4 -0
  32. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.js +18 -0
  33. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.d.ts +4 -0
  34. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.js +16 -0
  35. package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.d.ts +3 -0
  36. package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.js +104 -0
  37. package/dist/elements/EFMedia/services/AudioElementFactory.d.ts +22 -0
  38. package/dist/elements/EFMedia/services/AudioElementFactory.js +72 -0
  39. package/dist/elements/EFMedia/services/MediaSourceService.browsertest.d.ts +1 -0
  40. package/dist/elements/EFMedia/services/MediaSourceService.d.ts +47 -0
  41. package/dist/elements/EFMedia/services/MediaSourceService.js +73 -0
  42. package/dist/elements/EFMedia/shared/AudioSpanUtils.d.ts +7 -0
  43. package/dist/elements/EFMedia/shared/AudioSpanUtils.js +54 -0
  44. package/dist/elements/EFMedia/shared/BufferUtils.d.ts +70 -0
  45. package/dist/elements/EFMedia/shared/BufferUtils.js +89 -0
  46. package/dist/elements/EFMedia/shared/MediaTaskUtils.d.ts +23 -0
  47. package/dist/elements/EFMedia/shared/RenditionHelpers.browsertest.d.ts +1 -0
  48. package/dist/elements/EFMedia/shared/RenditionHelpers.d.ts +19 -0
  49. package/dist/elements/EFMedia/tasks/makeMediaEngineTask.browsertest.d.ts +1 -0
  50. package/dist/elements/EFMedia/tasks/makeMediaEngineTask.d.ts +18 -0
  51. package/dist/elements/EFMedia/tasks/makeMediaEngineTask.js +60 -0
  52. package/dist/elements/EFMedia/tasks/makeMediaEngineTask.test.d.ts +1 -0
  53. package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.browsertest.d.ts +9 -0
  54. package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.d.ts +16 -0
  55. package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.js +46 -0
  56. package/dist/elements/EFMedia/videoTasks/makeVideoInitSegmentFetchTask.browsertest.d.ts +9 -0
  57. package/dist/elements/EFMedia/videoTasks/makeVideoInitSegmentFetchTask.d.ts +4 -0
  58. package/dist/elements/EFMedia/videoTasks/makeVideoInitSegmentFetchTask.js +16 -0
  59. package/dist/elements/EFMedia/videoTasks/makeVideoInputTask.browsertest.d.ts +9 -0
  60. package/dist/elements/EFMedia/videoTasks/makeVideoInputTask.d.ts +3 -0
  61. package/dist/elements/EFMedia/videoTasks/makeVideoInputTask.js +27 -0
  62. package/dist/elements/EFMedia/videoTasks/makeVideoSeekTask.d.ts +7 -0
  63. package/dist/elements/EFMedia/videoTasks/makeVideoSeekTask.js +25 -0
  64. package/dist/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.browsertest.d.ts +9 -0
  65. package/dist/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.d.ts +4 -0
  66. package/dist/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.js +18 -0
  67. package/dist/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.browsertest.d.ts +9 -0
  68. package/dist/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.d.ts +4 -0
  69. package/dist/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.js +16 -0
  70. package/dist/elements/EFMedia.browsertest.d.ts +1 -0
  71. package/dist/elements/EFMedia.d.ts +75 -111
  72. package/dist/elements/EFMedia.js +141 -1111
  73. package/dist/elements/EFTemporal.d.ts +1 -1
  74. package/dist/elements/EFTemporal.js +1 -1
  75. package/dist/elements/EFTimegroup.d.ts +11 -0
  76. package/dist/elements/EFTimegroup.js +88 -13
  77. package/dist/elements/EFVideo.d.ts +60 -29
  78. package/dist/elements/EFVideo.js +103 -203
  79. package/dist/elements/EFWaveform.js +2 -2
  80. package/dist/elements/SampleBuffer.d.ts +14 -0
  81. package/dist/elements/SampleBuffer.js +52 -0
  82. package/dist/getRenderInfo.d.ts +2 -2
  83. package/dist/getRenderInfo.js +2 -1
  84. package/dist/gui/ContextMixin.js +17 -70
  85. package/dist/gui/EFFilmstrip.d.ts +3 -3
  86. package/dist/gui/EFFilmstrip.js +1 -1
  87. package/dist/gui/EFFitScale.d.ts +2 -2
  88. package/dist/gui/TWMixin.js +1 -1
  89. package/dist/gui/services/ElementConnectionManager.browsertest.d.ts +1 -0
  90. package/dist/gui/services/ElementConnectionManager.d.ts +59 -0
  91. package/dist/gui/services/ElementConnectionManager.js +128 -0
  92. package/dist/gui/services/PlaybackController.browsertest.d.ts +1 -0
  93. package/dist/gui/services/PlaybackController.d.ts +103 -0
  94. package/dist/gui/services/PlaybackController.js +290 -0
  95. package/dist/services/MediaSourceManager.d.ts +62 -0
  96. package/dist/services/MediaSourceManager.js +211 -0
  97. package/dist/style.css +1 -1
  98. package/dist/transcoding/cache/CacheManager.d.ts +73 -0
  99. package/dist/transcoding/cache/RequestDeduplicator.d.ts +29 -0
  100. package/dist/transcoding/cache/RequestDeduplicator.js +53 -0
  101. package/dist/transcoding/cache/RequestDeduplicator.test.d.ts +1 -0
  102. package/dist/transcoding/types/index.d.ts +242 -0
  103. package/dist/transcoding/utils/MediaUtils.d.ts +9 -0
  104. package/dist/transcoding/utils/UrlGenerator.d.ts +26 -0
  105. package/dist/transcoding/utils/UrlGenerator.js +45 -0
  106. package/dist/transcoding/utils/constants.d.ts +27 -0
  107. package/dist/utils/LRUCache.d.ts +34 -0
  108. package/dist/utils/LRUCache.js +115 -0
  109. package/package.json +3 -2
  110. package/src/elements/EFAudio.browsertest.ts +183 -43
  111. package/src/elements/EFAudio.ts +59 -13
  112. package/src/elements/EFImage.browsertest.ts +42 -0
  113. package/src/elements/EFImage.ts +23 -3
  114. package/src/elements/EFMedia/AssetIdMediaEngine.test.ts +222 -0
  115. package/src/elements/EFMedia/AssetIdMediaEngine.ts +70 -0
  116. package/src/elements/EFMedia/AssetMediaEngine.ts +210 -0
  117. package/src/elements/EFMedia/BaseMediaEngine.test.ts +164 -0
  118. package/src/elements/EFMedia/BaseMediaEngine.ts +170 -0
  119. package/src/elements/EFMedia/BufferedSeekingInput.browsertest.ts +400 -0
  120. package/src/elements/EFMedia/BufferedSeekingInput.ts +267 -0
  121. package/src/elements/EFMedia/JitMediaEngine.browsertest.ts +165 -0
  122. package/src/elements/EFMedia/JitMediaEngine.ts +110 -0
  123. package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.browsertest.ts +554 -0
  124. package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.ts +81 -0
  125. package/src/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.ts +241 -0
  126. package/src/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.browsertest.ts +59 -0
  127. package/src/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.ts +23 -0
  128. package/src/elements/EFMedia/audioTasks/makeAudioInputTask.browsertest.ts +55 -0
  129. package/src/elements/EFMedia/audioTasks/makeAudioInputTask.ts +35 -0
  130. package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.ts +42 -0
  131. package/src/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.ts +34 -0
  132. package/src/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.ts +23 -0
  133. package/src/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.ts +174 -0
  134. package/src/elements/EFMedia/services/AudioElementFactory.browsertest.ts +325 -0
  135. package/src/elements/EFMedia/services/AudioElementFactory.ts +119 -0
  136. package/src/elements/EFMedia/services/MediaSourceService.browsertest.ts +257 -0
  137. package/src/elements/EFMedia/services/MediaSourceService.ts +102 -0
  138. package/src/elements/EFMedia/shared/AudioSpanUtils.ts +128 -0
  139. package/src/elements/EFMedia/shared/BufferUtils.ts +310 -0
  140. package/src/elements/EFMedia/shared/MediaTaskUtils.ts +44 -0
  141. package/src/elements/EFMedia/shared/RenditionHelpers.browsertest.ts +247 -0
  142. package/src/elements/EFMedia/shared/RenditionHelpers.ts +79 -0
  143. package/src/elements/EFMedia/tasks/makeMediaEngineTask.browsertest.ts +128 -0
  144. package/src/elements/EFMedia/tasks/makeMediaEngineTask.test.ts +233 -0
  145. package/src/elements/EFMedia/tasks/makeMediaEngineTask.ts +89 -0
  146. package/src/elements/EFMedia/videoTasks/makeVideoBufferTask.browsertest.ts +555 -0
  147. package/src/elements/EFMedia/videoTasks/makeVideoBufferTask.ts +79 -0
  148. package/src/elements/EFMedia/videoTasks/makeVideoInitSegmentFetchTask.browsertest.ts +59 -0
  149. package/src/elements/EFMedia/videoTasks/makeVideoInitSegmentFetchTask.ts +23 -0
  150. package/src/elements/EFMedia/videoTasks/makeVideoInputTask.browsertest.ts +55 -0
  151. package/src/elements/EFMedia/videoTasks/makeVideoInputTask.ts +45 -0
  152. package/src/elements/EFMedia/videoTasks/makeVideoSeekTask.ts +44 -0
  153. package/src/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.browsertest.ts +57 -0
  154. package/src/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.ts +32 -0
  155. package/src/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.browsertest.ts +56 -0
  156. package/src/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.ts +23 -0
  157. package/src/elements/EFMedia.browsertest.ts +658 -265
  158. package/src/elements/EFMedia.ts +173 -1763
  159. package/src/elements/EFTemporal.ts +3 -4
  160. package/src/elements/EFTimegroup.browsertest.ts +6 -3
  161. package/src/elements/EFTimegroup.ts +152 -21
  162. package/src/elements/EFVideo.browsertest.ts +115 -37
  163. package/src/elements/EFVideo.ts +123 -452
  164. package/src/elements/EFWaveform.ts +1 -1
  165. package/src/elements/MediaController.ts +2 -12
  166. package/src/elements/SampleBuffer.ts +97 -0
  167. package/src/gui/ContextMixin.ts +23 -104
  168. package/src/gui/services/ElementConnectionManager.browsertest.ts +263 -0
  169. package/src/gui/services/ElementConnectionManager.ts +224 -0
  170. package/src/gui/services/PlaybackController.browsertest.ts +437 -0
  171. package/src/gui/services/PlaybackController.ts +521 -0
  172. package/src/services/MediaSourceManager.ts +333 -0
  173. package/src/transcoding/cache/CacheManager.ts +208 -0
  174. package/src/transcoding/cache/RequestDeduplicator.test.ts +170 -0
  175. package/src/transcoding/cache/RequestDeduplicator.ts +65 -0
  176. package/src/transcoding/types/index.ts +265 -0
  177. package/src/transcoding/utils/MediaUtils.ts +63 -0
  178. package/src/transcoding/utils/UrlGenerator.ts +68 -0
  179. package/src/transcoding/utils/constants.ts +36 -0
  180. package/src/utils/LRUCache.ts +153 -0
  181. package/test/EFVideo.framegen.browsertest.ts +38 -29
  182. package/test/__cache__/GET__api_v1_transcode_audio_1_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__32da3954ba60c96ad732020c65a08ebc/data.bin +0 -0
  183. package/test/__cache__/GET__api_v1_transcode_audio_1_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__32da3954ba60c96ad732020c65a08ebc/metadata.json +21 -0
  184. package/test/__cache__/GET__api_v1_transcode_audio_2_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__b0b2b07efcf607de8ee0f650328c32f7/data.bin +0 -0
  185. package/test/__cache__/GET__api_v1_transcode_audio_2_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__b0b2b07efcf607de8ee0f650328c32f7/metadata.json +21 -0
  186. package/test/__cache__/GET__api_v1_transcode_audio_3_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__a75c2252b542e0c152c780e9a8d7b154/data.bin +0 -0
  187. package/test/__cache__/GET__api_v1_transcode_audio_3_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__a75c2252b542e0c152c780e9a8d7b154/metadata.json +21 -0
  188. package/test/__cache__/GET__api_v1_transcode_audio_4_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__a64ff1cfb1b52cae14df4b5dfa1e222b/data.bin +0 -0
  189. package/test/__cache__/GET__api_v1_transcode_audio_4_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__a64ff1cfb1b52cae14df4b5dfa1e222b/metadata.json +21 -0
  190. package/test/__cache__/GET__api_v1_transcode_audio_5_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__91e8a522f950809b9f09f4173113b4b0/data.bin +0 -0
  191. package/test/__cache__/GET__api_v1_transcode_audio_5_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__91e8a522f950809b9f09f4173113b4b0/metadata.json +21 -0
  192. package/test/__cache__/GET__api_v1_transcode_audio_init_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__e66d2c831d951e74ad0aeaa6489795d0/data.bin +0 -0
  193. package/test/__cache__/GET__api_v1_transcode_audio_init_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__e66d2c831d951e74ad0aeaa6489795d0/metadata.json +21 -0
  194. package/test/__cache__/GET__api_v1_transcode_high_1_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__26197f6f7c46cacb0a71134131c3f775/data.bin +0 -0
  195. package/test/__cache__/GET__api_v1_transcode_high_1_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__26197f6f7c46cacb0a71134131c3f775/metadata.json +21 -0
  196. package/test/__cache__/GET__api_v1_transcode_high_2_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__4cb6774cd3650ccf59c8f8dc6678c0b9/data.bin +0 -0
  197. package/test/__cache__/GET__api_v1_transcode_high_2_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__4cb6774cd3650ccf59c8f8dc6678c0b9/metadata.json +21 -0
  198. package/test/__cache__/GET__api_v1_transcode_high_3_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__0b3b2b1c8933f7fcf8a9ecaa88d58b41/data.bin +0 -0
  199. package/test/__cache__/GET__api_v1_transcode_high_3_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__0b3b2b1c8933f7fcf8a9ecaa88d58b41/metadata.json +21 -0
  200. package/test/__cache__/GET__api_v1_transcode_high_init_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__0798c479b44aaeef850609a430f6e613/data.bin +0 -0
  201. package/test/__cache__/GET__api_v1_transcode_high_init_m4s_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__0798c479b44aaeef850609a430f6e613/metadata.json +21 -0
  202. package/test/__cache__/GET__api_v1_transcode_manifest_json_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__3be92a0437de726b431ed5af2369158a/data.bin +1 -0
  203. package/test/__cache__/GET__api_v1_transcode_manifest_json_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__3be92a0437de726b431ed5af2369158a/metadata.json +19 -0
  204. package/test/createJitTestClips.ts +320 -188
  205. package/test/recordReplayProxyPlugin.js +302 -0
  206. package/test/useAssetMSW.ts +1 -1
  207. package/test/useMSW.ts +35 -22
  208. package/types.json +1 -1
  209. package/dist/JitTranscodingClient.d.ts +0 -167
  210. package/dist/JitTranscodingClient.js +0 -373
  211. package/dist/ScrubTrackManager.js +0 -216
  212. package/dist/elements/printTaskStatus.js +0 -11
  213. package/src/elements/__screenshots__/EFMedia.browsertest.ts/EFMedia-JIT-audio-playback-audioBufferTask-should-work-in-JIT-mode-without-URL-errors-1.png +0 -0
  214. package/test/EFVideo.frame-tasks.browsertest.ts +0 -524
  215. /package/dist/{JitTranscodingClient.browsertest.d.ts → elements/EFMedia/AssetIdMediaEngine.test.d.ts} +0 -0
  216. /package/dist/{JitTranscodingClient.test.d.ts → elements/EFMedia/BaseMediaEngine.test.d.ts} +0 -0
  217. /package/dist/{ScrubTrackIntegration.test.d.ts → elements/EFMedia/BufferedSeekingInput.browsertest.d.ts} +0 -0
  218. /package/dist/{SegmentSwitchLoading.test.d.ts → elements/EFMedia/services/AudioElementFactory.browsertest.d.ts} +0 -0
@@ -1,11 +1,10 @@
1
1
  import { consume, createContext } from "@lit/context";
2
+ import { Task } from "@lit/task";
2
3
  import type { LitElement, PropertyValueMap, ReactiveController } from "lit";
3
4
  import { property, state } from "lit/decorators.js";
4
- import type { EFTimegroup } from "./EFTimegroup.js";
5
-
6
- import { Task } from "@lit/task";
7
5
  import { EF_INTERACTIVE } from "../EF_INTERACTIVE.js";
8
6
  import { durationConverter } from "./durationConverter.js";
7
+ import type { EFTimegroup } from "./EFTimegroup.js";
9
8
 
10
9
  export const timegroupContext = createContext<EFTimegroup>(
11
10
  Symbol("timeGroupContext"),
@@ -549,7 +548,7 @@ export const EFTemporal = <T extends Constructor<LitElement>>(
549
548
  }
550
549
  const previous = siblingTemorals?.[(ownIndex ?? 0) - 1];
551
550
  if (!previous) {
552
- console.log("Previous temporal element not found", {
551
+ console.error("Previous temporal element not found", {
553
552
  ownIndex,
554
553
  siblingTemorals,
555
554
  });
@@ -1,8 +1,8 @@
1
1
  import {
2
- LitElement,
3
- type TemplateResult,
4
2
  html,
3
+ LitElement,
5
4
  render as litRender,
5
+ type TemplateResult,
6
6
  } from "lit";
7
7
  import { assert, beforeEach, describe, test } from "vitest";
8
8
  import { EFTimegroup } from "./EFTimegroup.js";
@@ -353,7 +353,7 @@ describe("setting currentTime", () => {
353
353
  document.body.appendChild(timegroup);
354
354
  assert.isNull(localStorage.getItem(timegroup.storageKey));
355
355
  timegroup.currentTime = 5_000;
356
- assert.equal(localStorage.getItem(timegroup.storageKey), "5000");
356
+ assert.equal(localStorage.getItem(timegroup.storageKey), "10"); // Clamped to duration
357
357
  timegroup.remove();
358
358
  });
359
359
 
@@ -392,6 +392,9 @@ describe("setting currentTime", () => {
392
392
  assert.equal(a.ownCurrentTimeMs, 2_500);
393
393
  assert.equal(b.ownCurrentTimeMs, 0);
394
394
 
395
+ // Wait for frame update to complete before next assignment
396
+ await new Promise((resolve) => setTimeout(resolve, 10));
397
+
395
398
  root.currentTimeMs = 7_500;
396
399
 
397
400
  assert.equal(a.ownCurrentTimeMs, 5_000);
@@ -1,21 +1,21 @@
1
1
  import { provide } from "@lit/context";
2
2
  import { Task, TaskStatus } from "@lit/task";
3
3
  import debug from "debug";
4
- import { LitElement, type PropertyValueMap, css, html } from "lit";
4
+ import { css, html, LitElement, type PropertyValueMap } from "lit";
5
5
  import { customElement, property } from "lit/decorators.js";
6
6
 
7
7
  import { EF_INTERACTIVE } from "../EF_INTERACTIVE.js";
8
8
  import { isContextMixin } from "../gui/ContextMixin.js";
9
+ import { durationConverter } from "./durationConverter.js";
9
10
  import { deepGetMediaElements } from "./EFMedia.js";
10
11
  import {
11
- EFTemporal,
12
12
  deepGetElementsWithFrameTasks,
13
+ EFTemporal,
13
14
  flushStartTimeMsCache,
14
15
  shallowGetTemporalElements,
15
16
  timegroupContext,
16
17
  } from "./EFTemporal.js";
17
18
  import { TimegroupController } from "./TimegroupController.js";
18
- import { durationConverter } from "./durationConverter.js";
19
19
  import { updateAnimations } from "./updateAnimations.ts";
20
20
 
21
21
  const log = debug("ef:elements:EFTimegroup");
@@ -52,6 +52,10 @@ export class EFTimegroup extends EFTemporal(LitElement) {
52
52
 
53
53
  #currentTime = 0;
54
54
 
55
+ // Frame update locking mechanism (only for root timegroups)
56
+ private isFrameUpdateInProgress = false;
57
+ private queuedTimeUpdate: number | null = null;
58
+
55
59
  @property({
56
60
  type: String,
57
61
  attribute: "mode",
@@ -72,27 +76,84 @@ export class EFTimegroup extends EFTemporal(LitElement) {
72
76
 
73
77
  @property({ type: Number, attribute: "currenttime" })
74
78
  set currentTime(time: number) {
75
- this.#currentTime = Math.max(0, Math.min(time, this.durationMs / 1000));
76
- try {
77
- if (this.id) {
78
- if (this.isConnected) {
79
- localStorage.setItem(this.storageKey, time.toString());
80
- }
81
- }
82
- } catch (error) {
83
- log("Failed to save time to localStorage", error);
79
+ const newTime = Math.max(0, Math.min(time, this.durationMs / 1000));
80
+
81
+ // Only apply locking mechanism for root timegroups to prevent cascade overload
82
+ if (this.isRootTimegroup && this.isFrameUpdateInProgress) {
83
+ // Queue the latest time update - only keep the most recent
84
+ this.queuedTimeUpdate = newTime;
85
+ return;
86
+ }
87
+
88
+ if (this.isRootTimegroup) {
89
+ this.#executeTimeUpdate(newTime);
90
+ } else {
91
+ // Non-root timegroups update immediately (no cascade risk)
92
+ this.#currentTime = newTime;
93
+ this.#saveTimeToLocalStorage(newTime);
84
94
  }
85
95
  }
96
+
86
97
  get currentTime() {
87
98
  return this.#currentTime;
88
99
  }
100
+
89
101
  get currentTimeMs() {
90
102
  return this.currentTime * 1000;
91
103
  }
104
+
92
105
  set currentTimeMs(ms: number) {
93
106
  this.currentTime = ms / 1000;
94
107
  }
95
108
 
109
+ /**
110
+ * Determines if this is a root timegroup (no parent timegroups)
111
+ */
112
+ get isRootTimegroup(): boolean {
113
+ return this.closest("ef-timegroup") === this;
114
+ }
115
+
116
+ /**
117
+ * Executes time update with frame locking for root timegroups
118
+ */
119
+ async #executeTimeUpdate(time: number) {
120
+ this.isFrameUpdateInProgress = true;
121
+ this.#currentTime = time;
122
+
123
+ try {
124
+ // Save to localStorage
125
+ this.#saveTimeToLocalStorage(time);
126
+
127
+ // Wait for any pending frame tasks to complete before allowing next update
128
+ await this.waitForFrameTasks();
129
+ } catch (error) {
130
+ console.error("⚠️ [TIME_UPDATE_ERROR] Error during frame update:", error);
131
+ } finally {
132
+ this.isFrameUpdateInProgress = false;
133
+
134
+ // Process queued update if any (ensures latest scrub position is processed)
135
+ if (this.queuedTimeUpdate !== null && this.queuedTimeUpdate !== time) {
136
+ const nextTime = this.queuedTimeUpdate;
137
+ this.queuedTimeUpdate = null;
138
+ // Schedule on next tick to avoid recursive call stack
139
+ setTimeout(() => this.#executeTimeUpdate(nextTime), 0);
140
+ }
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Saves time to localStorage (extracted for reuse)
146
+ */
147
+ #saveTimeToLocalStorage(time: number) {
148
+ try {
149
+ if (this.id && this.isConnected) {
150
+ localStorage.setItem(this.storageKey, time.toString());
151
+ }
152
+ } catch (error) {
153
+ log("Failed to save time to localStorage", error);
154
+ }
155
+ }
156
+
96
157
  render() {
97
158
  return html`<slot></slot> `;
98
159
  }
@@ -236,9 +297,7 @@ export class EFTimegroup extends EFTemporal(LitElement) {
236
297
  const mediaElements = deepGetMediaElements(this);
237
298
  // Then, we must await the fragmentIndexTask to ensure all media elements have their
238
299
  // fragment index loaded, which is where their duration is parsed from.
239
- await Promise.all(
240
- mediaElements.map((m) => m.fragmentIndexTask.taskComplete),
241
- );
300
+ await Promise.all(mediaElements.map((m) => m.mediaEngineTask.taskComplete));
242
301
 
243
302
  // After waiting for durations, we must force some updates to cascade and ensure all temporal elements
244
303
  // have correct durations and start times. It is not ideal that we have to do this inside here,
@@ -338,16 +397,40 @@ export class EFTimegroup extends EFTemporal(LitElement) {
338
397
  ) {
339
398
  await this.waitForMediaDurations();
340
399
 
400
+ // Create AbortController for audio fetch operations
401
+ const abortController = new AbortController();
402
+
341
403
  await Promise.all(
342
404
  deepGetMediaElements(this).map(async (mediaElement) => {
405
+ // Skip muted elements entirely - no audio fetching or processing needed
406
+ if (mediaElement.mute) {
407
+ return;
408
+ }
409
+
343
410
  const mediaStartsBeforeEnd = mediaElement.startTimeMs <= toMs;
344
411
  const mediaEndsAfterStart = mediaElement.endTimeMs >= fromMs;
345
412
  const mediaOverlaps = mediaStartsBeforeEnd && mediaEndsAfterStart;
346
- if (!mediaOverlaps || mediaElement.defaultAudioTrackId === undefined) {
413
+ if (!mediaOverlaps) {
347
414
  return;
348
415
  }
349
416
 
350
- const audio = await mediaElement.fetchAudioSpanningTime(fromMs, toMs);
417
+ // Convert from root timegroup timeline to media element's local timeline
418
+ const mediaLocalFromMs = Math.max(0, fromMs - mediaElement.startTimeMs);
419
+ const mediaLocalToMs = Math.min(
420
+ mediaElement.endTimeMs - mediaElement.startTimeMs,
421
+ toMs - mediaElement.startTimeMs,
422
+ );
423
+
424
+ // Skip if no valid local time range
425
+ if (mediaLocalFromMs >= mediaLocalToMs) {
426
+ return;
427
+ }
428
+
429
+ const audio = await mediaElement.fetchAudioSpanningTime(
430
+ mediaLocalFromMs, // ✅ Now using media element's local timeline
431
+ mediaLocalToMs, // ✅ Now using media element's local timeline
432
+ abortController.signal,
433
+ );
351
434
  if (!audio) {
352
435
  throw new Error("Failed to fetch audio");
353
436
  }
@@ -358,16 +441,25 @@ export class EFTimegroup extends EFTemporal(LitElement) {
358
441
  );
359
442
  bufferSource.connect(audioContext.destination);
360
443
 
444
+ // Calculate timing for placing this audio in the output context
361
445
  const ctxStartMs = Math.max(0, mediaElement.startTimeMs - fromMs);
362
446
  const ctxEndMs = mediaElement.endTimeMs - fromMs;
363
447
  const ctxDurationMs = ctxEndMs - ctxStartMs;
364
448
 
365
- const offset =
366
- Math.max(0, fromMs - mediaElement.startTimeMs) - audio.startMs;
449
+ // Calculate offset within the fetched audio buffer
450
+ // Since we now use local timeline coordinates, audio.startMs is relative to media start
451
+ const requestedOffsetInMedia = mediaLocalFromMs; // Already in local timeline
452
+ const actualOffsetInBuffer = requestedOffsetInMedia - audio.startMs; // Both in local timeline
453
+
454
+ // Ensure offset is never negative (this would cause audio scheduling errors)
455
+ const safeOffset = Math.max(0, actualOffsetInBuffer);
456
+
457
+ if (safeOffset !== actualOffsetInBuffer) {
458
+ }
367
459
 
368
460
  bufferSource.start(
369
461
  ctxStartMs / 1000,
370
- offset / 1000,
462
+ safeOffset / 1000,
371
463
  ctxDurationMs / 1000,
372
464
  );
373
465
  }),
@@ -375,8 +467,14 @@ export class EFTimegroup extends EFTemporal(LitElement) {
375
467
  }
376
468
 
377
469
  async renderAudio(fromMs: number, toMs: number) {
470
+ // Here we determine the number of samples we need to render rather than the duration.
471
+ // We cannot tolerate having more or fewer samples than fit exactlly into AAC frames.
378
472
  const durationMs = toMs - fromMs;
379
- const contextSize = Math.round((48000 * durationMs) / 1000);
473
+ const duration = durationMs / 1000;
474
+ const exactSamples = 48000 * duration;
475
+ const aacFrames = exactSamples / 1024;
476
+ const alignedFrames = Math.round(aacFrames);
477
+ const contextSize = alignedFrames * 1024; // AAC-aligned sample count
380
478
 
381
479
  // Debug logging for audio duration calculations
382
480
  if (contextSize <= 0) {
@@ -400,6 +498,39 @@ export class EFTimegroup extends EFTemporal(LitElement) {
400
498
  return renderedBuffer;
401
499
  }
402
500
 
501
+ /**
502
+ * TEMPORARY TEST METHOD: Renders audio and immediately plays it back
503
+ * Usage: timegroup.testPlayAudio(0, 5000) // Play first 5 seconds
504
+ */
505
+ async testPlayAudio(fromMs: number, toMs: number) {
506
+ try {
507
+ // Render the audio using the existing renderAudio method
508
+ const renderedBuffer = await this.renderAudio(fromMs, toMs);
509
+
510
+ // Create a regular AudioContext for playback
511
+ const playbackContext = new AudioContext();
512
+
513
+ // Create a buffer source and connect it
514
+ const bufferSource = playbackContext.createBufferSource();
515
+ bufferSource.buffer = renderedBuffer;
516
+ bufferSource.connect(playbackContext.destination);
517
+
518
+ // Start playback immediately
519
+ bufferSource.start(0);
520
+
521
+ // Return a promise that resolves when playback ends
522
+ return new Promise<void>((resolve) => {
523
+ bufferSource.onended = () => {
524
+ playbackContext.close();
525
+ resolve();
526
+ };
527
+ });
528
+ } catch (error) {
529
+ console.error("🎵 [TEST_PLAY_AUDIO] Error:", error);
530
+ throw error;
531
+ }
532
+ }
533
+
403
534
  async loadMd5Sums() {
404
535
  const efElements = this.efElements;
405
536
  const loaderTasks: Promise<any>[] = [];
@@ -1,28 +1,26 @@
1
1
  import { html, render } from "lit";
2
- import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
2
+ import { afterEach, beforeEach, describe, vi } from "vitest";
3
3
  import { assetMSWHandlers } from "../../test/useAssetMSW.js";
4
- import { useMSW } from "../../test/useMSW.js";
4
+ import { test as baseTest } from "../../test/useMSW.js";
5
5
  import type { EFVideo } from "./EFVideo.js";
6
6
  import "./EFVideo.js";
7
7
  import "../gui/EFWorkbench.js";
8
8
  import "../gui/EFPreview.js";
9
9
  import "./EFTimegroup.js";
10
10
 
11
- describe("EFVideo", () => {
12
- const worker = useMSW();
11
+ // Extend the base test with no additional fixtures for EFVideo tests
12
+ const test = baseTest.extend({});
13
13
 
14
+ describe("EFVideo", () => {
14
15
  beforeEach(() => {
15
16
  // Clean up DOM and localStorage
16
17
  while (document.body.children.length) {
17
18
  document.body.children[0]?.remove();
18
19
  }
19
20
  localStorage.clear();
20
-
21
- // Set up centralized MSW handlers to proxy requests to test assets
22
- worker.use(...assetMSWHandlers);
23
21
  });
24
22
 
25
- afterEach(() => {
23
+ afterEach(async () => {
26
24
  // Clean up any remaining elements
27
25
  const videos = document.querySelectorAll("ef-video");
28
26
  for (const video of videos) {
@@ -31,7 +29,7 @@ describe("EFVideo", () => {
31
29
  });
32
30
 
33
31
  describe("basic rendering", () => {
34
- test("should be defined and render canvas", async () => {
32
+ test("should be defined and render canvas", async ({ expect }) => {
35
33
  const element = document.createElement("ef-video");
36
34
  document.body.appendChild(element);
37
35
 
@@ -43,7 +41,7 @@ describe("EFVideo", () => {
43
41
  expect(element.canvasElement?.tagName).toBe("CANVAS");
44
42
  });
45
43
 
46
- test("canvas has correct default properties", async () => {
44
+ test("canvas has correct default properties", async ({ expect }) => {
47
45
  const container = document.createElement("div");
48
46
  render(html`<ef-video></ef-video>`, container);
49
47
  document.body.appendChild(container);
@@ -60,7 +58,7 @@ describe("EFVideo", () => {
60
58
  expect(canvas?.height).toBeGreaterThan(0);
61
59
  });
62
60
 
63
- test("canvas inherits styling correctly", async () => {
61
+ test("canvas inherits styling correctly", async ({ expect }) => {
64
62
  const container = document.createElement("div");
65
63
  render(
66
64
  html`
@@ -86,7 +84,9 @@ describe("EFVideo", () => {
86
84
  });
87
85
 
88
86
  describe("video asset integration", () => {
89
- test("integrates with video asset loading", async () => {
87
+ test("integrates with video asset loading", async ({ expect, worker }) => {
88
+ // Set up MSW handlers for asset loading
89
+ worker.use(...assetMSWHandlers);
90
90
  const container = document.createElement("div");
91
91
  render(
92
92
  html`
@@ -111,7 +111,12 @@ describe("EFVideo", () => {
111
111
  expect(video.intrinsicDurationMs).toBeGreaterThan(0);
112
112
  });
113
113
 
114
- test("handles missing video asset gracefully", async () => {
114
+ test("handles missing video asset gracefully", async ({
115
+ expect,
116
+ worker,
117
+ }) => {
118
+ // Set up MSW handlers for asset loading
119
+ worker.use(...assetMSWHandlers);
115
120
  const container = document.createElement("div");
116
121
  render(
117
122
  html`
@@ -133,7 +138,9 @@ describe("EFVideo", () => {
133
138
  });
134
139
 
135
140
  describe("frame painting and canvas updates", () => {
136
- test("canvas dimensions update when frame dimensions change", async () => {
141
+ test("canvas dimensions update when frame dimensions change", async ({
142
+ expect,
143
+ }) => {
137
144
  const container = document.createElement("div");
138
145
  render(html`<ef-video></ef-video>`, container);
139
146
  document.body.appendChild(container);
@@ -168,7 +175,9 @@ describe("EFVideo", () => {
168
175
  expect(canvas.height).toBe(1080);
169
176
  });
170
177
 
171
- test("handles frame painting with null format gracefully", async () => {
178
+ test("handles frame painting with null format gracefully", async ({
179
+ expect,
180
+ }) => {
172
181
  const container = document.createElement("div");
173
182
  render(html`<ef-video></ef-video>`, container);
174
183
  document.body.appendChild(container);
@@ -200,7 +209,7 @@ describe("EFVideo", () => {
200
209
  }).not.toThrow();
201
210
  });
202
211
 
203
- test("canvas context is available for drawing", async () => {
212
+ test("canvas context is available for drawing", async ({ expect }) => {
204
213
  const container = document.createElement("div");
205
214
  render(html`<ef-video></ef-video>`, container);
206
215
  document.body.appendChild(container);
@@ -225,7 +234,7 @@ describe("EFVideo", () => {
225
234
  });
226
235
 
227
236
  describe("decoder lock scenarios", () => {
228
- test("handles concurrent paint attempts safely", async () => {
237
+ test("handles concurrent paint attempts safely", async ({ expect }) => {
229
238
  const container = document.createElement("div");
230
239
  render(html`<ef-video></ef-video>`, container);
231
240
  document.body.appendChild(container);
@@ -253,7 +262,7 @@ describe("EFVideo", () => {
253
262
  }
254
263
  });
255
264
 
256
- test("paintTask handles missing canvas gracefully", () => {
265
+ test("paintTask handles missing canvas gracefully", ({ expect }) => {
257
266
  const container = document.createElement("div");
258
267
  render(html`<ef-video></ef-video>`, container);
259
268
  document.body.appendChild(container);
@@ -270,7 +279,7 @@ describe("EFVideo", () => {
270
279
  }).not.toThrow();
271
280
  });
272
281
 
273
- test("handles paint task with no video asset", () => {
282
+ test("handles paint task with no video asset", ({ expect }) => {
274
283
  const container = document.createElement("div");
275
284
  render(html`<ef-video></ef-video>`, container);
276
285
  document.body.appendChild(container);
@@ -285,7 +294,7 @@ describe("EFVideo", () => {
285
294
  });
286
295
 
287
296
  describe("frame task integration", () => {
288
- test("frameTask coordinates all required tasks", async () => {
297
+ test("frameTask coordinates all required tasks", async ({ expect }) => {
289
298
  const container = document.createElement("div");
290
299
  render(
291
300
  html`
@@ -305,7 +314,7 @@ describe("EFVideo", () => {
305
314
  }).not.toThrow();
306
315
  });
307
316
 
308
- test("frameTask handles missing dependencies", () => {
317
+ test("frameTask handles missing dependencies", ({ expect }) => {
309
318
  const container = document.createElement("div");
310
319
  render(html`<ef-video></ef-video>`, container);
311
320
  document.body.appendChild(container);
@@ -320,7 +329,7 @@ describe("EFVideo", () => {
320
329
  });
321
330
 
322
331
  describe("error handling and edge cases", () => {
323
- test("handles seek to invalid time", () => {
332
+ test("handles seek to invalid time", ({ expect }) => {
324
333
  const container = document.createElement("div");
325
334
  render(html`<ef-video></ef-video>`, container);
326
335
  document.body.appendChild(container);
@@ -339,7 +348,7 @@ describe("EFVideo", () => {
339
348
  }).not.toThrow();
340
349
  });
341
350
 
342
- test("handles video element removal during playback", () => {
351
+ test("handles video element removal during playback", ({ expect }) => {
343
352
  const container = document.createElement("div");
344
353
  render(html`<ef-video></ef-video>`, container);
345
354
  document.body.appendChild(container);
@@ -358,7 +367,7 @@ describe("EFVideo", () => {
358
367
  }).not.toThrow();
359
368
  });
360
369
 
361
- test("handles canvas context loss gracefully", async () => {
370
+ test("handles canvas context loss gracefully", async ({ expect }) => {
362
371
  const container = document.createElement("div");
363
372
  render(html`<ef-video></ef-video>`, container);
364
373
  document.body.appendChild(container);
@@ -384,8 +393,57 @@ describe("EFVideo", () => {
384
393
  });
385
394
  });
386
395
 
396
+ describe("assetId property", () => {
397
+ test("reads assetId from html source", async ({ expect }) => {
398
+ const container = document.createElement("div");
399
+ container.innerHTML = `<ef-video asset-id="test-video-asset-123"></ef-video>`;
400
+ document.body.appendChild(container);
401
+
402
+ const video = container.querySelector("ef-video") as EFVideo;
403
+ await video.updateComplete;
404
+
405
+ expect(video).toBeDefined();
406
+ expect(video.assetId).toBe("test-video-asset-123");
407
+
408
+ container.remove();
409
+ });
410
+
411
+ test("reads from js property", ({ expect }) => {
412
+ const container = document.createElement("div");
413
+ render(html`<ef-video></ef-video>`, container);
414
+ const video = container.querySelector("ef-video") as EFVideo;
415
+
416
+ video.assetId = "test-video-456";
417
+ expect(video.assetId).toBe("test-video-456");
418
+ });
419
+
420
+ test("reflects property changes to attribute", async ({ expect }) => {
421
+ const container = document.createElement("div");
422
+ render(html`<ef-video></ef-video>`, container);
423
+ document.body.appendChild(container);
424
+
425
+ const video = container.querySelector("ef-video") as EFVideo;
426
+ await video.updateComplete;
427
+
428
+ video.assetId = "test-video-789";
429
+ await video.updateComplete;
430
+ expect(video.getAttribute("asset-id")).toBe("test-video-789");
431
+
432
+ video.assetId = null;
433
+ await video.updateComplete;
434
+ expect(video.hasAttribute("asset-id")).toBe(false);
435
+
436
+ container.remove();
437
+ });
438
+ });
439
+
387
440
  describe("integration with timegroups", () => {
388
- test("integrates correctly within timegroup structure", async () => {
441
+ test("integrates correctly within timegroup structure", async ({
442
+ expect,
443
+ worker,
444
+ }) => {
445
+ // Set up MSW handlers for asset loading
446
+ worker.use(...assetMSWHandlers);
389
447
  const container = document.createElement("div");
390
448
  render(
391
449
  html`
@@ -419,8 +477,10 @@ describe("EFVideo", () => {
419
477
  });
420
478
  });
421
479
 
422
- describe("scrub track integration", () => {
423
- test("should initialize scrub track manager for JIT transcode mode", async () => {
480
+ describe.skip("scrub track integration", () => {
481
+ test("should initialize scrub track manager for JIT transcode mode", async ({
482
+ expect,
483
+ }) => {
424
484
  const container = document.createElement("div");
425
485
  render(
426
486
  html`
@@ -442,7 +502,9 @@ describe("EFVideo", () => {
442
502
  expect(video.scrubTrackManager).toBeDefined();
443
503
  });
444
504
 
445
- test("should not initialize scrub track manager for asset mode", async () => {
505
+ test("should not initialize scrub track manager for asset mode", async ({
506
+ expect,
507
+ }) => {
446
508
  const container = document.createElement("div");
447
509
  render(
448
510
  html`
@@ -462,7 +524,9 @@ describe("EFVideo", () => {
462
524
  expect(video.scrubTrackManager).toBeUndefined();
463
525
  });
464
526
 
465
- test("should expose scrub track performance metrics", async () => {
527
+ test("should expose scrub track performance metrics", async ({
528
+ expect,
529
+ }) => {
466
530
  const container = document.createElement("div");
467
531
  render(
468
532
  html`
@@ -490,7 +554,9 @@ describe("EFVideo", () => {
490
554
  }
491
555
  });
492
556
 
493
- test("should return null stats when no scrub track manager exists", async () => {
557
+ test("should return null stats when no scrub track manager exists", async ({
558
+ expect,
559
+ }) => {
494
560
  const container = document.createElement("div");
495
561
  render(
496
562
  html`
@@ -509,7 +575,7 @@ describe("EFVideo", () => {
509
575
  expect(stats).toBeNull();
510
576
  });
511
577
 
512
- test("should have canvas element available", async () => {
578
+ test("should have canvas element available", async ({ expect }) => {
513
579
  const container = document.createElement("div");
514
580
  render(html`<ef-video></ef-video>`, container);
515
581
  document.body.appendChild(container);
@@ -522,7 +588,9 @@ describe("EFVideo", () => {
522
588
  expect(canvas?.tagName).toBe("CANVAS");
523
589
  });
524
590
 
525
- test("should clean up scrub track manager on disconnect", async () => {
591
+ test("should clean up scrub track manager on disconnect", async ({
592
+ expect,
593
+ }) => {
526
594
  const container = document.createElement("div");
527
595
  render(
528
596
  html`
@@ -555,7 +623,9 @@ describe("EFVideo", () => {
555
623
  });
556
624
 
557
625
  describe("loading indicator", () => {
558
- test("should not show loading indicator for operations completing under 250ms", async () => {
626
+ test("should not show loading indicator for operations completing under 250ms", async ({
627
+ expect,
628
+ }) => {
559
629
  const container = document.createElement("div");
560
630
  render(html`<ef-video></ef-video>`, container);
561
631
  document.body.appendChild(container);
@@ -577,7 +647,9 @@ describe("EFVideo", () => {
577
647
  expect(video.loadingState.isLoading).toBe(false);
578
648
  });
579
649
 
580
- test("should show loading indicator only after 250ms for slow operations", async () => {
650
+ test("should show loading indicator only after 250ms for slow operations", async ({
651
+ expect,
652
+ }) => {
581
653
  const container = document.createElement("div");
582
654
  render(html`<ef-video></ef-video>`, container);
583
655
  document.body.appendChild(container);
@@ -605,7 +677,9 @@ describe("EFVideo", () => {
605
677
  expect(video.loadingState.isLoading).toBe(false);
606
678
  });
607
679
 
608
- test("should handle multiple concurrent loading operations", async () => {
680
+ test("should handle multiple concurrent loading operations", async ({
681
+ expect,
682
+ }) => {
609
683
  const container = document.createElement("div");
610
684
  render(html`<ef-video></ef-video>`, container);
611
685
  document.body.appendChild(container);
@@ -636,7 +710,9 @@ describe("EFVideo", () => {
636
710
  expect(video.loadingState.isLoading).toBe(false);
637
711
  });
638
712
 
639
- test("should not show loading for background operations", async () => {
713
+ test("should not show loading for background operations", async ({
714
+ expect,
715
+ }) => {
640
716
  const container = document.createElement("div");
641
717
  render(html`<ef-video></ef-video>`, container);
642
718
  document.body.appendChild(container);
@@ -659,7 +735,9 @@ describe("EFVideo", () => {
659
735
  video.clearDelayedLoading("bg-op");
660
736
  });
661
737
 
662
- test("should properly clean up loading state on disconnect", async () => {
738
+ test("should properly clean up loading state on disconnect", async ({
739
+ expect,
740
+ }) => {
663
741
  const container = document.createElement("div");
664
742
  render(html`<ef-video></ef-video>`, container);
665
743
  document.body.appendChild(container);