@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.
- package/README.md +30 -0
- package/dist/DecoderResetFrequency.test.d.ts +1 -0
- package/dist/DecoderResetRecovery.test.d.ts +1 -0
- package/dist/DelayedLoadingState.d.ts +48 -0
- package/dist/DelayedLoadingState.integration.test.d.ts +1 -0
- package/dist/DelayedLoadingState.js +113 -0
- package/dist/DelayedLoadingState.test.d.ts +1 -0
- package/dist/EF_FRAMEGEN.d.ts +10 -1
- package/dist/EF_FRAMEGEN.js +199 -179
- package/dist/EF_INTERACTIVE.js +2 -6
- package/dist/EF_RENDERING.js +1 -3
- package/dist/LoadingDebounce.test.d.ts +1 -0
- package/dist/LoadingIndicator.browsertest.d.ts +0 -0
- package/dist/ManualScrubTest.test.d.ts +1 -0
- package/dist/ScrubResolvedFlashing.test.d.ts +1 -0
- package/dist/ScrubTrackManager.d.ts +96 -0
- package/dist/ScrubTrackManager.test.d.ts +1 -0
- package/dist/VideoSeekFlashing.browsertest.d.ts +0 -0
- package/dist/VideoStuckDiagnostic.test.d.ts +1 -0
- package/dist/elements/CrossUpdateController.js +13 -15
- package/dist/elements/EFAudio.browsertest.d.ts +0 -0
- package/dist/elements/EFAudio.d.ts +22 -3
- package/dist/elements/EFAudio.js +60 -43
- package/dist/elements/EFCaptions.js +337 -373
- package/dist/elements/EFImage.d.ts +1 -0
- package/dist/elements/EFImage.js +73 -91
- package/dist/elements/EFMedia/AssetIdMediaEngine.d.ts +18 -0
- package/dist/elements/EFMedia/AssetIdMediaEngine.js +41 -0
- package/dist/elements/EFMedia/AssetIdMediaEngine.test.d.ts +1 -0
- package/dist/elements/EFMedia/AssetMediaEngine.d.ts +47 -0
- package/dist/elements/EFMedia/AssetMediaEngine.js +116 -0
- package/dist/elements/EFMedia/BaseMediaEngine.d.ts +55 -0
- package/dist/elements/EFMedia/BaseMediaEngine.js +96 -0
- package/dist/elements/EFMedia/BaseMediaEngine.test.d.ts +1 -0
- package/dist/elements/EFMedia/BufferedSeekingInput.browsertest.d.ts +1 -0
- package/dist/elements/EFMedia/BufferedSeekingInput.d.ts +43 -0
- package/dist/elements/EFMedia/BufferedSeekingInput.js +159 -0
- package/dist/elements/EFMedia/JitMediaEngine.browsertest.d.ts +0 -0
- package/dist/elements/EFMedia/JitMediaEngine.d.ts +31 -0
- package/dist/elements/EFMedia/JitMediaEngine.js +62 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.browsertest.d.ts +9 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.d.ts +16 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.js +48 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.d.ts +3 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.js +138 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.browsertest.d.ts +9 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.d.ts +4 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.js +16 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.browsertest.d.ts +9 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.d.ts +3 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.js +22 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.d.ts +7 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.js +24 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.d.ts +4 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.js +18 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.d.ts +4 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.js +16 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.d.ts +3 -0
- package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.js +104 -0
- package/dist/elements/EFMedia/services/AudioElementFactory.browsertest.d.ts +1 -0
- package/dist/elements/EFMedia/services/AudioElementFactory.d.ts +22 -0
- package/dist/elements/EFMedia/services/AudioElementFactory.js +72 -0
- package/dist/elements/EFMedia/services/MediaSourceService.browsertest.d.ts +1 -0
- package/dist/elements/EFMedia/services/MediaSourceService.d.ts +47 -0
- package/dist/elements/EFMedia/services/MediaSourceService.js +73 -0
- package/dist/elements/EFMedia/shared/AudioSpanUtils.d.ts +7 -0
- package/dist/elements/EFMedia/shared/AudioSpanUtils.js +54 -0
- package/dist/elements/EFMedia/shared/BufferUtils.d.ts +70 -0
- package/dist/elements/EFMedia/shared/BufferUtils.js +89 -0
- package/dist/elements/EFMedia/shared/MediaTaskUtils.d.ts +23 -0
- package/dist/elements/EFMedia/shared/RenditionHelpers.browsertest.d.ts +1 -0
- package/dist/elements/EFMedia/shared/RenditionHelpers.d.ts +19 -0
- package/dist/elements/EFMedia/tasks/makeMediaEngineTask.browsertest.d.ts +1 -0
- package/dist/elements/EFMedia/tasks/makeMediaEngineTask.d.ts +18 -0
- package/dist/elements/EFMedia/tasks/makeMediaEngineTask.js +60 -0
- package/dist/elements/EFMedia/tasks/makeMediaEngineTask.test.d.ts +1 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.browsertest.d.ts +9 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.d.ts +16 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.js +46 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoInitSegmentFetchTask.browsertest.d.ts +9 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoInitSegmentFetchTask.d.ts +4 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoInitSegmentFetchTask.js +16 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoInputTask.browsertest.d.ts +9 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoInputTask.d.ts +3 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoInputTask.js +27 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoSeekTask.d.ts +7 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoSeekTask.js +25 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.browsertest.d.ts +9 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.d.ts +4 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.js +18 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.browsertest.d.ts +9 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.d.ts +4 -0
- package/dist/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.js +16 -0
- package/dist/elements/EFMedia.browsertest.d.ts +1 -0
- package/dist/elements/EFMedia.d.ts +95 -66
- package/dist/elements/EFMedia.js +204 -683
- package/dist/elements/EFSourceMixin.js +31 -48
- package/dist/elements/EFTemporal.d.ts +2 -1
- package/dist/elements/EFTemporal.js +266 -360
- package/dist/elements/EFTimegroup.d.ts +14 -1
- package/dist/elements/EFTimegroup.js +337 -323
- package/dist/elements/EFVideo.browsertest.d.ts +0 -0
- package/dist/elements/EFVideo.d.ts +123 -4
- package/dist/elements/EFVideo.js +308 -111
- package/dist/elements/EFWaveform.js +375 -411
- package/dist/elements/FetchMixin.js +14 -24
- package/dist/elements/MediaController.d.ts +30 -0
- package/dist/elements/SampleBuffer.d.ts +14 -0
- package/dist/elements/SampleBuffer.js +52 -0
- package/dist/elements/TargetController.js +130 -156
- package/dist/elements/TimegroupController.js +17 -19
- package/dist/elements/durationConverter.js +15 -4
- package/dist/elements/parseTimeToMs.js +4 -10
- package/dist/elements/printTaskStatus.d.ts +2 -0
- package/dist/elements/updateAnimations.js +39 -59
- package/dist/getRenderInfo.d.ts +2 -2
- package/dist/getRenderInfo.js +59 -67
- package/dist/gui/ContextMixin.js +150 -288
- package/dist/gui/EFConfiguration.js +27 -43
- package/dist/gui/EFFilmstrip.d.ts +3 -3
- package/dist/gui/EFFilmstrip.js +440 -620
- package/dist/gui/EFFitScale.d.ts +2 -2
- package/dist/gui/EFFitScale.js +112 -135
- package/dist/gui/EFFocusOverlay.js +45 -61
- package/dist/gui/EFPreview.js +30 -49
- package/dist/gui/EFScrubber.js +78 -99
- package/dist/gui/EFTimeDisplay.js +49 -70
- package/dist/gui/EFToggleLoop.js +17 -34
- package/dist/gui/EFTogglePlay.js +37 -58
- package/dist/gui/EFWorkbench.js +66 -88
- package/dist/gui/TWMixin.js +2 -48
- package/dist/gui/TWMixin2.js +31 -0
- package/dist/gui/efContext.js +2 -6
- package/dist/gui/fetchContext.js +1 -3
- package/dist/gui/focusContext.js +1 -3
- package/dist/gui/focusedElementContext.js +2 -6
- package/dist/gui/playingContext.js +1 -4
- package/dist/gui/services/ElementConnectionManager.browsertest.d.ts +1 -0
- package/dist/gui/services/ElementConnectionManager.d.ts +59 -0
- package/dist/gui/services/ElementConnectionManager.js +128 -0
- package/dist/gui/services/PlaybackController.browsertest.d.ts +1 -0
- package/dist/gui/services/PlaybackController.d.ts +103 -0
- package/dist/gui/services/PlaybackController.js +290 -0
- package/dist/index.js +5 -30
- package/dist/msToTimeCode.js +11 -13
- package/dist/services/MediaSourceManager.d.ts +62 -0
- package/dist/services/MediaSourceManager.js +211 -0
- package/dist/style.css +2 -1
- package/dist/transcoding/cache/CacheManager.d.ts +73 -0
- package/dist/transcoding/cache/RequestDeduplicator.d.ts +29 -0
- package/dist/transcoding/cache/RequestDeduplicator.js +53 -0
- package/dist/transcoding/cache/RequestDeduplicator.test.d.ts +1 -0
- package/dist/transcoding/types/index.d.ts +242 -0
- package/dist/transcoding/utils/MediaUtils.d.ts +9 -0
- package/dist/transcoding/utils/UrlGenerator.d.ts +26 -0
- package/dist/transcoding/utils/UrlGenerator.js +45 -0
- package/dist/transcoding/utils/constants.d.ts +27 -0
- package/dist/utils/LRUCache.d.ts +34 -0
- package/dist/utils/LRUCache.js +115 -0
- package/package.json +4 -3
- package/src/elements/EFAudio.browsertest.ts +709 -0
- package/src/elements/EFAudio.ts +59 -15
- package/src/elements/EFCaptions.browsertest.ts +0 -1
- package/src/elements/EFImage.browsertest.ts +42 -1
- package/src/elements/EFImage.ts +23 -3
- package/src/elements/EFMedia/AssetIdMediaEngine.test.ts +222 -0
- package/src/elements/EFMedia/AssetIdMediaEngine.ts +70 -0
- package/src/elements/EFMedia/AssetMediaEngine.ts +210 -0
- package/src/elements/EFMedia/BaseMediaEngine.test.ts +164 -0
- package/src/elements/EFMedia/BaseMediaEngine.ts +170 -0
- package/src/elements/EFMedia/BufferedSeekingInput.browsertest.ts +400 -0
- package/src/elements/EFMedia/BufferedSeekingInput.ts +267 -0
- package/src/elements/EFMedia/JitMediaEngine.browsertest.ts +165 -0
- package/src/elements/EFMedia/JitMediaEngine.ts +110 -0
- package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.browsertest.ts +554 -0
- package/src/elements/EFMedia/audioTasks/makeAudioBufferTask.ts +81 -0
- package/src/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.ts +241 -0
- package/src/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.browsertest.ts +59 -0
- package/src/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.ts +23 -0
- package/src/elements/EFMedia/audioTasks/makeAudioInputTask.browsertest.ts +55 -0
- package/src/elements/EFMedia/audioTasks/makeAudioInputTask.ts +35 -0
- package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.ts +42 -0
- package/src/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.ts +34 -0
- package/src/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.ts +23 -0
- package/src/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.ts +174 -0
- package/src/elements/EFMedia/services/AudioElementFactory.browsertest.ts +325 -0
- package/src/elements/EFMedia/services/AudioElementFactory.ts +119 -0
- package/src/elements/EFMedia/services/MediaSourceService.browsertest.ts +257 -0
- package/src/elements/EFMedia/services/MediaSourceService.ts +102 -0
- package/src/elements/EFMedia/shared/AudioSpanUtils.ts +128 -0
- package/src/elements/EFMedia/shared/BufferUtils.ts +310 -0
- package/src/elements/EFMedia/shared/MediaTaskUtils.ts +44 -0
- package/src/elements/EFMedia/shared/RenditionHelpers.browsertest.ts +247 -0
- package/src/elements/EFMedia/shared/RenditionHelpers.ts +79 -0
- package/src/elements/EFMedia/tasks/makeMediaEngineTask.browsertest.ts +128 -0
- package/src/elements/EFMedia/tasks/makeMediaEngineTask.test.ts +233 -0
- package/src/elements/EFMedia/tasks/makeMediaEngineTask.ts +89 -0
- package/src/elements/EFMedia/videoTasks/makeVideoBufferTask.browsertest.ts +555 -0
- package/src/elements/EFMedia/videoTasks/makeVideoBufferTask.ts +79 -0
- package/src/elements/EFMedia/videoTasks/makeVideoInitSegmentFetchTask.browsertest.ts +59 -0
- package/src/elements/EFMedia/videoTasks/makeVideoInitSegmentFetchTask.ts +23 -0
- package/src/elements/EFMedia/videoTasks/makeVideoInputTask.browsertest.ts +55 -0
- package/src/elements/EFMedia/videoTasks/makeVideoInputTask.ts +45 -0
- package/src/elements/EFMedia/videoTasks/makeVideoSeekTask.ts +44 -0
- package/src/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.browsertest.ts +57 -0
- package/src/elements/EFMedia/videoTasks/makeVideoSegmentFetchTask.ts +32 -0
- package/src/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.browsertest.ts +56 -0
- package/src/elements/EFMedia/videoTasks/makeVideoSegmentIdTask.ts +23 -0
- package/src/elements/EFMedia.browsertest.ts +696 -271
- package/src/elements/EFMedia.ts +218 -776
- package/src/elements/EFTemporal.browsertest.ts +0 -1
- package/src/elements/EFTemporal.ts +13 -3
- package/src/elements/EFTimegroup.browsertest.ts +6 -3
- package/src/elements/EFTimegroup.ts +221 -27
- package/src/elements/EFVideo.browsertest.ts +758 -0
- package/src/elements/EFVideo.ts +418 -68
- package/src/elements/EFWaveform.ts +5 -5
- package/src/elements/MediaController.ts +98 -0
- package/src/elements/SampleBuffer.ts +97 -0
- package/src/elements/printTaskStatus.ts +16 -0
- package/src/elements/updateAnimations.ts +6 -0
- package/src/gui/ContextMixin.ts +23 -104
- package/src/gui/TWMixin.ts +10 -3
- package/src/gui/services/ElementConnectionManager.browsertest.ts +263 -0
- package/src/gui/services/ElementConnectionManager.ts +224 -0
- package/src/gui/services/PlaybackController.browsertest.ts +437 -0
- package/src/gui/services/PlaybackController.ts +521 -0
- package/src/services/MediaSourceManager.ts +333 -0
- package/src/transcoding/cache/CacheManager.ts +208 -0
- package/src/transcoding/cache/RequestDeduplicator.test.ts +170 -0
- package/src/transcoding/cache/RequestDeduplicator.ts +65 -0
- package/src/transcoding/types/index.ts +265 -0
- package/src/transcoding/utils/MediaUtils.ts +63 -0
- package/src/transcoding/utils/UrlGenerator.ts +68 -0
- package/src/transcoding/utils/constants.ts +36 -0
- package/src/utils/LRUCache.ts +153 -0
- package/test/EFVideo.framegen.browsertest.ts +127 -0
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- package/test/__cache__/GET__api_v1_transcode_manifest_json_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__3be92a0437de726b431ed5af2369158a/data.bin +1 -0
- package/test/__cache__/GET__api_v1_transcode_manifest_json_url_http_3A_2F_2Fweb_3A3000_2Fhead_moov_480p_mp4__3be92a0437de726b431ed5af2369158a/metadata.json +19 -0
- package/test/createJitTestClips.ts +425 -0
- package/test/recordReplayProxyPlugin.js +302 -0
- package/test/useAssetMSW.ts +49 -0
- package/test/useMSW.ts +44 -0
- package/types.json +1 -1
- package/dist/gui/TWMixin.css.js +0 -4
- /package/dist/elements/{TargetController.test.d.ts → TargetController.browsertest.d.ts} +0 -0
- /package/src/elements/{TargetController.test.ts → TargetController.browsertest.ts} +0 -0
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const CACHE_DIR = join(__dirname, "__cache__");
|
|
9
|
+
const TARGET_HOST = "host.docker.internal";
|
|
10
|
+
const TARGET_PORT = 3000;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Vite plugin that adds record-and-replay proxy middleware
|
|
14
|
+
* This proxy intercepts requests to /api/v1/transcode/*, caches responses to disk,
|
|
15
|
+
* and serves cached responses when the real server is unavailable.
|
|
16
|
+
*/
|
|
17
|
+
export function recordReplayProxyPlugin() {
|
|
18
|
+
return {
|
|
19
|
+
name: "record-replay-proxy",
|
|
20
|
+
|
|
21
|
+
configureServer(server) {
|
|
22
|
+
console.log(
|
|
23
|
+
"[Proxy Plugin] Configuring record-replay proxy middleware...",
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
// Initialize cache directory
|
|
27
|
+
mkdir(CACHE_DIR, { recursive: true }).catch(console.error);
|
|
28
|
+
|
|
29
|
+
// Add middleware to handle /api/v1/transcode/* requests
|
|
30
|
+
server.middlewares.use("/api/v1/transcode", async (req, res, next) => {
|
|
31
|
+
await handleProxyRequest(req, res, next);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
console.log("[Proxy Plugin] Proxy middleware configured");
|
|
35
|
+
console.log(`[Proxy Plugin] Cache directory: ${CACHE_DIR}`);
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// Create cache key from request
|
|
40
|
+
function getCacheKey(method, url, headers) {
|
|
41
|
+
const range = headers.range || "";
|
|
42
|
+
const key = `${method}_${url}_${range}`;
|
|
43
|
+
const hash = crypto.createHash("md5").update(key).digest("hex");
|
|
44
|
+
const sanitized = key.replace(/[^a-zA-Z0-9]/g, "_").substring(0, 100);
|
|
45
|
+
return `${sanitized}_${hash}`; // Returns directory name
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Serve cached response
|
|
49
|
+
async function serveCachedResponse(res, cacheDir, req) {
|
|
50
|
+
try {
|
|
51
|
+
const metadataFile = join(cacheDir, "metadata.json");
|
|
52
|
+
const dataFile = join(cacheDir, "data.bin");
|
|
53
|
+
|
|
54
|
+
const metadata = JSON.parse(await readFile(metadataFile, "utf-8"));
|
|
55
|
+
let body = await readFile(dataFile);
|
|
56
|
+
|
|
57
|
+
// Rewrite URLs in cached JSON/text responses to point back to current proxy
|
|
58
|
+
const responseHeaders = { ...metadata.headers };
|
|
59
|
+
const contentType = responseHeaders["content-type"] || "";
|
|
60
|
+
if (
|
|
61
|
+
contentType.includes("application/json") ||
|
|
62
|
+
contentType.includes("text/")
|
|
63
|
+
) {
|
|
64
|
+
try {
|
|
65
|
+
const originalHost = `http://${TARGET_HOST}:${TARGET_PORT}`;
|
|
66
|
+
const proxyHost = `${req.headers["x-forwarded-proto"] || "http"}://${req.headers.host}`;
|
|
67
|
+
const bodyText = body.toString("utf-8");
|
|
68
|
+
const rewrittenText = bodyText.replace(
|
|
69
|
+
new RegExp(originalHost, "g"),
|
|
70
|
+
proxyHost,
|
|
71
|
+
);
|
|
72
|
+
body = Buffer.from(rewrittenText, "utf-8");
|
|
73
|
+
|
|
74
|
+
// Update content-length if it changed
|
|
75
|
+
if (bodyText.length !== rewrittenText.length) {
|
|
76
|
+
responseHeaders["content-length"] = body.length.toString();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
console.log(
|
|
80
|
+
`[Proxy] ✓ Rewrote cached URLs: ${originalHost} → ${proxyHost}`,
|
|
81
|
+
);
|
|
82
|
+
} catch (error) {
|
|
83
|
+
console.warn(
|
|
84
|
+
`[Proxy] Failed to rewrite cached URLs: ${error.message}`,
|
|
85
|
+
);
|
|
86
|
+
// Continue with original body on error
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
res.writeHead(metadata.statusCode, responseHeaders);
|
|
91
|
+
res.end(body);
|
|
92
|
+
} catch (error) {
|
|
93
|
+
console.error(
|
|
94
|
+
`[Proxy] Failed to serve cached response: ${error.message}`,
|
|
95
|
+
);
|
|
96
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
97
|
+
res.end(JSON.stringify({ error: "Failed to read cache" }));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Save response to cache
|
|
102
|
+
async function cacheResponse(
|
|
103
|
+
cacheDir,
|
|
104
|
+
statusCode,
|
|
105
|
+
headers,
|
|
106
|
+
body,
|
|
107
|
+
method,
|
|
108
|
+
url,
|
|
109
|
+
range,
|
|
110
|
+
) {
|
|
111
|
+
try {
|
|
112
|
+
await mkdir(cacheDir, { recursive: true }); // Create cache directory
|
|
113
|
+
|
|
114
|
+
const metadata = {
|
|
115
|
+
statusCode,
|
|
116
|
+
headers,
|
|
117
|
+
url,
|
|
118
|
+
method,
|
|
119
|
+
range: range || null,
|
|
120
|
+
timestamp: new Date().toISOString(),
|
|
121
|
+
};
|
|
122
|
+
const metadataFile = join(cacheDir, "metadata.json");
|
|
123
|
+
await writeFile(metadataFile, JSON.stringify(metadata, null, 2));
|
|
124
|
+
|
|
125
|
+
const dataFile = join(cacheDir, "data.bin");
|
|
126
|
+
await writeFile(dataFile, body); // Write raw binary data
|
|
127
|
+
|
|
128
|
+
console.log("[Proxy] ✓ Cached response");
|
|
129
|
+
} catch (error) {
|
|
130
|
+
console.warn(`[Proxy] Failed to cache: ${error.message}`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Handle proxy request as middleware
|
|
135
|
+
async function handleProxyRequest(req, res, next) {
|
|
136
|
+
const fullPath = `/api/v1/transcode${req.url}`;
|
|
137
|
+
console.log(`[Proxy] → ${req.method} ${fullPath}`);
|
|
138
|
+
if (req.headers.range) {
|
|
139
|
+
console.log(`[Proxy] Range: ${req.headers.range}`);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Set CORS headers
|
|
143
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
144
|
+
res.setHeader(
|
|
145
|
+
"Access-Control-Allow-Methods",
|
|
146
|
+
"GET, POST, PUT, DELETE, OPTIONS",
|
|
147
|
+
);
|
|
148
|
+
res.setHeader(
|
|
149
|
+
"Access-Control-Allow-Headers",
|
|
150
|
+
"Content-Type, Range, Authorization",
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
if (req.method === "OPTIONS") {
|
|
154
|
+
res.writeHead(200);
|
|
155
|
+
res.end();
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const cacheKey = getCacheKey(req.method, fullPath, req.headers);
|
|
160
|
+
const cacheDir = join(CACHE_DIR, cacheKey);
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
// Collect request body
|
|
164
|
+
const requestChunks = [];
|
|
165
|
+
req.on("data", (chunk) => {
|
|
166
|
+
requestChunks.push(chunk);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
req.on("end", async () => {
|
|
170
|
+
try {
|
|
171
|
+
const requestBody = Buffer.concat(requestChunks);
|
|
172
|
+
// Reconstruct the full path since middleware strips the prefix
|
|
173
|
+
const fullPath = `/api/v1/transcode${req.url}`;
|
|
174
|
+
const targetUrl = `http://${TARGET_HOST}:${TARGET_PORT}${fullPath}`;
|
|
175
|
+
|
|
176
|
+
const fetchOptions = {
|
|
177
|
+
method: req.method,
|
|
178
|
+
headers: req.headers,
|
|
179
|
+
body: requestBody.length > 0 ? requestBody : undefined,
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const response = await fetch(targetUrl, fetchOptions);
|
|
183
|
+
let body = Buffer.from(await response.arrayBuffer());
|
|
184
|
+
|
|
185
|
+
const responseHeaders = {};
|
|
186
|
+
response.headers.forEach((value, key) => {
|
|
187
|
+
responseHeaders[key] = value;
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// Rewrite URLs in JSON/text responses to point back to proxy
|
|
191
|
+
const contentType = responseHeaders["content-type"] || "";
|
|
192
|
+
if (
|
|
193
|
+
contentType.includes("application/json") ||
|
|
194
|
+
contentType.includes("text/")
|
|
195
|
+
) {
|
|
196
|
+
try {
|
|
197
|
+
const originalHost = `http://${TARGET_HOST}:${TARGET_PORT}`;
|
|
198
|
+
const proxyHost = `${req.headers["x-forwarded-proto"] || "http"}://${req.headers.host}`;
|
|
199
|
+
const bodyText = body.toString("utf-8");
|
|
200
|
+
const rewrittenText = bodyText.replace(
|
|
201
|
+
new RegExp(originalHost, "g"),
|
|
202
|
+
proxyHost,
|
|
203
|
+
);
|
|
204
|
+
body = Buffer.from(rewrittenText, "utf-8");
|
|
205
|
+
|
|
206
|
+
// Update content-length if it changed
|
|
207
|
+
if (bodyText.length !== rewrittenText.length) {
|
|
208
|
+
responseHeaders["content-length"] = body.length.toString();
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
console.log(
|
|
212
|
+
`[Proxy] ✓ Rewrote URLs: ${originalHost} → ${proxyHost}`,
|
|
213
|
+
);
|
|
214
|
+
} catch (error) {
|
|
215
|
+
console.warn(`[Proxy] Failed to rewrite URLs: ${error.message}`);
|
|
216
|
+
// Continue with original body on error
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// If we get a 404, try to serve from cache first
|
|
221
|
+
if (response.status === 404) {
|
|
222
|
+
console.log("[Proxy] ✗ Target server returned 404");
|
|
223
|
+
console.log(`[Proxy] Checking cache: ${cacheKey}`);
|
|
224
|
+
|
|
225
|
+
if (existsSync(cacheDir)) {
|
|
226
|
+
try {
|
|
227
|
+
const metadataFile = join(cacheDir, "metadata.json");
|
|
228
|
+
if (existsSync(metadataFile)) {
|
|
229
|
+
const metadata = JSON.parse(
|
|
230
|
+
await readFile(metadataFile, "utf-8"),
|
|
231
|
+
);
|
|
232
|
+
console.log(
|
|
233
|
+
`[Proxy] ✓ Serving from cache instead of 404 (${metadata.timestamp})`,
|
|
234
|
+
);
|
|
235
|
+
await serveCachedResponse(res, cacheDir, req);
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
} catch (cacheError) {
|
|
239
|
+
console.error(
|
|
240
|
+
`[Proxy] Failed to read cache: ${cacheError.message}`,
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
console.log("[Proxy] ✗ No cache available, passing through 404");
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
res.writeHead(response.status, responseHeaders);
|
|
249
|
+
res.end(body);
|
|
250
|
+
|
|
251
|
+
if (response.status >= 200 && response.status < 300) {
|
|
252
|
+
await cacheResponse(
|
|
253
|
+
cacheDir,
|
|
254
|
+
response.status,
|
|
255
|
+
responseHeaders,
|
|
256
|
+
body,
|
|
257
|
+
req.method,
|
|
258
|
+
fullPath,
|
|
259
|
+
req.headers.range,
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
} catch (err) {
|
|
263
|
+
console.log(`[Proxy] ✗ Real server failed: ${err.message}`);
|
|
264
|
+
console.log(`[Proxy] Checking cache: ${cacheKey}`);
|
|
265
|
+
|
|
266
|
+
if (existsSync(cacheDir)) {
|
|
267
|
+
try {
|
|
268
|
+
const metadataFile = join(cacheDir, "metadata.json");
|
|
269
|
+
if (existsSync(metadataFile)) {
|
|
270
|
+
const metadata = JSON.parse(
|
|
271
|
+
await readFile(metadataFile, "utf-8"),
|
|
272
|
+
);
|
|
273
|
+
console.log(
|
|
274
|
+
`[Proxy] ✓ Serving from cache (${metadata.timestamp})`,
|
|
275
|
+
);
|
|
276
|
+
await serveCachedResponse(res, cacheDir, req);
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
} catch (cacheError) {
|
|
280
|
+
console.error(
|
|
281
|
+
`[Proxy] Failed to read cache: ${cacheError.message}`,
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
console.log("[Proxy] ✗ No cache available, failing request");
|
|
287
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
288
|
+
res.end(
|
|
289
|
+
JSON.stringify({
|
|
290
|
+
error: "Server unavailable and no cache found",
|
|
291
|
+
cacheKey,
|
|
292
|
+
originalError: err.message,
|
|
293
|
+
}),
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
} catch (error) {
|
|
298
|
+
console.error(`[Proxy] Middleware error: ${error.message}`);
|
|
299
|
+
next();
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Asset-specific MSW handlers for testing
|
|
3
|
+
* Provides pre-configured handlers for asset fragment indexes and track data
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { HttpResponse, http } from "msw";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Asset MSW handlers that redirect requests to real test assets
|
|
10
|
+
* Use with MSW worker.use() to proxy asset requests to /test-assets/asset-mode/
|
|
11
|
+
*/
|
|
12
|
+
export const assetMSWHandlers = [
|
|
13
|
+
// Fragment index handler - rewrite to test asset
|
|
14
|
+
http.get("/@ef-track-fragment-index/*", async () => {
|
|
15
|
+
const response = await fetch("/asset-mode/index.json");
|
|
16
|
+
const data = await response.json();
|
|
17
|
+
return HttpResponse.json(data);
|
|
18
|
+
}),
|
|
19
|
+
|
|
20
|
+
// Track data handler - rewrite to test asset with proper range support
|
|
21
|
+
http.get("/@ef-track/*", async ({ request }) => {
|
|
22
|
+
const url = new URL(request.url);
|
|
23
|
+
const trackId = url.searchParams.get("trackId");
|
|
24
|
+
if (!trackId) {
|
|
25
|
+
return new HttpResponse(null, { status: 400 });
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const rangeHeader = request.headers.get("range");
|
|
29
|
+
const response = await fetch(`/asset-mode/track-${trackId}.mp4`, {
|
|
30
|
+
headers: {
|
|
31
|
+
...(rangeHeader && {
|
|
32
|
+
range: rangeHeader,
|
|
33
|
+
}),
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const contentRangeHeader = response.headers.get("Content-Range");
|
|
38
|
+
return new HttpResponse(await response.arrayBuffer(), {
|
|
39
|
+
status: response.status,
|
|
40
|
+
headers: {
|
|
41
|
+
"Content-Type": "video/mp4",
|
|
42
|
+
"Accept-Ranges": "bytes",
|
|
43
|
+
...(contentRangeHeader && {
|
|
44
|
+
"Content-Range": contentRangeHeader,
|
|
45
|
+
}),
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
}),
|
|
49
|
+
];
|
package/test/useMSW.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MSW integration for Vitest Browser Mode
|
|
3
|
+
* Based on: https://mswjs.io/docs/recipes/vitest-browser-mode/
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { setupWorker } from "msw/browser";
|
|
7
|
+
import { test as testBase } from "vitest";
|
|
8
|
+
|
|
9
|
+
// Create the worker instance that will be shared across tests
|
|
10
|
+
const worker = setupWorker();
|
|
11
|
+
|
|
12
|
+
// Start the worker once when this module is loaded
|
|
13
|
+
let workerStarted = false;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Extended test with MSW worker integration for Vitest Browser Mode
|
|
17
|
+
* This follows the official MSW recommendation for Vitest Browser Mode
|
|
18
|
+
*/
|
|
19
|
+
export const test = testBase.extend<{
|
|
20
|
+
worker: typeof worker;
|
|
21
|
+
}>({
|
|
22
|
+
worker: [
|
|
23
|
+
async ({}, use) => {
|
|
24
|
+
// Only start the worker once
|
|
25
|
+
if (!workerStarted) {
|
|
26
|
+
await worker.start({
|
|
27
|
+
onUnhandledRequest: "bypass", // Allow unhandled requests to pass through to server
|
|
28
|
+
quiet: true, // Enable logging to see what's being intercepted
|
|
29
|
+
});
|
|
30
|
+
workerStarted = true;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Expose the worker object on the test's context
|
|
34
|
+
await use(worker);
|
|
35
|
+
|
|
36
|
+
// Remove any request handlers added in individual test cases
|
|
37
|
+
// This prevents them from affecting unrelated tests
|
|
38
|
+
worker.resetHandlers();
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
auto: true, // Critical: Auto-start MSW for all tests
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
});
|