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