@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,15 +1,37 @@
1
+ import { css } from "lit";
1
2
  import { customElement } from "lit/decorators.js";
2
- import { v4 } from "uuid";
3
- import { afterEach, beforeEach, describe, expect, test } from "vitest";
4
- import { EFMedia } from "./EFMedia.js";
5
- import "../gui/EFWorkbench.js";
3
+ import type { VideoSample } from "mediabunny";
4
+ import { afterEach, beforeEach, describe, vi } from "vitest";
5
+ import { test as baseTest } from "../../test/useMSW.js";
6
+
7
+ import type { EFConfiguration } from "../gui/EFConfiguration.js";
6
8
  import "../gui/EFPreview.js";
9
+ import "../gui/EFWorkbench.js";
10
+ import { JitMediaEngine } from "./EFMedia/JitMediaEngine.js";
11
+ import { EFMedia } from "./EFMedia.js";
7
12
  import "./EFTimegroup.js";
8
- import { assetMSWHandlers } from "../../test/useAssetMSW.js";
9
- import { useMSW } from "../../test/useMSW.js";
13
+ import type { EFTimegroup } from "./EFTimegroup.js";
14
+ import "./EFVideo.js";
15
+ import { UrlGenerator } from "../transcoding/utils/UrlGenerator.js";
16
+ import type { EFVideo } from "./EFVideo.js";
10
17
 
11
18
  @customElement("test-media")
12
- class TestMedia extends EFMedia {}
19
+ class TestMedia extends EFMedia {
20
+ static styles = [
21
+ ...EFMedia.styles,
22
+ css`
23
+ :host {
24
+ display: block;
25
+ width: 100%;
26
+ height: 100%;
27
+ }
28
+ video {
29
+ width: 100%;
30
+ height: 100%;
31
+ }
32
+ `,
33
+ ];
34
+ }
13
35
 
14
36
  declare global {
15
37
  interface HTMLElementTagNameMap {
@@ -17,17 +39,145 @@ declare global {
17
39
  }
18
40
  }
19
41
 
20
- describe("EFMedia", () => {
21
- const worker = useMSW();
42
+ const test = baseTest.extend<{
43
+ timegroup: EFTimegroup;
44
+ jitVideo: EFVideo;
45
+ configuration: EFConfiguration;
46
+ urlGenerator: UrlGenerator;
47
+ host: EFVideo;
48
+ }>({
49
+ timegroup: async ({}, use) => {
50
+ const timegroup = document.createElement("ef-timegroup");
51
+ timegroup.setAttribute("mode", "contain");
52
+ await use(timegroup);
53
+ },
54
+ configuration: async ({ expect }, use) => {
55
+ const configuration = document.createElement("ef-configuration");
56
+ configuration.innerHTML = `<h1 style="font: 10px monospace">${expect.getState().currentTestName}</h1>`;
57
+ // Use integrated proxy server (same host/port as test runner)
58
+ const apiHost = `${window.location.protocol}//${window.location.host}`;
59
+ configuration.setAttribute("api-host", apiHost);
60
+ configuration.apiHost = apiHost;
61
+ document.body.appendChild(configuration);
62
+ await use(configuration);
63
+ // configuration.remove();
64
+ },
65
+ urlGenerator: async ({}, use) => {
66
+ // UrlGenerator points to integrated proxy server (same host/port as test runner)
67
+ const apiHost = `${window.location.protocol}//${window.location.host}`;
68
+ const generator = new UrlGenerator(() => apiHost);
69
+ await use(generator);
70
+ },
71
+ host: async ({ configuration }, use) => {
72
+ const host = document.createElement("ef-video");
73
+ configuration.appendChild(host);
74
+ host.src = "http://web:3000/head-moov-480p.mp4";
75
+ await use(host);
76
+ },
77
+ jitVideo: async ({ configuration, timegroup, host }, use) => {
78
+ timegroup.append(host);
79
+ configuration.append(timegroup);
80
+ await host.mediaEngineTask.run();
81
+ await use(host);
82
+ },
83
+ });
84
+
85
+ describe("JIT Media Engine", () => {
86
+ test("initializes JitMediaEngine", async ({ jitVideo, expect }) => {
87
+ const mediaEngine = jitVideo.mediaEngineTask.value;
88
+ expect(mediaEngine).toBeInstanceOf(JitMediaEngine);
89
+ });
22
90
 
91
+ test("loads media duration", async ({ jitVideo, expect }) => {
92
+ expect(jitVideo.intrinsicDurationMs).toBe(10_000);
93
+ });
94
+
95
+ describe("video seek on load", () => {
96
+ test("seeks to time specified on element", async ({
97
+ configuration,
98
+ expect,
99
+ timegroup,
100
+ }) => {
101
+ const element = document.createElement("ef-video");
102
+ element.src = "http://web:3000/head-moov-480p.mp4";
103
+ timegroup.append(element);
104
+ configuration.append(timegroup);
105
+
106
+ // Initialize media engine first
107
+ await element.mediaEngineTask.run();
108
+ await element.videoSegmentIdTask.run();
109
+
110
+ // Then set the time - this should trigger proper synchronization
111
+ timegroup.currentTimeMs = 2200;
112
+ element.desiredSeekTimeMs = 2200;
113
+
114
+ const sample = await element.videoSeekTask.taskComplete;
115
+
116
+ expect(sample).toBeDefined();
117
+ expect(sample?.timestamp).toEqual(2.2);
118
+ });
119
+ });
120
+
121
+ describe("video seeking", () => {
122
+ test("seeks to 0 seconds and loads first frame", async ({
123
+ timegroup,
124
+ jitVideo,
125
+ expect,
126
+ }) => {
127
+ timegroup.currentTimeMs = 0;
128
+ const frame = await (jitVideo as any).videoSeekTask.taskComplete;
129
+ expect(frame).toBeDefined();
130
+ expect(frame?.timestamp).toEqual(0);
131
+ });
132
+
133
+ test("seeks to 3 seconds and loads frame", async ({
134
+ timegroup,
135
+ jitVideo,
136
+ expect,
137
+ }) => {
138
+ timegroup.currentTimeMs = 3_000;
139
+ jitVideo.desiredSeekTimeMs = 3_000;
140
+ const frame = await (jitVideo as any).videoSeekTask.taskComplete;
141
+ expect(frame).toBeDefined();
142
+ expect(frame?.timestamp).toEqual(3);
143
+ });
144
+
145
+ test("seeks to 5 seconds and loads frame", async ({
146
+ timegroup,
147
+ jitVideo,
148
+ expect,
149
+ }) => {
150
+ timegroup.currentTimeMs = 5_000;
151
+ jitVideo.desiredSeekTimeMs = 5_000;
152
+ const frame = await (jitVideo as any).videoSeekTask.taskComplete;
153
+ expect(frame).toBeDefined();
154
+ expect(frame?.timestamp).toEqual(5);
155
+ });
156
+
157
+ test("seeks ahead in 50ms increments", async ({
158
+ timegroup,
159
+ jitVideo,
160
+ expect,
161
+ }) => {
162
+ timegroup.currentTimeMs = 0;
163
+ let frame: VideoSample | undefined;
164
+ for (let i = 0; i <= 3000; i += 50) {
165
+ timegroup.currentTimeMs = i;
166
+ jitVideo.desiredSeekTimeMs = i;
167
+ frame = await (jitVideo as any).videoSeekTask.taskComplete;
168
+ expect(frame).toBeDefined();
169
+ }
170
+ expect(frame?.timestamp).toEqual(3);
171
+ });
172
+ });
173
+ });
174
+
175
+ describe("EFMedia", () => {
23
176
  beforeEach(() => {
24
177
  // Clean up DOM
25
178
  while (document.body.children.length) {
26
179
  document.body.children[0]?.remove();
27
180
  }
28
-
29
- // Set up MSW handlers to proxy requests to test assets
30
- worker.use(...assetMSWHandlers);
31
181
  });
32
182
 
33
183
  afterEach(() => {
@@ -38,325 +188,568 @@ describe("EFMedia", () => {
38
188
  }
39
189
  });
40
190
 
41
- test("should be defined", () => {
42
- const element = document.createElement("test-media");
191
+ const test = baseTest.extend<{
192
+ element: TestMedia;
193
+ }>({
194
+ element: async ({}, use) => {
195
+ const element = document.createElement("test-media");
196
+ document.body.appendChild(element);
197
+ await use(element);
198
+ element.remove();
199
+ },
200
+ });
201
+
202
+ test("should be defined", ({ element, expect }) => {
43
203
  expect(element.tagName).toBe("TEST-MEDIA");
44
204
  });
45
205
 
46
- describe("mode detection", () => {
47
- test("should detect JIT mode correctly for external URLs", () => {
48
- const element = document.createElement("test-media");
49
- element.src = "http://web:3000/tail-moov-1080p.mp4";
50
- element.mode = "auto";
206
+ describe("mute", () => {
207
+ test("defaults to false", ({ element, expect }) => {
208
+ expect(element.mute).toBe(false);
209
+ });
51
210
 
52
- expect(element.effectiveMode).toBe("jit-transcode");
211
+ test("reads from js property", ({ element, expect }) => {
212
+ element.mute = true;
213
+ expect(element.mute).toBe(true);
53
214
  });
54
215
 
55
- test("should detect asset mode correctly for internal URLs", () => {
56
- const element = document.createElement("test-media");
57
- element.assetId = "test-asset-123";
58
- element.mode = "auto";
216
+ test("reads from dom attribute", ({ element, expect }) => {
217
+ element.setAttribute("mute", "true");
218
+ expect(element.mute).toBe(true);
219
+ });
59
220
 
60
- expect(element.effectiveMode).toBe("asset");
221
+ test("handles any attribute value as true (standard boolean behavior)", ({
222
+ element,
223
+ expect,
224
+ }) => {
225
+ element.setAttribute("mute", "false");
226
+ expect(element.mute).toBe(true); // Standard boolean attributes: any value = true
61
227
  });
62
- });
63
228
 
64
- describe("JIT audio playback", () => {
65
- test.fails(
66
- "audioBufferTask should work in JIT mode without URL errors",
67
- async () => {
68
- const element = document.createElement("test-media");
69
- document.body.appendChild(element);
229
+ test("reflects property changes to attribute", async ({
230
+ element,
231
+ expect,
232
+ }) => {
233
+ element.mute = true;
234
+ await element.updateComplete; // Wait for Lit to update
235
+ expect(element.hasAttribute("mute")).toBe(true);
236
+ expect(element.getAttribute("mute")).toBe(""); // Standard boolean reflection
237
+
238
+ element.mute = false;
239
+ await element.updateComplete; // Wait for Lit to update
240
+ expect(element.hasAttribute("mute")).toBe(false); // Standard boolean reflection removes attribute
241
+ });
70
242
 
71
- // Set up EFMedia in JIT mode
72
- element.src = "http://web:3000/tail-moov-1080p.mp4";
73
- element.mode = "jit-transcode";
74
- element.currentTimeMs = 1000; // Seek to 1 second
243
+ describe("audio rendering", () => {
244
+ // Create a separate test context for audio rendering tests that need configuration
245
+ const audioTest = baseTest.extend<{
246
+ timegroup: EFTimegroup;
247
+ configuration: EFConfiguration;
248
+ }>({
249
+ timegroup: async ({}, use) => {
250
+ const timegroup = document.createElement("ef-timegroup");
251
+ timegroup.setAttribute("mode", "contain");
252
+ await use(timegroup);
253
+ },
254
+ configuration: async ({ expect }, use) => {
255
+ const configuration = document.createElement("ef-configuration");
256
+ configuration.innerHTML = `<h1 style="font: 10px monospace">${expect.getState().currentTestName}</h1>`;
257
+ // Use integrated proxy server (same host/port as test runner)
258
+ const apiHost = `${window.location.protocol}//${window.location.host}`;
259
+ configuration.setAttribute("api-host", apiHost);
260
+ configuration.apiHost = apiHost;
261
+ document.body.appendChild(configuration);
262
+ await use(configuration);
263
+ // configuration.remove();
264
+ },
265
+ });
266
+
267
+ audioTest(
268
+ "skips muted elements during audio rendering",
269
+ async ({ configuration, timegroup, expect }) => {
270
+ // Create a muted media element
271
+ const mutedElement = document.createElement("test-media");
272
+ mutedElement.src = "http://web:3000/head-moov-480p.mp4";
273
+ mutedElement.mute = true;
274
+ timegroup.append(mutedElement);
275
+
276
+ // Create an unmuted media element
277
+ const unmutedElement = document.createElement("test-media");
278
+ unmutedElement.src = "http://web:3000/head-moov-480p.mp4";
279
+ unmutedElement.mute = false;
280
+ timegroup.append(unmutedElement);
281
+
282
+ configuration.append(timegroup);
283
+
284
+ // Wait for media engines to initialize
285
+ await mutedElement.mediaEngineTask.run();
286
+ await unmutedElement.mediaEngineTask.run();
287
+
288
+ // Spy on fetchAudioSpanningTime to verify muted element is skipped
289
+ const mutedFetchSpy = vi.spyOn(
290
+ mutedElement,
291
+ "fetchAudioSpanningTime",
292
+ );
293
+ const unmutedFetchSpy = vi.spyOn(
294
+ unmutedElement,
295
+ "fetchAudioSpanningTime",
296
+ );
297
+
298
+ // Render a short audio segment
299
+ try {
300
+ await timegroup.renderAudio(0, 1000); // 1 second
301
+ } catch (error) {
302
+ // Audio rendering might fail in test environment, but we're testing the mute logic
303
+ console.log("Audio rendering failed (expected in test):", error);
304
+ }
305
+
306
+ // Verify muted element was skipped (no fetch calls)
307
+ expect(mutedFetchSpy).not.toHaveBeenCalled();
308
+
309
+ // Verify unmuted element was processed (would have fetch calls if audio succeeds)
310
+ // Note: In test environment, this might still be 0 due to audio context limitations
311
+ // but the important thing is that muted element definitely wasn't called
312
+ const mutedCalls = mutedFetchSpy.mock.calls.length;
313
+ const unmutedCalls = unmutedFetchSpy.mock.calls.length;
314
+
315
+ expect(mutedCalls).toBe(0);
316
+ // Unmuted element should either be called (audio works) or both fail equally
317
+ // The key test is that muted=0 and muted < unmuted (if audio works)
318
+ expect(mutedCalls).toBeLessThanOrEqual(unmutedCalls);
319
+
320
+ mutedFetchSpy.mockRestore();
321
+ unmutedFetchSpy.mockRestore();
322
+ },
323
+ );
75
324
 
76
- // This test should fail because audioBufferTask doesn't handle JIT mode properly
77
- // It will try to decode audio from JIT segments which have a different structure
78
- await element.audioBufferTask.taskComplete;
325
+ audioTest(
326
+ "processes unmuted elements normally",
327
+ async ({ configuration, timegroup, expect }) => {
328
+ // Create an unmuted media element
329
+ const element = document.createElement("test-media");
330
+ element.src = "http://web:3000/head-moov-480p.mp4";
331
+ element.mute = false;
332
+ timegroup.append(element);
79
333
 
80
- const audioBuffer = element.audioBufferTask.value;
81
- expect(audioBuffer).toBeDefined();
82
- expect(audioBuffer?.buffer).toBeDefined();
83
- expect(audioBuffer?.startOffsetMs).toBeGreaterThanOrEqual(0);
84
- },
85
- );
334
+ configuration.append(timegroup);
86
335
 
87
- test.skip("fetchAudioSpanningTime should work in JIT mode without URL errors", async () => {
88
- const element = document.createElement("test-media");
89
- document.body.appendChild(element);
336
+ await element.mediaEngineTask.run();
90
337
 
91
- // Set up EFMedia in JIT mode
92
- element.src = "http://web:3000/tail-moov-1080p.mp4";
93
- element.mode = "jit-transcode";
338
+ const fetchSpy = vi.spyOn(element, "fetchAudioSpanningTime");
94
339
 
95
- // Wait for JIT metadata to load first
96
- await element.jitMetadataLoader.taskComplete;
340
+ try {
341
+ await timegroup.renderAudio(0, 1000);
342
+ } catch (error) {
343
+ // Audio rendering might fail in test environment
344
+ console.log("Audio rendering failed (expected in test):", error);
345
+ }
97
346
 
98
- // Wait for fragment index to be created from JIT metadata
99
- await element.fragmentIndexTask.taskComplete;
347
+ // The element should not have been skipped due to mute
348
+ // (whether it actually gets called depends on test environment audio support)
349
+ expect(element.mute).toBe(false);
100
350
 
101
- // This test now passes because the bug has been fixed
102
- const audioData = await element.fetchAudioSpanningTime(0, 2000);
351
+ fetchSpy.mockRestore();
352
+ },
353
+ );
103
354
 
104
- // If we get here without an error, the bug is fixed
105
- expect(audioData).toBeDefined();
106
- expect(audioData?.blob).toBeDefined();
107
- expect(audioData?.startMs).toBeGreaterThanOrEqual(0);
108
- expect(audioData?.endMs).toBeGreaterThan(audioData!.startMs);
109
- });
355
+ audioTest(
356
+ "handles dynamic mute changes",
357
+ async ({ configuration, timegroup, expect }) => {
358
+ const element = document.createElement("test-media");
359
+ element.src = "http://web:3000/head-moov-480p.mp4";
360
+ element.mute = false; // Start unmuted
361
+ timegroup.append(element);
110
362
 
111
- test("should handle audio properly in asset mode (baseline)", async () => {
112
- const element = document.createElement("test-media");
113
- const preview = document.createElement("ef-preview");
114
- preview.appendChild(element);
115
- document.body.appendChild(preview);
363
+ configuration.append(timegroup);
116
364
 
117
- // Set up EFMedia in asset mode
118
- element.assetId = "test-asset-123";
119
- element.mode = "asset";
120
- element.src = "/test-assets/media/bars-n-tone2.mp4";
121
-
122
- // Wait for fragment index to load
123
- await new Promise((resolve) => setTimeout(resolve, 300));
124
-
125
- // Asset mode should work fine (this is our baseline)
126
- try {
127
- const audioData = await element.fetchAudioSpanningTime(0, 2000);
128
- expect(audioData).toBeDefined();
129
- } catch (error) {
130
- // Audio data might not be available in test environment, but should not throw URL errors
131
- expect(String(error)).not.toMatch(/URL/);
132
- }
133
- });
134
- });
365
+ await element.mediaEngineTask.run();
135
366
 
136
- describe("when rendering", () => {
137
- beforeEach(() => {
138
- // @ts-expect-error
139
- window.FRAMEGEN_BRIDGE = true;
140
- });
141
- afterEach(() => {
142
- delete window.FRAMEGEN_BRIDGE;
143
- });
367
+ const fetchSpy = vi.spyOn(element, "fetchAudioSpanningTime");
144
368
 
145
- test("fragmentIndexPath uses http:// protocol", () => {
146
- const workbench = document.createElement("ef-workbench");
147
- const element = document.createElement("test-media");
148
- workbench.appendChild(element);
149
- element.assetId = "550e8400-e29b-41d4-a716-446655440000:example.mp4";
150
- expect(element.fragmentIndexPath()).toBe(
151
- "https://editframe.dev/api/v1/isobmff_files/550e8400-e29b-41d4-a716-446655440000:example.mp4/index",
152
- );
153
- });
369
+ // First render - unmuted
370
+ try {
371
+ await timegroup.renderAudio(0, 500);
372
+ } catch (error) {
373
+ console.log("Audio rendering failed (expected in test):", error);
374
+ }
154
375
 
155
- test("fragmentTrackPath uses http:// protocol", () => {
156
- const workbench = document.createElement("ef-workbench");
157
- const element = document.createElement("test-media");
158
- workbench.appendChild(element);
159
- element.assetId = "550e8400-e29b-41d4-a716-446655440000:example.mp4";
160
- expect(element.fragmentTrackPath("1")).toBe(
161
- "https://editframe.dev/api/v1/isobmff_tracks/550e8400-e29b-41d4-a716-446655440000:example.mp4/1",
162
- );
163
- });
164
- });
376
+ const firstCallCount = fetchSpy.mock.calls.length;
165
377
 
166
- describe("attribute: asset-id", () => {
167
- test("determines fragmentIndexPath", () => {
168
- const id = v4();
169
- const element = document.createElement("test-media");
170
- element.setAttribute("asset-id", id);
171
- expect(element.fragmentIndexPath()).toBe(
172
- `https://editframe.dev/api/v1/isobmff_files/${id}/index`,
173
- );
174
- });
378
+ // Mute the element
379
+ element.mute = true;
380
+ await element.updateComplete;
175
381
 
176
- test("determines fragmentTrackPath", () => {
177
- const id = v4();
178
- const element = document.createElement("test-media");
179
- element.setAttribute("asset-id", id);
180
- expect(element.fragmentTrackPath("1")).toBe(
181
- `https://editframe.dev/api/v1/isobmff_tracks/${id}/1`,
182
- );
183
- });
382
+ // Second render - muted (should be skipped)
383
+ try {
384
+ await timegroup.renderAudio(500, 1000);
385
+ } catch (error) {
386
+ console.log("Audio rendering failed (expected in test):", error);
387
+ }
184
388
 
185
- test("honors apiHost in fragmentIndexPath", () => {
186
- const id = v4();
187
- const element = document.createElement("test-media");
188
- element.setAttribute("asset-id", id);
189
- const preview = document.createElement("ef-preview");
190
- preview.appendChild(element);
191
- preview.apiHost = "test://";
192
- document.body.appendChild(preview);
193
- expect(element.fragmentIndexPath()).toBe(
194
- `test:///api/v1/isobmff_files/${id}/index`,
195
- );
196
- });
389
+ const secondCallCount = fetchSpy.mock.calls.length;
197
390
 
198
- test("honors apiHost in fragmentTrackPath", () => {
199
- const id = v4();
200
- const element = document.createElement("test-media");
201
- element.setAttribute("asset-id", id);
202
- const preview = document.createElement("ef-preview");
203
- preview.appendChild(element);
204
- preview.apiHost = "test://";
205
- document.body.appendChild(preview);
206
- expect(element.fragmentTrackPath("1")).toBe(
207
- `test:///api/v1/isobmff_tracks/${id}/1`,
391
+ // Verify no additional calls were made when muted
392
+ expect(secondCallCount).toBe(firstCallCount);
393
+
394
+ fetchSpy.mockRestore();
395
+ },
208
396
  );
209
397
  });
210
398
  });
211
399
 
212
- describe("calculating duration", () => {
213
- test("Computes duration from track fragment index", async () => {
214
- const element = document.createElement("test-media");
215
- element.src = "/test-assets/media/bars-n-tone2.mp4";
216
- element.mode = "asset";
217
-
218
- const preview = document.createElement("ef-preview");
219
- preview.appendChild(element);
220
- document.body.appendChild(preview);
221
-
222
- // Wait for fragment index to load
223
- await new Promise((resolve) => setTimeout(resolve, 300));
224
-
225
- // The media should have loaded successfully and have a duration > 0
226
- // We don't test for specific duration since real assets may vary
227
- expect(element.intrinsicDurationMs).toBeGreaterThan(0);
228
- expect(element.durationMs).toBeGreaterThan(0);
400
+ describe("audio analysis", () => {
401
+ const audioAnalysisTest = baseTest.extend<{
402
+ timegroup: EFTimegroup;
403
+ configuration: EFConfiguration;
404
+ }>({
405
+ timegroup: async ({}, use) => {
406
+ const timegroup = document.createElement("ef-timegroup");
407
+ timegroup.setAttribute("mode", "contain");
408
+ await use(timegroup);
409
+ },
410
+ configuration: async ({ expect }, use) => {
411
+ const configuration = document.createElement("ef-configuration");
412
+ configuration.innerHTML = `<h1 style="font: 10px monospace">${expect.getState().currentTestName}</h1>`;
413
+ // Use integrated proxy server (same host/port as test runner)
414
+ const apiHost = `${window.location.protocol}//${window.location.host}`;
415
+ configuration.setAttribute("api-host", apiHost);
416
+ configuration.apiHost = apiHost;
417
+ document.body.appendChild(configuration);
418
+ await use(configuration);
419
+ },
229
420
  });
230
421
 
231
- test("Computes duration from track fragment index sourcein", async () => {
232
- const timegroup = document.createElement("ef-timegroup");
233
- timegroup.mode = "sequence";
234
- const element = document.createElement("test-media");
235
- element.src = "/assets/10s-bars.mp4";
236
- element.sourceInMs = 1_000;
422
+ audioAnalysisTest(
423
+ "has time domain analysis task",
424
+ async ({ configuration, timegroup, expect }) => {
425
+ const element = document.createElement("test-media");
426
+ element.src = "http://web:3000/head-moov-480p.mp4";
427
+ timegroup.append(element);
428
+ configuration.append(timegroup);
237
429
 
238
- const preview = document.createElement("ef-preview");
239
- timegroup.appendChild(element);
240
- preview.appendChild(timegroup);
241
- document.body.appendChild(preview);
430
+ await element.mediaEngineTask.run();
242
431
 
243
- // Await the next tick to ensure the element has a chance to load the track fragment
244
- await Promise.resolve();
432
+ expect(element.byteTimeDomainTask).toBeDefined();
433
+ expect(typeof element.byteTimeDomainTask.taskComplete).toBe("object");
434
+ },
435
+ );
245
436
 
246
- await element.fragmentIndexTask.taskComplete;
437
+ audioAnalysisTest(
438
+ "has frequency analysis task",
439
+ async ({ configuration, timegroup, expect }) => {
440
+ const element = document.createElement("test-media");
441
+ element.src = "http://web:3000/head-moov-480p.mp4";
442
+ timegroup.append(element);
443
+ configuration.append(timegroup);
247
444
 
248
- // Use tolerance for real asset durations (should be close to expected)
249
- expect(element.durationMs).toBeGreaterThan(8500);
250
- expect(element.durationMs).toBeLessThan(9500);
251
- expect(timegroup.durationMs).toBeGreaterThan(8500);
252
- expect(timegroup.durationMs).toBeLessThan(9500);
253
- });
445
+ await element.mediaEngineTask.run();
254
446
 
255
- test("Computes duration from track fragment index sourcein", async () => {
256
- const timegroup = document.createElement("ef-timegroup");
257
- timegroup.mode = "sequence";
258
- const element = document.createElement("test-media");
259
- element.src = "/assets/10s-bars.mp4";
260
- element.sourceInMs = 6_000;
447
+ expect(element.frequencyDataTask).toBeDefined();
448
+ expect(typeof element.frequencyDataTask.taskComplete).toBe("object");
449
+ },
450
+ );
261
451
 
262
- const preview = document.createElement("ef-preview");
263
- timegroup.appendChild(element);
264
- preview.appendChild(timegroup);
265
- document.body.appendChild(preview);
452
+ audioAnalysisTest(
453
+ "respects FFT configuration properties",
454
+ async ({ configuration, timegroup, expect }) => {
455
+ const element = document.createElement("test-media");
456
+ element.src = "http://web:3000/head-moov-480p.mp4";
457
+ element.fftSize = 256;
458
+ element.fftDecay = 4;
459
+ element.fftGain = 2.0;
460
+ element.interpolateFrequencies = true;
461
+ timegroup.append(element);
462
+ configuration.append(timegroup);
463
+
464
+ await element.mediaEngineTask.run();
465
+
466
+ expect(element.fftSize).toBe(256);
467
+ expect(element.fftDecay).toBe(4);
468
+ expect(element.fftGain).toBe(2.0);
469
+ expect(element.interpolateFrequencies).toBe(true);
470
+ expect(element.shouldInterpolateFrequencies).toBe(true);
471
+ },
472
+ );
266
473
 
267
- // Await the next tick to ensure the element has a chance to load the track fragment
268
- await Promise.resolve();
474
+ audioAnalysisTest(
475
+ "generates FREQ_WEIGHTS based on fftSize",
476
+ async ({ configuration, timegroup, expect }) => {
477
+ const element = document.createElement("test-media");
478
+ element.src = "http://web:3000/head-moov-480p.mp4";
479
+ element.fftSize = 128;
480
+ timegroup.append(element);
481
+ configuration.append(timegroup);
482
+
483
+ await element.mediaEngineTask.run();
484
+
485
+ const weights = element.FREQ_WEIGHTS;
486
+ expect(weights).toBeInstanceOf(Float32Array);
487
+ expect(weights.length).toBe(element.fftSize / 2); // 64 for fftSize 128
488
+
489
+ // Test frequency weighting - lower frequencies should have lower weights
490
+ expect(weights.length).toBeGreaterThan(0);
491
+ const firstWeight = weights[0];
492
+ const lastWeight = weights[weights.length - 1];
493
+ expect(firstWeight).toBeDefined();
494
+ expect(lastWeight).toBeDefined();
495
+ expect(firstWeight!).toBeLessThan(lastWeight!);
496
+ },
497
+ );
498
+ });
269
499
 
270
- await element.fragmentIndexTask.taskComplete;
500
+ describe("assetId", () => {
501
+ test("reads from js property", ({ element, expect }) => {
502
+ element.assetId = "test-asset-123";
503
+ expect(element.assetId).toBe("test-asset-123");
504
+ });
271
505
 
272
- expect(element.durationMs).toBeCloseTo(4085, 0);
273
- expect(timegroup.durationMs).toBeCloseTo(4085, 0);
506
+ test("reads from dom attribute", ({ element, expect }) => {
507
+ element.setAttribute("asset-id", "test-asset-123");
508
+ expect(element.assetId).toBe("test-asset-123");
274
509
  });
275
510
 
276
- test("Computes duration from track fragment index sourceout", async () => {
277
- const timegroup = document.createElement("ef-timegroup");
278
- timegroup.mode = "sequence";
279
- const element = document.createElement("test-media");
280
- element.src = "/assets/10s-bars.mp4";
281
- element.sourceOutMs = 6_000;
511
+ test("defaults to null", ({ element, expect }) => {
512
+ expect(element.assetId).toBe(null);
513
+ });
282
514
 
283
- const preview = document.createElement("ef-preview");
284
- timegroup.appendChild(element);
285
- preview.appendChild(timegroup);
286
- document.body.appendChild(preview);
515
+ test("reflects property changes to attribute", async ({
516
+ element,
517
+ expect,
518
+ }) => {
519
+ element.assetId = "test-asset-456";
520
+ await element.updateComplete;
521
+ expect(element.getAttribute("asset-id")).toBe("test-asset-456");
522
+
523
+ element.assetId = null;
524
+ await element.updateComplete;
525
+ expect(element.hasAttribute("asset-id")).toBe(false);
526
+ });
287
527
 
288
- // Await the next tick to ensure the element has a chance to load the track fragment
289
- await Promise.resolve();
528
+ test("reads assetId from html source", async ({ expect }) => {
529
+ const container = document.createElement("div");
530
+ container.innerHTML = `<test-media asset-id="test-asset-789"></test-media>`;
531
+ const media = container.querySelector("test-media") as TestMedia;
532
+ expect(media).toBeDefined();
533
+ expect(media.assetId).toBe("test-asset-789");
534
+ });
535
+ });
290
536
 
291
- await element.fragmentIndexTask.taskComplete;
537
+ describe("fftSize", () => {
538
+ test("defaults to 128", ({ element, expect }) => {
539
+ expect(element.fftSize).toBe(128);
540
+ });
292
541
 
293
- expect(element.durationMs).toBeCloseTo(6_000, 0);
294
- expect(timegroup.durationMs).toBeCloseTo(6_000, 0);
542
+ test("reads from js property", ({ element, expect }) => {
543
+ element.fftSize = 1024;
544
+ expect(element.fftSize).toBe(1024);
295
545
  });
296
546
 
297
- test("Computes duration from track fragment index sourceout", async () => {
298
- const timegroup = document.createElement("ef-timegroup");
299
- timegroup.mode = "sequence";
300
- const element = document.createElement("test-media");
301
- element.src = "/assets/10s-bars.mp4";
302
- element.sourceOutMs = 5_000;
547
+ test("reads from dom attribute", ({ element, expect }) => {
548
+ element.setAttribute("fft-size", "1024");
549
+ expect(element.fftSize).toBe(1024);
550
+ });
303
551
 
304
- const preview = document.createElement("ef-preview");
305
- timegroup.appendChild(element);
306
- preview.appendChild(timegroup);
307
- document.body.appendChild(preview);
552
+ test("reflects property changes to attribute", async ({
553
+ element,
554
+ expect,
555
+ }) => {
556
+ element.fftSize = 512;
557
+ await element.updateComplete;
558
+ expect(element.getAttribute("fft-size")).toBe("512");
559
+ });
560
+ });
308
561
 
309
- // Await the next tick to ensure the element has a chance to load the track fragment
310
- await Promise.resolve();
562
+ describe("fftDecay", () => {
563
+ test("defaults to 8", ({ element, expect }) => {
564
+ expect(element.fftDecay).toBe(8);
565
+ });
311
566
 
312
- await element.fragmentIndexTask.taskComplete;
567
+ test("reads from js property", ({ element, expect }) => {
568
+ element.fftDecay = 16;
569
+ expect(element.fftDecay).toBe(16);
570
+ });
313
571
 
314
- expect(element.durationMs).toBeCloseTo(5_000, 0);
315
- expect(timegroup.durationMs).toBeCloseTo(5_000, 0);
572
+ test("reads from dom attribute", ({ element, expect }) => {
573
+ element.setAttribute("fft-decay", "16");
574
+ expect(element.fftDecay).toBe(16);
316
575
  });
317
576
 
318
- test("Computes duration from track fragment index sourceout and sourcein", async () => {
319
- const timegroup = document.createElement("ef-timegroup");
320
- timegroup.mode = "sequence";
321
- const element = document.createElement("test-media");
322
- element.src = "/assets/10s-bars.mp4";
323
- element.sourceInMs = 1_000;
324
- element.sourceOutMs = 5_000;
577
+ test("reflects property changes to attribute", async ({
578
+ element,
579
+ expect,
580
+ }) => {
581
+ element.fftDecay = 32;
582
+ await element.updateComplete;
583
+ expect(element.getAttribute("fft-decay")).toBe("32");
584
+ });
585
+ });
325
586
 
326
- const preview = document.createElement("ef-preview");
327
- timegroup.appendChild(element);
328
- preview.appendChild(timegroup);
329
- document.body.appendChild(preview);
587
+ describe("fftGain", () => {
588
+ test("defaults to 3.0", ({ element, expect }) => {
589
+ expect(element.fftGain).toBe(3.0);
590
+ });
330
591
 
331
- // Await the next tick to ensure the element has a chance to load the track fragment
332
- await Promise.resolve();
592
+ test("reads from js property", ({ element, expect }) => {
593
+ element.fftGain = 0.5;
594
+ expect(element.fftGain).toBe(0.5);
595
+ });
333
596
 
334
- await element.fragmentIndexTask.taskComplete;
597
+ test("reads from dom attribute", ({ element, expect }) => {
598
+ element.setAttribute("fft-gain", "0.5");
599
+ expect(element.fftGain).toBe(0.5);
600
+ });
335
601
 
336
- expect(element.durationMs).toBeCloseTo(4_000, 0);
337
- expect(timegroup.durationMs).toBeCloseTo(4_000, 0);
602
+ test("reflects property changes to attribute", async ({
603
+ element,
604
+ expect,
605
+ }) => {
606
+ element.fftGain = 2.5;
607
+ await element.updateComplete;
608
+ expect(element.getAttribute("fft-gain")).toBe("2.5");
338
609
  });
610
+ });
339
611
 
340
- test("Computes duration from track fragment index sourceout and sourcein", async () => {
341
- const timegroup = document.createElement("ef-timegroup");
342
- timegroup.mode = "sequence";
343
- const element = document.createElement("test-media");
344
- element.src = "/assets/10s-bars.mp4";
345
- element.sourceInMs = 9_000;
346
- element.sourceOutMs = 10_000;
612
+ describe("interpolateFrequencies", () => {
613
+ test("defaults to false", ({ element, expect }) => {
614
+ expect(element.interpolateFrequencies).toBe(false);
615
+ });
347
616
 
348
- const preview = document.createElement("ef-preview");
349
- timegroup.appendChild(element);
350
- preview.appendChild(timegroup);
351
- document.body.appendChild(preview);
617
+ test("reads from js property", ({ element, expect }) => {
618
+ element.interpolateFrequencies = true;
619
+ expect(element.interpolateFrequencies).toBe(true);
620
+ });
352
621
 
353
- // Await the next tick to ensure the element has a chance to load the track fragment
354
- await Promise.resolve();
622
+ test("reads from dom attribute", ({ element, expect }) => {
623
+ element.setAttribute("interpolate-frequencies", "true");
624
+ expect(element.interpolateFrequencies).toBe(true);
625
+ });
355
626
 
356
- await element.fragmentIndexTask.taskComplete;
627
+ test("handles any attribute value as true (standard boolean behavior)", ({
628
+ element,
629
+ expect,
630
+ }) => {
631
+ element.setAttribute("interpolate-frequencies", "false");
632
+ expect(element.interpolateFrequencies).toBe(true); // Standard boolean attributes: any value = true
633
+ });
357
634
 
358
- expect(element.durationMs).toBeCloseTo(1_000, 0);
359
- expect(timegroup.durationMs).toBeCloseTo(1_000, 0);
635
+ test("reflects property changes to attribute", async ({
636
+ element,
637
+ expect,
638
+ }) => {
639
+ element.interpolateFrequencies = true;
640
+ await element.updateComplete;
641
+ expect(element.hasAttribute("interpolate-frequencies")).toBe(true);
642
+ expect(element.getAttribute("interpolate-frequencies")).toBe(""); // Standard boolean reflection
643
+
644
+ element.interpolateFrequencies = false;
645
+ await element.updateComplete;
646
+ expect(element.hasAttribute("interpolate-frequencies")).toBe(false); // Standard boolean reflection removes attribute
360
647
  });
361
648
  });
649
+
650
+ // describe("mediaEngineTask", () => {
651
+ // test("is defined", ({ element, expect }) => {
652
+ // expect(element.mediaEngineTask).toBeDefined();
653
+ // });
654
+
655
+ // test("is a task", ({ element, expect }) => {
656
+ // expect(element.mediaEngineTask).toBeInstanceOf(Task);
657
+ // });
658
+
659
+ // test("throws if assetId is set", async ({ element, expect }) => {
660
+ // element.assetId = "test-asset-123";
661
+ // await element.mediaEngineTask.run();
662
+ // expect(element.mediaEngineTask.error).toBeInstanceOf(Error);
663
+ // });
664
+
665
+ // test("creates JitMediaEngine for http sources", async ({
666
+ // elementWithJitManifest,
667
+ // expect,
668
+ // worker,
669
+ // }) => {
670
+ // await elementWithJitManifest.mediaEngineTask.run();
671
+ // expect(elementWithJitManifest.mediaEngineTask.value).toBeInstanceOf(
672
+ // JitMediaEngine,
673
+ // );
674
+ // });
675
+
676
+ // test("creates AssetMediaEngine for local sources", async ({
677
+ // elementWithAsset,
678
+ // expect,
679
+ // }) => {
680
+ // await elementWithAsset.mediaEngineTask.run();
681
+ // expect(elementWithAsset.mediaEngineTask.value).toBeInstanceOf(
682
+ // AssetMediaEngine,
683
+ // );
684
+ // });
685
+ // });
686
+
687
+ // describe("Video Buffering Integration", () => {
688
+ // test("videoBufferTask is available and configured", ({
689
+ // element,
690
+ // expect,
691
+ // }) => {
692
+ // expect(element.videoBufferTask).toBeDefined();
693
+ // expect(element.videoBufferDurationMs).toBe(60000); // 60 seconds default
694
+ // expect(element.maxVideoBufferFetches).toBe(2); // 2 parallel fetches default
695
+ // expect(element.enableVideoBuffering).toBe(true); // enabled by default
696
+ // });
697
+
698
+ // test("buffer configuration can be customized", ({ element, expect }) => {
699
+ // element.videoBufferDurationMs = 45000;
700
+ // element.maxVideoBufferFetches = 3;
701
+ // element.enableVideoBuffering = false;
702
+
703
+ // expect(element.videoBufferDurationMs).toBe(45000);
704
+ // expect(element.maxVideoBufferFetches).toBe(3);
705
+ // expect(element.enableVideoBuffering).toBe(false);
706
+ // });
707
+
708
+ // test("buffer task starts automatically with JIT asset", async ({
709
+ // elementWithJitManifest,
710
+ // expect,
711
+ // }) => {
712
+ // const element = elementWithJitManifest;
713
+
714
+ // // Wait for media engine to initialize
715
+ // await element.mediaEngineTask.taskComplete;
716
+
717
+ // // Buffer task should be available and have started
718
+ // expect(element.videoBufferTask).toBeDefined();
719
+ // // Task status should be INITIAL (0) or higher, indicating it's been created
720
+ // expect(element.videoBufferTask.status).toBeGreaterThanOrEqual(0);
721
+ // });
722
+ // });
723
+ // });
724
+
725
+ // // Test to verify buffer tasks use EFMedia properties directly (no hardcoded config duplication)
726
+ // describe("Buffer Task Property Integration", () => {
727
+ // test("audio and video buffer tasks use EFMedia properties directly", async ({
728
+ // element,
729
+ // expect,
730
+ // }) => {
731
+ // // Set custom buffer configuration on the element
732
+ // element.audioBufferDurationMs = 15000;
733
+ // element.maxAudioBufferFetches = 3;
734
+ // element.enableAudioBuffering = false;
735
+
736
+ // element.videoBufferDurationMs = 45000;
737
+ // element.maxVideoBufferFetches = 5;
738
+ // element.enableVideoBuffering = false;
739
+
740
+ // // Verify the tasks are created without requiring hardcoded config
741
+ // expect(element.audioBufferTask).toBeDefined();
742
+ // expect(element.videoBufferTask).toBeDefined();
743
+
744
+ // // The task configuration should now come directly from element properties
745
+ // // This test ensures no hardcoded config duplication exists
746
+ // expect(element.audioBufferDurationMs).toBe(15000);
747
+ // expect(element.maxAudioBufferFetches).toBe(3);
748
+ // expect(element.enableAudioBuffering).toBe(false);
749
+
750
+ // expect(element.videoBufferDurationMs).toBe(45000);
751
+ // expect(element.maxVideoBufferFetches).toBe(5);
752
+ // expect(element.enableVideoBuffering).toBe(false);
753
+ // });
754
+ // });
362
755
  });