@editframe/elements 0.16.8-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 (267) hide show
  1. package/README.md +30 -0
  2. package/dist/DecoderResetFrequency.test.d.ts +1 -0
  3. package/dist/DecoderResetRecovery.test.d.ts +1 -0
  4. package/dist/DelayedLoadingState.d.ts +48 -0
  5. package/dist/DelayedLoadingState.integration.test.d.ts +1 -0
  6. package/dist/DelayedLoadingState.js +113 -0
  7. package/dist/DelayedLoadingState.test.d.ts +1 -0
  8. package/dist/EF_FRAMEGEN.d.ts +10 -1
  9. package/dist/EF_FRAMEGEN.js +199 -179
  10. package/dist/EF_INTERACTIVE.js +2 -6
  11. package/dist/EF_RENDERING.js +1 -3
  12. package/dist/LoadingDebounce.test.d.ts +1 -0
  13. package/dist/LoadingIndicator.browsertest.d.ts +0 -0
  14. package/dist/ManualScrubTest.test.d.ts +1 -0
  15. package/dist/ScrubResolvedFlashing.test.d.ts +1 -0
  16. package/dist/ScrubTrackManager.d.ts +96 -0
  17. package/dist/ScrubTrackManager.test.d.ts +1 -0
  18. package/dist/VideoSeekFlashing.browsertest.d.ts +0 -0
  19. package/dist/VideoStuckDiagnostic.test.d.ts +1 -0
  20. package/dist/elements/CrossUpdateController.js +13 -15
  21. package/dist/elements/EFAudio.browsertest.d.ts +0 -0
  22. package/dist/elements/EFAudio.d.ts +22 -3
  23. package/dist/elements/EFAudio.js +60 -43
  24. package/dist/elements/EFCaptions.js +337 -373
  25. package/dist/elements/EFImage.d.ts +1 -0
  26. package/dist/elements/EFImage.js +73 -91
  27. package/dist/elements/EFMedia/AssetIdMediaEngine.d.ts +18 -0
  28. package/dist/elements/EFMedia/AssetIdMediaEngine.js +41 -0
  29. package/dist/elements/EFMedia/AssetIdMediaEngine.test.d.ts +1 -0
  30. package/dist/elements/EFMedia/AssetMediaEngine.d.ts +47 -0
  31. package/dist/elements/EFMedia/AssetMediaEngine.js +116 -0
  32. package/dist/elements/EFMedia/BaseMediaEngine.d.ts +55 -0
  33. package/dist/elements/EFMedia/BaseMediaEngine.js +96 -0
  34. package/dist/elements/EFMedia/BaseMediaEngine.test.d.ts +1 -0
  35. package/dist/elements/EFMedia/BufferedSeekingInput.browsertest.d.ts +1 -0
  36. package/dist/elements/EFMedia/BufferedSeekingInput.d.ts +43 -0
  37. package/dist/elements/EFMedia/BufferedSeekingInput.js +159 -0
  38. package/dist/elements/EFMedia/JitMediaEngine.browsertest.d.ts +0 -0
  39. package/dist/elements/EFMedia/JitMediaEngine.d.ts +31 -0
  40. package/dist/elements/EFMedia/JitMediaEngine.js +62 -0
  41. package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.browsertest.d.ts +9 -0
  42. package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.d.ts +16 -0
  43. package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.js +48 -0
  44. package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.d.ts +3 -0
  45. package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.js +138 -0
  46. package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.browsertest.d.ts +9 -0
  47. package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.d.ts +4 -0
  48. package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.js +16 -0
  49. package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.browsertest.d.ts +9 -0
  50. package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.d.ts +3 -0
  51. package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.js +22 -0
  52. package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.d.ts +7 -0
  53. package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.js +24 -0
  54. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.d.ts +4 -0
  55. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.js +18 -0
  56. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.d.ts +4 -0
  57. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.js +16 -0
  58. package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.d.ts +3 -0
  59. package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.js +104 -0
  60. package/dist/elements/EFMedia/services/AudioElementFactory.browsertest.d.ts +1 -0
  61. package/dist/elements/EFMedia/services/AudioElementFactory.d.ts +22 -0
  62. package/dist/elements/EFMedia/services/AudioElementFactory.js +72 -0
  63. package/dist/elements/EFMedia/services/MediaSourceService.browsertest.d.ts +1 -0
  64. package/dist/elements/EFMedia/services/MediaSourceService.d.ts +47 -0
  65. package/dist/elements/EFMedia/services/MediaSourceService.js +73 -0
  66. package/dist/elements/EFMedia/shared/AudioSpanUtils.d.ts +7 -0
  67. package/dist/elements/EFMedia/shared/AudioSpanUtils.js +54 -0
  68. package/dist/elements/EFMedia/shared/BufferUtils.d.ts +70 -0
  69. package/dist/elements/EFMedia/shared/BufferUtils.js +89 -0
  70. package/dist/elements/EFMedia/shared/MediaTaskUtils.d.ts +23 -0
  71. package/dist/elements/EFMedia/shared/RenditionHelpers.browsertest.d.ts +1 -0
  72. package/dist/elements/EFMedia/shared/RenditionHelpers.d.ts +19 -0
  73. package/dist/elements/EFMedia/tasks/makeMediaEngineTask.browsertest.d.ts +1 -0
  74. package/dist/elements/EFMedia/tasks/makeMediaEngineTask.d.ts +18 -0
  75. package/dist/elements/EFMedia/tasks/makeMediaEngineTask.js +60 -0
  76. package/dist/elements/EFMedia/tasks/makeMediaEngineTask.test.d.ts +1 -0
  77. package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.browsertest.d.ts +9 -0
  78. package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.d.ts +16 -0
  79. package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.js +46 -0
  80. package/dist/elements/EFMedia/videoTasks/makeVideoInitSegmentFetchTask.browsertest.d.ts +9 -0
  81. package/dist/elements/EFMedia/videoTasks/makeVideoInitSegmentFetchTask.d.ts +4 -0
  82. package/dist/elements/EFMedia/videoTasks/makeVideoInitSegmentFetchTask.js +16 -0
  83. package/dist/elements/EFMedia/videoTasks/makeVideoInputTask.browsertest.d.ts +9 -0
  84. package/dist/elements/EFMedia/videoTasks/makeVideoInputTask.d.ts +3 -0
  85. package/dist/elements/EFMedia/videoTasks/makeVideoInputTask.js +27 -0
  86. package/dist/elements/EFMedia/videoTasks/makeVideoSeekTask.d.ts +7 -0
  87. package/dist/elements/EFMedia/videoTasks/makeVideoSeekTask.js +25 -0
  88. package/dist/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.browsertest.d.ts +9 -0
  89. package/dist/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.d.ts +4 -0
  90. package/dist/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.js +18 -0
  91. package/dist/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.browsertest.d.ts +9 -0
  92. package/dist/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.d.ts +4 -0
  93. package/dist/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.js +16 -0
  94. package/dist/elements/EFMedia.browsertest.d.ts +1 -0
  95. package/dist/elements/EFMedia.d.ts +95 -66
  96. package/dist/elements/EFMedia.js +204 -683
  97. package/dist/elements/EFSourceMixin.js +31 -48
  98. package/dist/elements/EFTemporal.d.ts +2 -1
  99. package/dist/elements/EFTemporal.js +266 -360
  100. package/dist/elements/EFTimegroup.d.ts +14 -1
  101. package/dist/elements/EFTimegroup.js +337 -323
  102. package/dist/elements/EFVideo.browsertest.d.ts +0 -0
  103. package/dist/elements/EFVideo.d.ts +123 -4
  104. package/dist/elements/EFVideo.js +308 -111
  105. package/dist/elements/EFWaveform.js +375 -411
  106. package/dist/elements/FetchMixin.js +14 -24
  107. package/dist/elements/MediaController.d.ts +30 -0
  108. package/dist/elements/SampleBuffer.d.ts +14 -0
  109. package/dist/elements/SampleBuffer.js +52 -0
  110. package/dist/elements/TargetController.js +130 -156
  111. package/dist/elements/TimegroupController.js +17 -19
  112. package/dist/elements/durationConverter.js +15 -4
  113. package/dist/elements/parseTimeToMs.js +4 -10
  114. package/dist/elements/printTaskStatus.d.ts +2 -0
  115. package/dist/elements/updateAnimations.js +39 -59
  116. package/dist/getRenderInfo.d.ts +2 -2
  117. package/dist/getRenderInfo.js +59 -67
  118. package/dist/gui/ContextMixin.js +150 -288
  119. package/dist/gui/EFConfiguration.js +27 -43
  120. package/dist/gui/EFFilmstrip.d.ts +3 -3
  121. package/dist/gui/EFFilmstrip.js +440 -620
  122. package/dist/gui/EFFitScale.d.ts +2 -2
  123. package/dist/gui/EFFitScale.js +112 -135
  124. package/dist/gui/EFFocusOverlay.js +45 -61
  125. package/dist/gui/EFPreview.js +30 -49
  126. package/dist/gui/EFScrubber.js +78 -99
  127. package/dist/gui/EFTimeDisplay.js +49 -70
  128. package/dist/gui/EFToggleLoop.js +17 -34
  129. package/dist/gui/EFTogglePlay.js +37 -58
  130. package/dist/gui/EFWorkbench.js +66 -88
  131. package/dist/gui/TWMixin.js +2 -48
  132. package/dist/gui/TWMixin2.js +31 -0
  133. package/dist/gui/efContext.js +2 -6
  134. package/dist/gui/fetchContext.js +1 -3
  135. package/dist/gui/focusContext.js +1 -3
  136. package/dist/gui/focusedElementContext.js +2 -6
  137. package/dist/gui/playingContext.js +1 -4
  138. package/dist/gui/services/ElementConnectionManager.browsertest.d.ts +1 -0
  139. package/dist/gui/services/ElementConnectionManager.d.ts +59 -0
  140. package/dist/gui/services/ElementConnectionManager.js +128 -0
  141. package/dist/gui/services/PlaybackController.browsertest.d.ts +1 -0
  142. package/dist/gui/services/PlaybackController.d.ts +103 -0
  143. package/dist/gui/services/PlaybackController.js +290 -0
  144. package/dist/index.js +5 -30
  145. package/dist/msToTimeCode.js +11 -13
  146. package/dist/services/MediaSourceManager.d.ts +62 -0
  147. package/dist/services/MediaSourceManager.js +211 -0
  148. package/dist/style.css +2 -1
  149. package/dist/transcoding/cache/CacheManager.d.ts +73 -0
  150. package/dist/transcoding/cache/RequestDeduplicator.d.ts +29 -0
  151. package/dist/transcoding/cache/RequestDeduplicator.js +53 -0
  152. package/dist/transcoding/cache/RequestDeduplicator.test.d.ts +1 -0
  153. package/dist/transcoding/types/index.d.ts +242 -0
  154. package/dist/transcoding/utils/MediaUtils.d.ts +9 -0
  155. package/dist/transcoding/utils/UrlGenerator.d.ts +26 -0
  156. package/dist/transcoding/utils/UrlGenerator.js +45 -0
  157. package/dist/transcoding/utils/constants.d.ts +27 -0
  158. package/dist/utils/LRUCache.d.ts +34 -0
  159. package/dist/utils/LRUCache.js +115 -0
  160. package/package.json +4 -3
  161. package/src/elements/EFAudio.browsertest.ts +709 -0
  162. package/src/elements/EFAudio.ts +59 -15
  163. package/src/elements/EFCaptions.browsertest.ts +0 -1
  164. package/src/elements/EFImage.browsertest.ts +42 -1
  165. package/src/elements/EFImage.ts +23 -3
  166. package/src/elements/EFMedia/AssetIdMediaEngine.test.ts +222 -0
  167. package/src/elements/EFMedia/AssetIdMediaEngine.ts +70 -0
  168. package/src/elements/EFMedia/AssetMediaEngine.ts +210 -0
  169. package/src/elements/EFMedia/BaseMediaEngine.test.ts +164 -0
  170. package/src/elements/EFMedia/BaseMediaEngine.ts +170 -0
  171. package/src/elements/EFMedia/BufferedSeekingInput.browsertest.ts +400 -0
  172. package/src/elements/EFMedia/BufferedSeekingInput.ts +267 -0
  173. package/src/elements/EFMedia/JitMediaEngine.browsertest.ts +165 -0
  174. package/src/elements/EFMedia/JitMediaEngine.ts +110 -0
  175. package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.browsertest.ts +554 -0
  176. package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.ts +81 -0
  177. package/src/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.ts +241 -0
  178. package/src/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.browsertest.ts +59 -0
  179. package/src/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.ts +23 -0
  180. package/src/elements/EFMedia/audioTasks/makeAudioInputTask.browsertest.ts +55 -0
  181. package/src/elements/EFMedia/audioTasks/makeAudioInputTask.ts +35 -0
  182. package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.ts +42 -0
  183. package/src/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.ts +34 -0
  184. package/src/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.ts +23 -0
  185. package/src/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.ts +174 -0
  186. package/src/elements/EFMedia/services/AudioElementFactory.browsertest.ts +325 -0
  187. package/src/elements/EFMedia/services/AudioElementFactory.ts +119 -0
  188. package/src/elements/EFMedia/services/MediaSourceService.browsertest.ts +257 -0
  189. package/src/elements/EFMedia/services/MediaSourceService.ts +102 -0
  190. package/src/elements/EFMedia/shared/AudioSpanUtils.ts +128 -0
  191. package/src/elements/EFMedia/shared/BufferUtils.ts +310 -0
  192. package/src/elements/EFMedia/shared/MediaTaskUtils.ts +44 -0
  193. package/src/elements/EFMedia/shared/RenditionHelpers.browsertest.ts +247 -0
  194. package/src/elements/EFMedia/shared/RenditionHelpers.ts +79 -0
  195. package/src/elements/EFMedia/tasks/makeMediaEngineTask.browsertest.ts +128 -0
  196. package/src/elements/EFMedia/tasks/makeMediaEngineTask.test.ts +233 -0
  197. package/src/elements/EFMedia/tasks/makeMediaEngineTask.ts +89 -0
  198. package/src/elements/EFMedia/videoTasks/makeVideoBufferTask.browsertest.ts +555 -0
  199. package/src/elements/EFMedia/videoTasks/makeVideoBufferTask.ts +79 -0
  200. package/src/elements/EFMedia/videoTasks/makeVideoInitSegmentFetchTask.browsertest.ts +59 -0
  201. package/src/elements/EFMedia/videoTasks/makeVideoInitSegmentFetchTask.ts +23 -0
  202. package/src/elements/EFMedia/videoTasks/makeVideoInputTask.browsertest.ts +55 -0
  203. package/src/elements/EFMedia/videoTasks/makeVideoInputTask.ts +45 -0
  204. package/src/elements/EFMedia/videoTasks/makeVideoSeekTask.ts +44 -0
  205. package/src/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.browsertest.ts +57 -0
  206. package/src/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.ts +32 -0
  207. package/src/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.browsertest.ts +56 -0
  208. package/src/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.ts +23 -0
  209. package/src/elements/EFMedia.browsertest.ts +696 -271
  210. package/src/elements/EFMedia.ts +218 -776
  211. package/src/elements/EFTemporal.browsertest.ts +0 -1
  212. package/src/elements/EFTemporal.ts +13 -3
  213. package/src/elements/EFTimegroup.browsertest.ts +6 -3
  214. package/src/elements/EFTimegroup.ts +221 -27
  215. package/src/elements/EFVideo.browsertest.ts +758 -0
  216. package/src/elements/EFVideo.ts +418 -68
  217. package/src/elements/EFWaveform.ts +5 -5
  218. package/src/elements/MediaController.ts +98 -0
  219. package/src/elements/SampleBuffer.ts +97 -0
  220. package/src/elements/printTaskStatus.ts +16 -0
  221. package/src/elements/updateAnimations.ts +6 -0
  222. package/src/gui/ContextMixin.ts +23 -104
  223. package/src/gui/TWMixin.ts +10 -3
  224. package/src/gui/services/ElementConnectionManager.browsertest.ts +263 -0
  225. package/src/gui/services/ElementConnectionManager.ts +224 -0
  226. package/src/gui/services/PlaybackController.browsertest.ts +437 -0
  227. package/src/gui/services/PlaybackController.ts +521 -0
  228. package/src/services/MediaSourceManager.ts +333 -0
  229. package/src/transcoding/cache/CacheManager.ts +208 -0
  230. package/src/transcoding/cache/RequestDeduplicator.test.ts +170 -0
  231. package/src/transcoding/cache/RequestDeduplicator.ts +65 -0
  232. package/src/transcoding/types/index.ts +265 -0
  233. package/src/transcoding/utils/MediaUtils.ts +63 -0
  234. package/src/transcoding/utils/UrlGenerator.ts +68 -0
  235. package/src/transcoding/utils/constants.ts +36 -0
  236. package/src/utils/LRUCache.ts +153 -0
  237. package/test/EFVideo.framegen.browsertest.ts +127 -0
  238. 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
  239. 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
  240. 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
  241. 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
  242. 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
  243. 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
  244. 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
  245. 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
  246. 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
  247. 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
  248. 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
  249. 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
  250. 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
  251. 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
  252. 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
  253. 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
  254. 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
  255. 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
  256. 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
  257. 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
  258. package/test/__cache__/GET__api_v1_transcode_manifest_json_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__3be92a0437de726b431ed5af2369158a/data.bin +1 -0
  259. package/test/__cache__/GET__api_v1_transcode_manifest_json_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__3be92a0437de726b431ed5af2369158a/metadata.json +19 -0
  260. package/test/createJitTestClips.ts +425 -0
  261. package/test/recordReplayProxyPlugin.js +302 -0
  262. package/test/useAssetMSW.ts +49 -0
  263. package/test/useMSW.ts +44 -0
  264. package/types.json +1 -1
  265. package/dist/gui/TWMixin.css.js +0 -4
  266. /package/dist/elements/{TargetController.test.d.ts → TargetController.browsertest.d.ts} +0 -0
  267. /package/src/elements/{TargetController.test.ts → TargetController.browsertest.ts} +0 -0
@@ -1,16 +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 { createTestFragmentIndex } from "TEST/createTestFragmentIndex.js";
9
- import { useMockWorker } from "TEST/useMockWorker.js";
10
- import { http, HttpResponse } from "msw";
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";
11
17
 
12
18
  @customElement("test-media")
13
- 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
+ }
14
35
 
15
36
  declare global {
16
37
  interface HTMLElementTagNameMap {
@@ -18,313 +39,717 @@ declare global {
18
39
  }
19
40
  }
20
41
 
21
- describe("EFMedia", () => {
22
- const worker = useMockWorker();
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
+ });
23
84
 
24
- test("should be defined", () => {
25
- const element = document.createElement("test-media");
26
- expect(element.tagName).toBe("TEST-MEDIA");
85
+ describe("JIT Media Engine", () => {
86
+ test("initializes JitMediaEngine", async ({ jitVideo, expect }) => {
87
+ const mediaEngine = jitVideo.mediaEngineTask.value;
88
+ expect(mediaEngine).toBeInstanceOf(JitMediaEngine);
89
+ });
90
+
91
+ test("loads media duration", async ({ jitVideo, expect }) => {
92
+ expect(jitVideo.intrinsicDurationMs).toBe(10_000);
27
93
  });
28
94
 
29
- describe("when rendering", () => {
30
- beforeEach(() => {
31
- // @ts-ignore
32
- window.FRAMEGEN_BRIDGE = true;
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);
33
118
  });
34
- afterEach(() => {
35
- // @ts-ignore
36
- // biome-ignore lint/performance/noDelete: <explanation>
37
- delete window.FRAMEGEN_BRIDGE;
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);
38
131
  });
39
- test("fragmentIndexPath uses http:// protocol", () => {
40
- const workbench = document.createElement("ef-workbench");
41
- const element = document.createElement("test-media");
42
- workbench.appendChild(element);
43
- element.assetId = "550e8400-e29b-41d4-a716-446655440000:example.mp4";
44
- expect(element.fragmentIndexPath()).toBe(
45
- "https://editframe.dev/api/v1/isobmff_files/550e8400-e29b-41d4-a716-446655440000:example.mp4/index",
46
- );
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);
47
143
  });
48
144
 
49
- test("fragmentTrackPath uses http:// protocol", () => {
50
- const workbench = document.createElement("ef-workbench");
51
- const element = document.createElement("test-media");
52
- workbench.appendChild(element);
53
- element.assetId = "550e8400-e29b-41d4-a716-446655440000:example.mp4";
54
- expect(element.fragmentTrackPath("1")).toBe(
55
- "https://editframe.dev/api/v1/isobmff_tracks/550e8400-e29b-41d4-a716-446655440000:example.mp4/1",
56
- );
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);
57
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", () => {
176
+ beforeEach(() => {
177
+ // Clean up DOM
178
+ while (document.body.children.length) {
179
+ document.body.children[0]?.remove();
180
+ }
58
181
  });
59
182
 
60
- describe("attribute: asset-id", () => {
61
- test("determines fragmentIndexPath", () => {
62
- const id = v4();
183
+ afterEach(() => {
184
+ // Clean up any remaining elements
185
+ const elements = document.querySelectorAll("test-media");
186
+ for (const element of elements) {
187
+ element.remove();
188
+ }
189
+ });
190
+
191
+ const test = baseTest.extend<{
192
+ element: TestMedia;
193
+ }>({
194
+ element: async ({}, use) => {
63
195
  const element = document.createElement("test-media");
64
- element.setAttribute("asset-id", id);
65
- expect(element.fragmentIndexPath()).toBe(
66
- `https://editframe.dev/api/v1/isobmff_files/${id}/index`,
67
- );
196
+ document.body.appendChild(element);
197
+ await use(element);
198
+ element.remove();
199
+ },
200
+ });
201
+
202
+ test("should be defined", ({ element, expect }) => {
203
+ expect(element.tagName).toBe("TEST-MEDIA");
204
+ });
205
+
206
+ describe("mute", () => {
207
+ test("defaults to false", ({ element, expect }) => {
208
+ expect(element.mute).toBe(false);
68
209
  });
69
210
 
70
- test("determines fragmentTrackPath", () => {
71
- const id = v4();
72
- const element = document.createElement("test-media");
73
- element.setAttribute("asset-id", id);
74
- expect(element.fragmentTrackPath("1")).toBe(
75
- `https://editframe.dev/api/v1/isobmff_tracks/${id}/1`,
76
- );
211
+ test("reads from js property", ({ element, expect }) => {
212
+ element.mute = true;
213
+ expect(element.mute).toBe(true);
77
214
  });
78
215
 
79
- test("honors apiHost in fragmentIndexPath", () => {
80
- const id = v4();
81
- const element = document.createElement("test-media");
82
- element.setAttribute("asset-id", id);
83
- const preview = document.createElement("ef-preview");
84
- preview.appendChild(element);
85
- preview.apiHost = "test://";
86
- document.body.appendChild(preview);
87
- expect(element.fragmentIndexPath()).toBe(
88
- `test:///api/v1/isobmff_files/${id}/index`,
89
- );
216
+ test("reads from dom attribute", ({ element, expect }) => {
217
+ element.setAttribute("mute", "true");
218
+ expect(element.mute).toBe(true);
90
219
  });
91
220
 
92
- test("honors apiHost in fragmentTrackPath", () => {
93
- const id = v4();
94
- const element = document.createElement("test-media");
95
- element.setAttribute("asset-id", id);
96
- const preview = document.createElement("ef-preview");
97
- preview.appendChild(element);
98
- preview.apiHost = "test://";
99
- document.body.appendChild(preview);
100
- expect(element.fragmentTrackPath("1")).toBe(
101
- `test:///api/v1/isobmff_tracks/${id}/1`,
102
- );
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
103
227
  });
104
- });
105
228
 
106
- describe("calculating duration", () => {
107
- test("Computes duration from track fragment index", async () => {
108
- // Mock the request for the track fragment index, responds with a 10 second duration
109
- worker.use(
110
- http.get("/@ef-track-fragment-index//assets/10s-bars.mp4", () => {
111
- return HttpResponse.json(
112
- createTestFragmentIndex({
113
- audio: { duration: 10_000 },
114
- video: { duration: 10_000 },
115
- }),
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
+ });
242
+
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",
116
296
  );
117
- }),
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
+ },
118
323
  );
119
324
 
120
- const element = document.createElement("test-media");
121
- element.src = "/assets/10s-bars.mp4";
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);
122
333
 
123
- const preview = document.createElement("ef-preview");
124
- preview.appendChild(element);
125
- document.body.appendChild(preview);
334
+ configuration.append(timegroup);
126
335
 
127
- // Await the next tick to ensure the element has a chance to load the track fragment
128
- await Promise.resolve();
336
+ await element.mediaEngineTask.run();
129
337
 
130
- await element.trackFragmentIndexLoader.taskComplete;
338
+ const fetchSpy = vi.spyOn(element, "fetchAudioSpanningTime");
131
339
 
132
- expect(element.durationMs).toBe(10_000);
133
- });
134
- test("Computes duration from track fragment index sourcein", async () => {
135
- // Mock the request for the track fragment index, responds with a 10 second duration
136
- worker.use(
137
- http.get("/@ef-track-fragment-index//assets/10s-bars.mp4", () => {
138
- return HttpResponse.json(
139
- createTestFragmentIndex({
140
- audio: { duration: 10_000 },
141
- video: { duration: 10_000 },
142
- }),
143
- );
144
- }),
145
- );
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
+ }
146
346
 
147
- const timegroup = document.createElement("ef-timegroup");
148
- timegroup.mode = "sequence";
149
- const element = document.createElement("test-media");
150
- element.src = "/assets/10s-bars.mp4";
151
- element.sourceInMs = 1_000;
152
-
153
- const preview = document.createElement("ef-preview");
154
- timegroup.appendChild(element);
155
- preview.appendChild(timegroup);
156
- document.body.appendChild(preview);
157
-
158
- // Await the next tick to ensure the element has a chance to load the track fragment
159
- await Promise.resolve();
160
-
161
- await element.trackFragmentIndexLoader.taskComplete;
162
-
163
- expect(element.durationMs).toBe(9_000);
164
- expect(timegroup.durationMs).toBe(9_000);
165
- });
166
- test("Computes duration from track fragment index sourcein", async () => {
167
- // Mock the request for the track fragment index, responds with a 10 second duration
168
- worker.use(
169
- http.get("/@ef-track-fragment-index//assets/10s-bars.mp4", () => {
170
- return HttpResponse.json(
171
- createTestFragmentIndex({
172
- audio: { duration: 10_000 },
173
- video: { duration: 10_000 },
174
- }),
175
- );
176
- }),
177
- );
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);
178
350
 
179
- const timegroup = document.createElement("ef-timegroup");
180
- timegroup.mode = "sequence";
181
- const element = document.createElement("test-media");
182
- element.src = "/assets/10s-bars.mp4";
183
- element.sourceInMs = 6_000;
184
-
185
- const preview = document.createElement("ef-preview");
186
- timegroup.appendChild(element);
187
- preview.appendChild(timegroup);
188
- document.body.appendChild(preview);
189
-
190
- // Await the next tick to ensure the element has a chance to load the track fragment
191
- await Promise.resolve();
192
-
193
- await element.trackFragmentIndexLoader.taskComplete;
194
-
195
- expect(element.durationMs).toBe(4_000);
196
- expect(timegroup.durationMs).toBe(4_000);
197
- });
198
- test("Computes duration from track fragment index sourceout", async () => {
199
- // Mock the request for the track fragment index, responds with a 10 second duration
200
- worker.use(
201
- http.get("/@ef-track-fragment-index//assets/10s-bars.mp4", () => {
202
- return HttpResponse.json(
203
- createTestFragmentIndex({
204
- audio: { duration: 10_000 },
205
- video: { duration: 10_000 },
206
- }),
207
- );
208
- }),
351
+ fetchSpy.mockRestore();
352
+ },
209
353
  );
210
354
 
211
- const timegroup = document.createElement("ef-timegroup");
212
- timegroup.mode = "sequence";
213
- const element = document.createElement("test-media");
214
- element.src = "/assets/10s-bars.mp4";
215
- element.sourceOutMs = 6_000;
216
-
217
- const preview = document.createElement("ef-preview");
218
- timegroup.appendChild(element);
219
- preview.appendChild(timegroup);
220
- document.body.appendChild(preview);
221
-
222
- // Await the next tick to ensure the element has a chance to load the track fragment
223
- await Promise.resolve();
224
-
225
- await element.trackFragmentIndexLoader.taskComplete;
226
-
227
- expect(element.durationMs).toBe(6_000);
228
- expect(timegroup.durationMs).toBe(6_000);
229
- });
230
- test("Computes duration from track fragment index sourceout", async () => {
231
- // Mock the request for the track fragment index, responds with a 10 second duration
232
- worker.use(
233
- http.get("/@ef-track-fragment-index//assets/10s-bars.mp4", () => {
234
- return HttpResponse.json(
235
- createTestFragmentIndex({
236
- audio: { duration: 10_000 },
237
- video: { duration: 10_000 },
238
- }),
239
- );
240
- }),
241
- );
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);
242
362
 
243
- const timegroup = document.createElement("ef-timegroup");
244
- timegroup.mode = "sequence";
245
- const element = document.createElement("test-media");
246
- element.src = "/assets/10s-bars.mp4";
247
- element.sourceOutMs = 5_000;
248
-
249
- const preview = document.createElement("ef-preview");
250
- timegroup.appendChild(element);
251
- preview.appendChild(timegroup);
252
- document.body.appendChild(preview);
253
-
254
- // Await the next tick to ensure the element has a chance to load the track fragment
255
- await Promise.resolve();
256
-
257
- await element.trackFragmentIndexLoader.taskComplete;
258
-
259
- expect(element.durationMs).toBe(5_000);
260
- expect(timegroup.durationMs).toBe(5_000);
261
- });
262
- test("Computes duration from track fragment index sourceout and sourcein", async () => {
263
- // Mock the request for the track fragment index, responds with a 10 second duration
264
- worker.use(
265
- http.get("/@ef-track-fragment-index//assets/10s-bars.mp4", () => {
266
- return HttpResponse.json(
267
- createTestFragmentIndex({
268
- audio: { duration: 10_000 },
269
- video: { duration: 10_000 },
270
- }),
271
- );
272
- }),
363
+ configuration.append(timegroup);
364
+
365
+ await element.mediaEngineTask.run();
366
+
367
+ const fetchSpy = vi.spyOn(element, "fetchAudioSpanningTime");
368
+
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
+ }
375
+
376
+ const firstCallCount = fetchSpy.mock.calls.length;
377
+
378
+ // Mute the element
379
+ element.mute = true;
380
+ await element.updateComplete;
381
+
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
+ }
388
+
389
+ const secondCallCount = fetchSpy.mock.calls.length;
390
+
391
+ // Verify no additional calls were made when muted
392
+ expect(secondCallCount).toBe(firstCallCount);
393
+
394
+ fetchSpy.mockRestore();
395
+ },
273
396
  );
397
+ });
398
+ });
274
399
 
275
- const timegroup = document.createElement("ef-timegroup");
276
- timegroup.mode = "sequence";
277
- const element = document.createElement("test-media");
278
- element.src = "/assets/10s-bars.mp4";
279
- element.sourceInMs = 1_000;
280
- element.sourceOutMs = 5_000;
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
+ },
420
+ });
281
421
 
282
- const preview = document.createElement("ef-preview");
283
- timegroup.appendChild(element);
284
- preview.appendChild(timegroup);
285
- document.body.appendChild(preview);
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);
429
+
430
+ await element.mediaEngineTask.run();
431
+
432
+ expect(element.byteTimeDomainTask).toBeDefined();
433
+ expect(typeof element.byteTimeDomainTask.taskComplete).toBe("object");
434
+ },
435
+ );
436
+
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);
444
+
445
+ await element.mediaEngineTask.run();
446
+
447
+ expect(element.frequencyDataTask).toBeDefined();
448
+ expect(typeof element.frequencyDataTask.taskComplete).toBe("object");
449
+ },
450
+ );
451
+
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
+ );
473
+
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
+ });
286
499
 
287
- // Await the next tick to ensure the element has a chance to load the track fragment
288
- await Promise.resolve();
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
+ });
289
505
 
290
- await element.trackFragmentIndexLoader.taskComplete;
506
+ test("reads from dom attribute", ({ element, expect }) => {
507
+ element.setAttribute("asset-id", "test-asset-123");
508
+ expect(element.assetId).toBe("test-asset-123");
509
+ });
291
510
 
292
- expect(element.durationMs).toBe(4_000);
293
- expect(timegroup.durationMs).toBe(4_000);
511
+ test("defaults to null", ({ element, expect }) => {
512
+ expect(element.assetId).toBe(null);
294
513
  });
295
514
 
296
- test("Computes duration from track fragment index sourceout and sourcein", async () => {
297
- // Mock the request for the track fragment index, responds with a 10 second duration
298
- worker.use(
299
- http.get("/@ef-track-fragment-index//assets/10s-bars.mp4", () => {
300
- return HttpResponse.json(
301
- createTestFragmentIndex({
302
- audio: { duration: 10_000 },
303
- video: { duration: 10_000 },
304
- }),
305
- );
306
- }),
307
- );
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
+ });
308
527
 
309
- const timegroup = document.createElement("ef-timegroup");
310
- timegroup.mode = "sequence";
311
- const element = document.createElement("test-media");
312
- element.src = "/assets/10s-bars.mp4";
313
- element.sourceInMs = 9_000;
314
- element.sourceOutMs = 10_000;
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
+ });
536
+
537
+ describe("fftSize", () => {
538
+ test("defaults to 128", ({ element, expect }) => {
539
+ expect(element.fftSize).toBe(128);
540
+ });
541
+
542
+ test("reads from js property", ({ element, expect }) => {
543
+ element.fftSize = 1024;
544
+ expect(element.fftSize).toBe(1024);
545
+ });
546
+
547
+ test("reads from dom attribute", ({ element, expect }) => {
548
+ element.setAttribute("fft-size", "1024");
549
+ expect(element.fftSize).toBe(1024);
550
+ });
551
+
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
+ });
561
+
562
+ describe("fftDecay", () => {
563
+ test("defaults to 8", ({ element, expect }) => {
564
+ expect(element.fftDecay).toBe(8);
565
+ });
566
+
567
+ test("reads from js property", ({ element, expect }) => {
568
+ element.fftDecay = 16;
569
+ expect(element.fftDecay).toBe(16);
570
+ });
571
+
572
+ test("reads from dom attribute", ({ element, expect }) => {
573
+ element.setAttribute("fft-decay", "16");
574
+ expect(element.fftDecay).toBe(16);
575
+ });
576
+
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
+ });
586
+
587
+ describe("fftGain", () => {
588
+ test("defaults to 3.0", ({ element, expect }) => {
589
+ expect(element.fftGain).toBe(3.0);
590
+ });
591
+
592
+ test("reads from js property", ({ element, expect }) => {
593
+ element.fftGain = 0.5;
594
+ expect(element.fftGain).toBe(0.5);
595
+ });
596
+
597
+ test("reads from dom attribute", ({ element, expect }) => {
598
+ element.setAttribute("fft-gain", "0.5");
599
+ expect(element.fftGain).toBe(0.5);
600
+ });
601
+
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");
609
+ });
610
+ });
315
611
 
316
- const preview = document.createElement("ef-preview");
317
- timegroup.appendChild(element);
318
- preview.appendChild(timegroup);
319
- document.body.appendChild(preview);
612
+ describe("interpolateFrequencies", () => {
613
+ test("defaults to false", ({ element, expect }) => {
614
+ expect(element.interpolateFrequencies).toBe(false);
615
+ });
320
616
 
321
- // Await the next tick to ensure the element has a chance to load the track fragment
322
- await Promise.resolve();
617
+ test("reads from js property", ({ element, expect }) => {
618
+ element.interpolateFrequencies = true;
619
+ expect(element.interpolateFrequencies).toBe(true);
620
+ });
323
621
 
324
- await element.trackFragmentIndexLoader.taskComplete;
622
+ test("reads from dom attribute", ({ element, expect }) => {
623
+ element.setAttribute("interpolate-frequencies", "true");
624
+ expect(element.interpolateFrequencies).toBe(true);
625
+ });
325
626
 
326
- expect(element.durationMs).toBe(1_000);
327
- expect(timegroup.durationMs).toBe(1_000);
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
+ });
634
+
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
328
647
  });
329
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
+ // });
330
755
  });