@editframe/elements 0.30.1-beta.0 → 0.31.0-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.d.ts +5 -0
- package/dist/EF_FRAMEGEN.js +20 -4
- package/dist/EF_FRAMEGEN.js.map +1 -1
- package/dist/EF_INTERACTIVE.js.map +1 -1
- package/dist/_virtual/rolldown_runtime.js +27 -0
- package/dist/canvas/EFCanvas.d.ts +311 -0
- package/dist/canvas/EFCanvas.js +1089 -0
- package/dist/canvas/EFCanvas.js.map +1 -0
- package/dist/canvas/EFCanvasItem.d.ts +55 -0
- package/dist/canvas/EFCanvasItem.js +72 -0
- package/dist/canvas/EFCanvasItem.js.map +1 -0
- package/dist/canvas/api/CanvasAPI.d.ts +115 -0
- package/dist/canvas/api/CanvasAPI.js +182 -0
- package/dist/canvas/api/CanvasAPI.js.map +1 -0
- package/dist/canvas/api/types.d.ts +42 -0
- package/dist/canvas/coordinateTransform.js +90 -0
- package/dist/canvas/coordinateTransform.js.map +1 -0
- package/dist/canvas/getElementBounds.js +40 -0
- package/dist/canvas/getElementBounds.js.map +1 -0
- package/dist/canvas/overlays/SelectionOverlay.js +265 -0
- package/dist/canvas/overlays/SelectionOverlay.js.map +1 -0
- package/dist/canvas/overlays/overlayState.js +153 -0
- package/dist/canvas/overlays/overlayState.js.map +1 -0
- package/dist/canvas/selection/SelectionController.js +105 -0
- package/dist/canvas/selection/SelectionController.js.map +1 -0
- package/dist/canvas/selection/SelectionModel.d.ts +98 -0
- package/dist/canvas/selection/SelectionModel.js +229 -0
- package/dist/canvas/selection/SelectionModel.js.map +1 -0
- package/dist/canvas/selection/selectionContext.d.ts +31 -0
- package/dist/canvas/selection/selectionContext.js +12 -0
- package/dist/canvas/selection/selectionContext.js.map +1 -0
- package/dist/elements/ContainerInfo.d.ts +29 -0
- package/dist/elements/ContainerInfo.js +30 -0
- package/dist/elements/ContainerInfo.js.map +1 -0
- package/dist/elements/EFAudio.d.ts +13 -3
- package/dist/elements/EFAudio.js +64 -10
- package/dist/elements/EFAudio.js.map +1 -1
- package/dist/elements/EFCaptions.d.ts +18 -16
- package/dist/elements/EFCaptions.js +110 -19
- package/dist/elements/EFCaptions.js.map +1 -1
- package/dist/elements/EFImage.d.ts +16 -6
- package/dist/elements/EFImage.js +79 -9
- package/dist/elements/EFImage.js.map +1 -1
- package/dist/elements/EFMedia/AssetIdMediaEngine.js +51 -4
- package/dist/elements/EFMedia/AssetIdMediaEngine.js.map +1 -1
- package/dist/elements/EFMedia/AssetMediaEngine.js +125 -52
- package/dist/elements/EFMedia/AssetMediaEngine.js.map +1 -1
- package/dist/elements/EFMedia/BaseMediaEngine.js +24 -6
- package/dist/elements/EFMedia/BaseMediaEngine.js.map +1 -1
- package/dist/elements/EFMedia/JitMediaEngine.js +12 -8
- package/dist/elements/EFMedia/JitMediaEngine.js.map +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.js +46 -7
- package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.js.map +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.js +98 -73
- package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.js.map +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.js +28 -5
- package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.js.map +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.js +18 -6
- package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.js.map +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.js +8 -2
- package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.js.map +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.js +31 -6
- package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.js.map +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.js +28 -5
- package/dist/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.js.map +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.js +97 -72
- package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.js.map +1 -1
- package/dist/elements/EFMedia/shared/AudioSpanUtils.js +3 -1
- package/dist/elements/EFMedia/shared/AudioSpanUtils.js.map +1 -1
- package/dist/elements/EFMedia/shared/BufferUtils.js +1 -1
- package/dist/elements/EFMedia/shared/BufferUtils.js.map +1 -1
- package/dist/elements/EFMedia/shared/ThumbnailExtractor.js +25 -14
- package/dist/elements/EFMedia/shared/ThumbnailExtractor.js.map +1 -1
- package/dist/elements/EFMedia/tasks/makeMediaEngineTask.js +47 -16
- package/dist/elements/EFMedia/tasks/makeMediaEngineTask.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/MainVideoInputCache.js +37 -19
- package/dist/elements/EFMedia/videoTasks/MainVideoInputCache.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/ScrubInputCache.js +65 -21
- package/dist/elements/EFMedia/videoTasks/ScrubInputCache.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.js +8 -3
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoInitSegmentFetchTask.js +32 -9
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoInitSegmentFetchTask.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoInputTask.js +33 -10
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoInputTask.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.js +23 -8
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.js +34 -10
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.js +31 -8
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.js +31 -114
- package/dist/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.js +44 -8
- package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.js.map +1 -1
- package/dist/elements/EFMedia.d.ts +18 -7
- package/dist/elements/EFMedia.js +23 -3
- package/dist/elements/EFMedia.js.map +1 -1
- package/dist/elements/EFPanZoom.d.ts +96 -0
- package/dist/elements/EFPanZoom.js +290 -0
- package/dist/elements/EFPanZoom.js.map +1 -0
- package/dist/elements/EFSourceMixin.js +7 -6
- package/dist/elements/EFSourceMixin.js.map +1 -1
- package/dist/elements/EFSurface.d.ts +6 -6
- package/dist/elements/EFSurface.js +7 -2
- package/dist/elements/EFSurface.js.map +1 -1
- package/dist/elements/EFTemporal.d.ts +2 -1
- package/dist/elements/EFTemporal.js +192 -71
- package/dist/elements/EFTemporal.js.map +1 -1
- package/dist/elements/EFText.d.ts +5 -4
- package/dist/elements/EFText.js +102 -13
- package/dist/elements/EFText.js.map +1 -1
- package/dist/elements/EFTextSegment.d.ts +32 -6
- package/dist/elements/EFTextSegment.js +53 -15
- package/dist/elements/EFTextSegment.js.map +1 -1
- package/dist/elements/EFThumbnailStrip.d.ts +118 -56
- package/dist/elements/EFThumbnailStrip.js +522 -358
- package/dist/elements/EFThumbnailStrip.js.map +1 -1
- package/dist/elements/EFTimegroup.d.ts +223 -27
- package/dist/elements/EFTimegroup.js +851 -148
- package/dist/elements/EFTimegroup.js.map +1 -1
- package/dist/elements/EFVideo.d.ts +42 -5
- package/dist/elements/EFVideo.js +165 -11
- package/dist/elements/EFVideo.js.map +1 -1
- package/dist/elements/EFWaveform.d.ts +6 -6
- package/dist/elements/EFWaveform.js +2 -1
- package/dist/elements/EFWaveform.js.map +1 -1
- package/dist/elements/ElementPositionInfo.d.ts +35 -0
- package/dist/elements/ElementPositionInfo.js +49 -0
- package/dist/elements/ElementPositionInfo.js.map +1 -0
- package/dist/elements/FetchMixin.js +16 -1
- package/dist/elements/FetchMixin.js.map +1 -1
- package/dist/elements/SessionThumbnailCache.js +152 -0
- package/dist/elements/SessionThumbnailCache.js.map +1 -0
- package/dist/elements/TargetController.js +3 -1
- package/dist/elements/TargetController.js.map +1 -1
- package/dist/elements/TimegroupController.js +9 -3
- package/dist/elements/TimegroupController.js.map +1 -1
- package/dist/elements/findRootTemporal.js +30 -0
- package/dist/elements/findRootTemporal.js.map +1 -0
- package/dist/elements/renderTemporalAudio.js +18 -5
- package/dist/elements/renderTemporalAudio.js.map +1 -1
- package/dist/elements/updateAnimations.js +492 -109
- package/dist/elements/updateAnimations.js.map +1 -1
- package/dist/getRenderInfo.d.ts +2 -2
- package/dist/gui/ContextMixin.js +4 -2
- package/dist/gui/ContextMixin.js.map +1 -1
- package/dist/gui/Controllable.js +74 -1
- package/dist/gui/Controllable.js.map +1 -1
- package/dist/gui/EFActiveRootTemporal.d.ts +50 -0
- package/dist/gui/EFActiveRootTemporal.js +94 -0
- package/dist/gui/EFActiveRootTemporal.js.map +1 -0
- package/dist/gui/EFConfiguration.d.ts +11 -5
- package/dist/gui/EFConfiguration.js.map +1 -1
- package/dist/gui/EFControls.d.ts +2 -2
- package/dist/gui/EFControls.js +109 -13
- package/dist/gui/EFControls.js.map +1 -1
- package/dist/gui/EFDial.d.ts +4 -4
- package/dist/gui/EFFilmstrip.d.ts +11 -214
- package/dist/gui/EFFilmstrip.js +53 -1152
- package/dist/gui/EFFilmstrip.js.map +1 -1
- package/dist/gui/EFFitScale.d.ts +3 -3
- package/dist/gui/EFFitScale.js +39 -12
- package/dist/gui/EFFitScale.js.map +1 -1
- package/dist/gui/EFFocusOverlay.d.ts +4 -4
- package/dist/gui/EFOverlayItem.d.ts +48 -0
- package/dist/gui/EFOverlayItem.js +97 -0
- package/dist/gui/EFOverlayItem.js.map +1 -0
- package/dist/gui/EFOverlayLayer.d.ts +70 -0
- package/dist/gui/EFOverlayLayer.js +104 -0
- package/dist/gui/EFOverlayLayer.js.map +1 -0
- package/dist/gui/EFPause.d.ts +4 -4
- package/dist/gui/EFPlay.d.ts +4 -4
- package/dist/gui/EFPreview.d.ts +4 -4
- package/dist/gui/EFResizableBox.d.ts +12 -16
- package/dist/gui/EFResizableBox.js +109 -451
- package/dist/gui/EFResizableBox.js.map +1 -1
- package/dist/gui/EFScrubber.d.ts +30 -5
- package/dist/gui/EFScrubber.js +224 -31
- package/dist/gui/EFScrubber.js.map +1 -1
- package/dist/gui/EFTimeDisplay.d.ts +4 -4
- package/dist/gui/EFTimeDisplay.js +4 -1
- package/dist/gui/EFTimeDisplay.js.map +1 -1
- package/dist/gui/EFTimelineRuler.d.ts +71 -0
- package/dist/gui/EFTimelineRuler.js +320 -0
- package/dist/gui/EFTimelineRuler.js.map +1 -0
- package/dist/gui/EFToggleLoop.d.ts +4 -4
- package/dist/gui/EFTogglePlay.d.ts +4 -4
- package/dist/gui/EFTransformHandles.d.ts +91 -0
- package/dist/gui/EFTransformHandles.js +393 -0
- package/dist/gui/EFTransformHandles.js.map +1 -0
- package/dist/gui/EFWorkbench.d.ts +182 -4
- package/dist/gui/EFWorkbench.js +2067 -22
- package/dist/gui/EFWorkbench.js.map +1 -1
- package/dist/gui/FitScaleHelpers.d.ts +31 -0
- package/dist/gui/FitScaleHelpers.js +41 -0
- package/dist/gui/FitScaleHelpers.js.map +1 -0
- package/dist/gui/PlaybackController.d.ts +2 -1
- package/dist/gui/PlaybackController.js +46 -15
- package/dist/gui/PlaybackController.js.map +1 -1
- package/dist/gui/TWMixin.js +1 -1
- package/dist/gui/TWMixin.js.map +1 -1
- package/dist/gui/hierarchy/EFHierarchy.d.ts +65 -0
- package/dist/gui/hierarchy/EFHierarchy.js +338 -0
- package/dist/gui/hierarchy/EFHierarchy.js.map +1 -0
- package/dist/gui/hierarchy/EFHierarchyItem.d.ts +118 -0
- package/dist/gui/hierarchy/EFHierarchyItem.js +551 -0
- package/dist/gui/hierarchy/EFHierarchyItem.js.map +1 -0
- package/dist/gui/hierarchy/hierarchyContext.d.ts +38 -0
- package/dist/gui/hierarchy/hierarchyContext.js +8 -0
- package/dist/gui/hierarchy/hierarchyContext.js.map +1 -0
- package/dist/gui/icons.js +34 -0
- package/dist/gui/icons.js.map +1 -0
- package/dist/gui/panZoomTransformContext.js +12 -0
- package/dist/gui/panZoomTransformContext.js.map +1 -0
- package/dist/gui/previewSettingsContext.js +12 -0
- package/dist/gui/previewSettingsContext.js.map +1 -0
- package/dist/gui/timeline/EFTimeline.d.ts +270 -0
- package/dist/gui/timeline/EFTimeline.js +1369 -0
- package/dist/gui/timeline/EFTimeline.js.map +1 -0
- package/dist/gui/timeline/EFTimelineRow.js +374 -0
- package/dist/gui/timeline/EFTimelineRow.js.map +1 -0
- package/dist/gui/timeline/TrimHandles.d.ts +36 -0
- package/dist/gui/timeline/TrimHandles.js +204 -0
- package/dist/gui/timeline/TrimHandles.js.map +1 -0
- package/dist/gui/timeline/flattenHierarchy.js +31 -0
- package/dist/gui/timeline/flattenHierarchy.js.map +1 -0
- package/dist/gui/timeline/timelineStateContext.d.ts +26 -0
- package/dist/gui/timeline/timelineStateContext.js +42 -0
- package/dist/gui/timeline/timelineStateContext.js.map +1 -0
- package/dist/gui/timeline/tracks/AudioTrack.js +264 -0
- package/dist/gui/timeline/tracks/AudioTrack.js.map +1 -0
- package/dist/gui/timeline/tracks/CaptionsTrack.js +595 -0
- package/dist/gui/timeline/tracks/CaptionsTrack.js.map +1 -0
- package/dist/gui/timeline/tracks/HTMLTrack.js +19 -0
- package/dist/gui/timeline/tracks/HTMLTrack.js.map +1 -0
- package/dist/gui/timeline/tracks/ImageTrack.js +53 -0
- package/dist/gui/timeline/tracks/ImageTrack.js.map +1 -0
- package/dist/gui/timeline/tracks/TextTrack.js +250 -0
- package/dist/gui/timeline/tracks/TextTrack.js.map +1 -0
- package/dist/gui/timeline/tracks/TimegroupTrack.js +143 -0
- package/dist/gui/timeline/tracks/TimegroupTrack.js.map +1 -0
- package/dist/gui/timeline/tracks/TrackItem.js +269 -0
- package/dist/gui/timeline/tracks/TrackItem.js.map +1 -0
- package/dist/gui/timeline/tracks/VideoTrack.js +265 -0
- package/dist/gui/timeline/tracks/VideoTrack.js.map +1 -0
- package/dist/gui/timeline/tracks/WaveformTrack.js +19 -0
- package/dist/gui/timeline/tracks/WaveformTrack.js.map +1 -0
- package/dist/gui/timeline/tracks/ensureTrackItemInit.js +1 -0
- package/dist/gui/timeline/tracks/preloadTracks.js +9 -0
- package/dist/gui/timeline/tracks/renderTrackChildren.js +119 -0
- package/dist/gui/timeline/tracks/renderTrackChildren.js.map +1 -0
- package/dist/gui/timeline/tracks/waveformUtils.js +80 -0
- package/dist/gui/timeline/tracks/waveformUtils.js.map +1 -0
- package/dist/gui/transformCalculations.js +217 -0
- package/dist/gui/transformCalculations.js.map +1 -0
- package/dist/gui/transformUtils.d.ts +37 -0
- package/dist/gui/transformUtils.js +77 -0
- package/dist/gui/transformUtils.js.map +1 -0
- package/dist/gui/tree/EFTree.d.ts +59 -0
- package/dist/gui/tree/EFTree.js +174 -0
- package/dist/gui/tree/EFTree.js.map +1 -0
- package/dist/gui/tree/EFTreeItem.d.ts +38 -0
- package/dist/gui/tree/EFTreeItem.js +146 -0
- package/dist/gui/tree/EFTreeItem.js.map +1 -0
- package/dist/gui/tree/treeContext.d.ts +60 -0
- package/dist/gui/tree/treeContext.js +23 -0
- package/dist/gui/tree/treeContext.js.map +1 -0
- package/dist/index.d.ts +32 -8
- package/dist/index.js +30 -6
- package/dist/index.js.map +1 -1
- package/dist/node_modules/react/cjs/react-jsx-runtime.development.js +688 -0
- package/dist/node_modules/react/cjs/react-jsx-runtime.development.js.map +1 -0
- package/dist/node_modules/react/cjs/react.development.js +1521 -0
- package/dist/node_modules/react/cjs/react.development.js.map +1 -0
- package/dist/node_modules/react/index.js +13 -0
- package/dist/node_modules/react/index.js.map +1 -0
- package/dist/node_modules/react/jsx-runtime.js +13 -0
- package/dist/node_modules/react/jsx-runtime.js.map +1 -0
- package/dist/preview/AdaptiveResolutionTracker.js +228 -0
- package/dist/preview/AdaptiveResolutionTracker.js.map +1 -0
- package/dist/preview/RenderProfiler.js +135 -0
- package/dist/preview/RenderProfiler.js.map +1 -0
- package/dist/preview/previewSettings.js +131 -0
- package/dist/preview/previewSettings.js.map +1 -0
- package/dist/preview/previewTypes.js +64 -0
- package/dist/preview/previewTypes.js.map +1 -0
- package/dist/preview/renderTimegroupPreview.js +656 -0
- package/dist/preview/renderTimegroupPreview.js.map +1 -0
- package/dist/preview/renderTimegroupToCanvas.d.ts +37 -0
- package/dist/preview/renderTimegroupToCanvas.js +840 -0
- package/dist/preview/renderTimegroupToCanvas.js.map +1 -0
- package/dist/preview/renderTimegroupToVideo.d.ts +39 -0
- package/dist/preview/renderTimegroupToVideo.js +274 -0
- package/dist/preview/renderTimegroupToVideo.js.map +1 -0
- package/dist/preview/renderers.js +16 -0
- package/dist/preview/renderers.js.map +1 -0
- package/dist/preview/statsTrackingStrategy.js +201 -0
- package/dist/preview/statsTrackingStrategy.js.map +1 -0
- package/dist/preview/thumbnailCacheSettings.js +52 -0
- package/dist/preview/thumbnailCacheSettings.js.map +1 -0
- package/dist/preview/workers/WorkerPool.js +178 -0
- package/dist/preview/workers/WorkerPool.js.map +1 -0
- package/dist/sandbox/PlaybackControls.js +10 -0
- package/dist/sandbox/PlaybackControls.js.map +1 -0
- package/dist/sandbox/ScenarioRunner.js +1 -0
- package/dist/sandbox/index.js +2 -0
- package/dist/style.css +66 -69
- package/dist/transcoding/types/index.d.ts +2 -1
- package/dist/transcoding/utils/UrlGenerator.d.ts +6 -1
- package/dist/transcoding/utils/UrlGenerator.js +12 -3
- package/dist/transcoding/utils/UrlGenerator.js.map +1 -1
- package/dist/utils/LRUCache.js +1 -375
- package/dist/utils/LRUCache.js.map +1 -1
- package/dist/utils/frameTime.js +14 -0
- package/dist/utils/frameTime.js.map +1 -0
- package/package.json +3 -3
- package/test/profilingPlugin.ts +223 -0
- package/test/recordReplayProxyPlugin.js +22 -27
- package/test/thumbnail-performance-test.html +116 -0
- package/test/visualRegressionUtils.ts +286 -0
- package/types.json +1 -1
- package/dist/elements/TimegroupController.d.ts +0 -18
- package/dist/msToTimeCode.js +0 -17
- package/dist/msToTimeCode.js.map +0 -1
package/dist/elements/EFVideo.js
CHANGED
|
@@ -105,7 +105,9 @@ let EFVideo = class EFVideo$1 extends TWMixin(EFMedia) {
|
|
|
105
105
|
autoRun: false,
|
|
106
106
|
args: () => [this.desiredSeekTimeMs],
|
|
107
107
|
onError: (error) => {
|
|
108
|
-
|
|
108
|
+
this.frameTask.taskComplete.catch(() => {});
|
|
109
|
+
if (error instanceof DOMException && error.name === "AbortError" || error instanceof Error && (error.name === "AbortError" || error.message?.includes("signal is aborted") || error.message?.includes("The user aborted a request"))) return;
|
|
110
|
+
if (error instanceof Error && !error.message.includes("Video rendition unavailable") && !error.message.includes("No valid media source") && !error.message.includes("Sample not found for time")) console.error("frameTask error", error);
|
|
109
111
|
},
|
|
110
112
|
onComplete: () => {},
|
|
111
113
|
task: async ([_desiredSeekTimeMs], { signal }) => {
|
|
@@ -117,22 +119,26 @@ let EFVideo = class EFVideo$1 extends TWMixin(EFMedia) {
|
|
|
117
119
|
}, void 0, async (span) => {
|
|
118
120
|
const t1 = performance.now();
|
|
119
121
|
span.setAttribute("preworkMs", t1 - t0);
|
|
120
|
-
this.unifiedVideoSeekTask.run();
|
|
122
|
+
this.unifiedVideoSeekTask.run().catch(() => {});
|
|
121
123
|
const t2 = performance.now();
|
|
122
124
|
span.setAttribute("seekRunMs", t2 - t1);
|
|
123
|
-
|
|
125
|
+
try {
|
|
126
|
+
await this.unifiedVideoSeekTask.taskComplete;
|
|
127
|
+
} catch (error) {
|
|
128
|
+
if (error instanceof DOMException && error.name === "AbortError") {
|
|
129
|
+
signal?.throwIfAborted();
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
throw error;
|
|
133
|
+
}
|
|
124
134
|
const t3 = performance.now();
|
|
125
135
|
span.setAttribute("seekAwaitMs", t3 - t2);
|
|
126
|
-
|
|
127
|
-
span.setAttribute("aborted", true);
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
this.paint(this.desiredSeekTimeMs, span);
|
|
131
|
-
if (!this.parentTimegroup) updateAnimations(this);
|
|
136
|
+
signal?.throwIfAborted();
|
|
132
137
|
const t4 = performance.now();
|
|
133
138
|
this.paint(_desiredSeekTimeMs, span);
|
|
134
139
|
const t5 = performance.now();
|
|
135
140
|
span.setAttribute("paintMs", t5 - t4);
|
|
141
|
+
if (!this.parentTimegroup) updateAnimations(this);
|
|
136
142
|
span.setAttribute("totalFrameMs", t5 - t0);
|
|
137
143
|
});
|
|
138
144
|
}
|
|
@@ -227,7 +233,9 @@ let EFVideo = class EFVideo$1 extends TWMixin(EFMedia) {
|
|
|
227
233
|
console.warn("Unified video pipeline error:", error);
|
|
228
234
|
}
|
|
229
235
|
if (!isProductionRendering) {
|
|
230
|
-
|
|
236
|
+
const isInRenderClone = !!this.closest(".ef-render-clone-container");
|
|
237
|
+
if (isInRenderClone) span.setAttribute("renderClone", true);
|
|
238
|
+
if (!isInRenderClone && (!this.rootTimegroup || this.rootTimegroup.currentTimeMs === 0 && this.desiredSeekTimeMs === 0)) {
|
|
231
239
|
span.setAttribute("skipped", "preview-initialization");
|
|
232
240
|
return;
|
|
233
241
|
}
|
|
@@ -344,8 +352,140 @@ let EFVideo = class EFVideo$1 extends TWMixin(EFMedia) {
|
|
|
344
352
|
* @returns Promise that resolves when the frame is ready
|
|
345
353
|
*/
|
|
346
354
|
async waitForFrameReady() {
|
|
355
|
+
const currentTime = this.currentSourceTimeMs;
|
|
356
|
+
if (this.desiredSeekTimeMs !== currentTime) this.desiredSeekTimeMs = currentTime;
|
|
347
357
|
await this.updateComplete;
|
|
348
|
-
|
|
358
|
+
try {
|
|
359
|
+
await this.frameTask.run();
|
|
360
|
+
} catch (error) {
|
|
361
|
+
if (error instanceof DOMException && error.name === "AbortError" || error instanceof Error && (error.name === "AbortError" || error.message?.includes("signal is aborted") || error.message?.includes("The user aborted a request"))) return;
|
|
362
|
+
throw error;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Pre-fetch scrub segments for given timestamps.
|
|
367
|
+
* Loads 30-second segments sequentially, emitting progress events.
|
|
368
|
+
* This ensures scrub track is cached for fast thumbnail generation.
|
|
369
|
+
*
|
|
370
|
+
* @param timestamps - Array of timestamps (in ms) that will be captured
|
|
371
|
+
* @param onProgress - Optional callback for loading progress
|
|
372
|
+
* @returns Promise that resolves when all segments are cached
|
|
373
|
+
* @public
|
|
374
|
+
*/
|
|
375
|
+
async prefetchScrubSegments(timestamps, onProgress) {
|
|
376
|
+
const mediaEngine = await this.mediaEngineTask.taskComplete;
|
|
377
|
+
if (!mediaEngine) {
|
|
378
|
+
log("prefetchScrubSegments: no media engine available");
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
const scrubRendition = mediaEngine.getScrubVideoRendition();
|
|
382
|
+
if (!scrubRendition) {
|
|
383
|
+
log("prefetchScrubSegments: no scrub rendition available");
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
const scrubRenditionWithSrc = {
|
|
387
|
+
...scrubRendition,
|
|
388
|
+
src: mediaEngine.src
|
|
389
|
+
};
|
|
390
|
+
const segmentIds = /* @__PURE__ */ new Set();
|
|
391
|
+
for (const ts of timestamps) {
|
|
392
|
+
const segmentId = mediaEngine.computeSegmentId(ts, scrubRenditionWithSrc);
|
|
393
|
+
if (segmentId !== void 0) segmentIds.add(segmentId);
|
|
394
|
+
}
|
|
395
|
+
if (segmentIds.size === 0) {
|
|
396
|
+
log("prefetchScrubSegments: no segments to prefetch");
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
const firstSegmentId = Array.from(segmentIds)[0];
|
|
400
|
+
if (mediaEngine.isSegmentCached(firstSegmentId, scrubRenditionWithSrc)) {
|
|
401
|
+
log("prefetchScrubSegments: scrub track already cached");
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
log(`prefetchScrubSegments: fetching scrub track for ${segmentIds.size} segments...`);
|
|
405
|
+
const durationMs = mediaEngine.durationMs || 0;
|
|
406
|
+
this.dispatchEvent(new CustomEvent("scrub-segment-loading", {
|
|
407
|
+
detail: {
|
|
408
|
+
segmentId: 0,
|
|
409
|
+
timeRangeMs: [0, durationMs],
|
|
410
|
+
loaded: 0,
|
|
411
|
+
total: 1,
|
|
412
|
+
status: "loading"
|
|
413
|
+
},
|
|
414
|
+
bubbles: true,
|
|
415
|
+
composed: true
|
|
416
|
+
}));
|
|
417
|
+
try {
|
|
418
|
+
await mediaEngine.fetchMediaSegment(firstSegmentId, scrubRenditionWithSrc);
|
|
419
|
+
log(`prefetchScrubSegments: scrub track loaded`);
|
|
420
|
+
} catch (error) {
|
|
421
|
+
log(`prefetchScrubSegments: failed to load scrub track`, error);
|
|
422
|
+
}
|
|
423
|
+
this.dispatchEvent(new CustomEvent("scrub-segment-loading", {
|
|
424
|
+
detail: {
|
|
425
|
+
segmentId: 0,
|
|
426
|
+
timeRangeMs: [0, durationMs],
|
|
427
|
+
loaded: 1,
|
|
428
|
+
total: 1,
|
|
429
|
+
status: "loaded"
|
|
430
|
+
},
|
|
431
|
+
bubbles: true,
|
|
432
|
+
composed: true
|
|
433
|
+
}));
|
|
434
|
+
onProgress?.(1, 1, [0, durationMs]);
|
|
435
|
+
log(`prefetchScrubSegments: complete`);
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Pre-fetch main video segments for given timestamps.
|
|
439
|
+
* This ensures segments are cached for fast seeking during video export.
|
|
440
|
+
*
|
|
441
|
+
* @param timestamps - Array of timestamps (in ms) that will be captured
|
|
442
|
+
* @param onProgress - Optional callback for loading progress
|
|
443
|
+
* @returns Promise that resolves when all segments are cached
|
|
444
|
+
* @public
|
|
445
|
+
*/
|
|
446
|
+
async prefetchMainVideoSegments(timestamps, onProgress) {
|
|
447
|
+
const mediaEngine = await this.mediaEngineTask.taskComplete;
|
|
448
|
+
if (!mediaEngine) {
|
|
449
|
+
log("prefetchMainVideoSegments: no media engine available");
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
const videoRendition = mediaEngine.getVideoRendition?.() || mediaEngine.videoRendition;
|
|
453
|
+
if (!videoRendition) {
|
|
454
|
+
log("prefetchMainVideoSegments: no video rendition available");
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
const segmentIds = /* @__PURE__ */ new Set();
|
|
458
|
+
for (const ts of timestamps) {
|
|
459
|
+
const segmentId = mediaEngine.computeSegmentId(ts, videoRendition);
|
|
460
|
+
if (segmentId !== void 0) segmentIds.add(segmentId);
|
|
461
|
+
}
|
|
462
|
+
if (segmentIds.size === 0) {
|
|
463
|
+
log("prefetchMainVideoSegments: no segments to prefetch");
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
const uncachedSegmentIds = [];
|
|
467
|
+
for (const segmentId of segmentIds) if (!mediaEngine.isSegmentCached(segmentId, videoRendition)) uncachedSegmentIds.push(segmentId);
|
|
468
|
+
if (uncachedSegmentIds.length === 0) {
|
|
469
|
+
log("prefetchMainVideoSegments: all segments already cached");
|
|
470
|
+
onProgress?.(segmentIds.size, segmentIds.size);
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
log(`prefetchMainVideoSegments: fetching ${uncachedSegmentIds.length} segments...`);
|
|
474
|
+
try {
|
|
475
|
+
await mediaEngine.fetchInitSegment(videoRendition);
|
|
476
|
+
} catch (error) {
|
|
477
|
+
log("prefetchMainVideoSegments: failed to fetch init segment", error);
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
let loaded = segmentIds.size - uncachedSegmentIds.length;
|
|
481
|
+
for (const segmentId of uncachedSegmentIds) try {
|
|
482
|
+
await mediaEngine.fetchMediaSegment(segmentId, videoRendition);
|
|
483
|
+
loaded++;
|
|
484
|
+
onProgress?.(loaded, segmentIds.size);
|
|
485
|
+
} catch (error) {
|
|
486
|
+
log(`prefetchMainVideoSegments: failed to fetch segment ${segmentId}`, error);
|
|
487
|
+
}
|
|
488
|
+
log(`prefetchMainVideoSegments: complete (${loaded}/${segmentIds.size} segments)`);
|
|
349
489
|
}
|
|
350
490
|
/**
|
|
351
491
|
* Clean up resources when component is disconnected
|
|
@@ -360,6 +500,20 @@ let EFVideo = class EFVideo$1 extends TWMixin(EFMedia) {
|
|
|
360
500
|
didBecomeChild() {
|
|
361
501
|
super.didBecomeChild();
|
|
362
502
|
}
|
|
503
|
+
/**
|
|
504
|
+
* Get the natural dimensions of the video (coded width and height).
|
|
505
|
+
* Returns null if the video hasn't loaded yet or canvas isn't available.
|
|
506
|
+
*
|
|
507
|
+
* @public
|
|
508
|
+
*/
|
|
509
|
+
getNaturalDimensions() {
|
|
510
|
+
const canvas = this.canvasElement;
|
|
511
|
+
if (!canvas || canvas.width === 0 || canvas.height === 0) return null;
|
|
512
|
+
return {
|
|
513
|
+
width: canvas.width,
|
|
514
|
+
height: canvas.height
|
|
515
|
+
};
|
|
516
|
+
}
|
|
363
517
|
};
|
|
364
518
|
__decorate([property({
|
|
365
519
|
type: Number,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EFVideo.js","names":["EFVideo"],"sources":["../../src/elements/EFVideo.ts"],"sourcesContent":["import { Task } from \"@lit/task\";\nimport { context, trace } from \"@opentelemetry/api\";\nimport debug from \"debug\";\nimport { css, html, type PropertyValueMap } from \"lit\";\nimport { customElement, property, state } from \"lit/decorators.js\";\nimport { createRef, ref } from \"lit/directives/ref.js\";\nimport { DelayedLoadingState } from \"../DelayedLoadingState.js\";\nimport { TWMixin } from \"../gui/TWMixin.js\";\nimport { withSpan, withSpanSync } from \"../otel/tracingHelpers.js\";\nimport { makeScrubVideoBufferTask } from \"./EFMedia/videoTasks/makeScrubVideoBufferTask.ts\";\nimport { makeScrubVideoInitSegmentFetchTask } from \"./EFMedia/videoTasks/makeScrubVideoInitSegmentFetchTask.ts\";\nimport { makeScrubVideoInputTask } from \"./EFMedia/videoTasks/makeScrubVideoInputTask.ts\";\nimport { makeScrubVideoSeekTask } from \"./EFMedia/videoTasks/makeScrubVideoSeekTask.ts\";\nimport { makeScrubVideoSegmentFetchTask } from \"./EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.ts\";\nimport { makeScrubVideoSegmentIdTask } from \"./EFMedia/videoTasks/makeScrubVideoSegmentIdTask.ts\";\nimport { makeUnifiedVideoSeekTask } from \"./EFMedia/videoTasks/makeUnifiedVideoSeekTask.ts\";\nimport { makeVideoBufferTask } from \"./EFMedia/videoTasks/makeVideoBufferTask.ts\";\nimport { EFMedia } from \"./EFMedia.js\";\nimport { updateAnimations } from \"./updateAnimations.js\";\n\n// EF_FRAMEGEN is a global instance created in EF_FRAMEGEN.ts\ndeclare global {\n var EF_FRAMEGEN: import(\"../EF_FRAMEGEN.js\").EFFramegen;\n}\n\nconst log = debug(\"ef:elements:EFVideo\");\n\ninterface LoadingState {\n isLoading: boolean;\n operation: \"scrub-segment\" | \"video-segment\" | \"seeking\" | \"decoding\" | null;\n message: string;\n}\n\n@customElement(\"ef-video\")\nexport class EFVideo extends TWMixin(EFMedia) {\n static styles = [\n css`\n :host {\n display: block;\n position: relative;\n }\n canvas {\n overflow: hidden;\n position: static;\n width: 100%;\n height: 100%;\n margin: 0;\n padding: 0;\n overflow: hidden;\n border: none;\n outline: none;\n box-shadow: none;\n }\n .loading-overlay {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.6);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10;\n backdrop-filter: blur(2px);\n }\n .loading-content {\n background: rgba(0, 0, 0, 0.8);\n border-radius: 8px;\n padding: 16px 24px;\n display: flex;\n align-items: center;\n gap: 12px;\n color: white;\n font-size: 14px;\n font-weight: 500;\n }\n .loading-spinner {\n width: 20px;\n height: 20px;\n border: 2px solid rgba(255, 255, 255, 0.2);\n border-left: 2px solid #fff;\n border-radius: 50%;\n animation: spin 1s linear infinite;\n }\n @keyframes spin {\n 0% { transform: rotate(0deg); }\n 100% { transform: rotate(360deg); }\n }\n .loading-message {\n font-size: 12px;\n opacity: 0.8;\n }\n `,\n ];\n canvasRef = createRef<HTMLCanvasElement>();\n\n /**\n * Duration in milliseconds for video buffering ahead of current time\n * @domAttribute \"video-buffer-duration\"\n */\n @property({ type: Number, attribute: \"video-buffer-duration\" })\n videoBufferDurationMs = 10000; // 10 seconds - reasonable for JIT encoding\n\n /**\n * Maximum number of concurrent video segment fetches for buffering\n * @domAttribute \"max-video-buffer-fetches\"\n */\n @property({ type: Number, attribute: \"max-video-buffer-fetches\" })\n maxVideoBufferFetches = 2;\n\n /**\n * Enable/disable video buffering system\n * @domAttribute \"enable-video-buffering\"\n */\n @property({ type: Boolean, attribute: \"enable-video-buffering\" })\n enableVideoBuffering = true;\n\n // Unified video system - single smart seek task that routes to scrub or main\n unifiedVideoSeekTask = makeUnifiedVideoSeekTask(this);\n videoBufferTask = makeVideoBufferTask(this); // Keep for main video buffering\n\n // Scrub video preloading system\n scrubVideoBufferTask = makeScrubVideoBufferTask(this);\n scrubVideoInputTask = makeScrubVideoInputTask(this);\n scrubVideoSeekTask = makeScrubVideoSeekTask(this);\n scrubVideoSegmentIdTask = makeScrubVideoSegmentIdTask(this);\n scrubVideoSegmentFetchTask = makeScrubVideoSegmentFetchTask(this);\n scrubVideoInitSegmentFetchTask = makeScrubVideoInitSegmentFetchTask(this);\n\n /**\n * Delayed loading state manager for user feedback\n */\n private delayedLoadingState: DelayedLoadingState;\n\n /**\n * Loading state for user feedback\n */\n @state()\n loadingState = {\n isLoading: false,\n operation: null as LoadingState[\"operation\"],\n message: \"\",\n };\n\n constructor() {\n super();\n\n // Initialize delayed loading state with callback to update UI\n this.delayedLoadingState = new DelayedLoadingState(\n 250,\n (isLoading, message) => {\n this.setLoadingState(isLoading, null, message);\n },\n );\n }\n\n protected updated(\n changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>,\n ): void {\n super.updated(changedProperties);\n\n // No need to clear canvas - displayFrame() overwrites it completely\n // and clearing creates blank frame gaps during transitions\n }\n\n render() {\n return html`\n <canvas ${ref(this.canvasRef)}></canvas>\n ${\n this.loadingState.isLoading\n ? html`\n <div class=\"loading-overlay\">\n <div class=\"loading-content\">\n <div class=\"loading-spinner\"></div>\n <div>\n <div>Loading Video...</div>\n <div class=\"loading-message\">${this.loadingState.message}</div>\n </div>\n </div>\n </div>\n `\n : \"\"\n }\n `;\n }\n\n get canvasElement() {\n const referencedCanvas = this.canvasRef.value;\n if (referencedCanvas) {\n return referencedCanvas;\n }\n const shadowCanvas = this.shadowRoot?.querySelector(\"canvas\");\n if (shadowCanvas) {\n return shadowCanvas;\n }\n return undefined;\n }\n\n frameTask = new Task(this, {\n autoRun: false,\n args: () => [this.desiredSeekTimeMs] as const,\n onError: (error) => {\n console.error(\"frameTask error\", error);\n },\n onComplete: () => {},\n task: async ([_desiredSeekTimeMs], { signal }) => {\n const t0 = performance.now();\n\n await withSpan(\n \"video.frameTask\",\n {\n elementId: this.id || \"unknown\",\n desiredSeekTimeMs: _desiredSeekTimeMs,\n src: this.src || \"none\",\n },\n undefined,\n async (span) => {\n const t1 = performance.now();\n span.setAttribute(\"preworkMs\", t1 - t0);\n\n this.unifiedVideoSeekTask.run();\n const t2 = performance.now();\n span.setAttribute(\"seekRunMs\", t2 - t1);\n\n await this.unifiedVideoSeekTask.taskComplete;\n const t3 = performance.now();\n span.setAttribute(\"seekAwaitMs\", t3 - t2);\n if (signal.aborted) {\n span.setAttribute(\"aborted\", true);\n return;\n }\n\n this.paint(this.desiredSeekTimeMs, span);\n\n if (!this.parentTimegroup) {\n updateAnimations(this);\n }\n\n const t4 = performance.now();\n this.paint(_desiredSeekTimeMs, span);\n const t5 = performance.now();\n span.setAttribute(\"paintMs\", t5 - t4);\n span.setAttribute(\"totalFrameMs\", t5 - t0);\n },\n );\n },\n });\n\n /**\n * Start a delayed loading operation for testing\n */\n startDelayedLoading(\n operationId: string,\n message: string,\n options: { background?: boolean } = {},\n ): void {\n this.delayedLoadingState.startLoading(operationId, message, options);\n }\n\n /**\n * Clear a delayed loading operation for testing\n */\n clearDelayedLoading(operationId: string): void {\n this.delayedLoadingState.clearLoading(operationId);\n }\n\n /**\n * Set loading state for user feedback\n */\n private setLoadingState(\n isLoading: boolean,\n operation: LoadingState[\"operation\"] = null,\n message = \"\",\n ): void {\n this.loadingState = {\n isLoading,\n operation,\n message,\n };\n }\n\n /**\n * Paint the current video frame to canvas\n * Called by frameTask after seek is complete\n */\n paint(seekToMs: number, parentSpan?: any): void {\n const parentContext = parentSpan\n ? trace.setSpan(context.active(), parentSpan)\n : undefined;\n\n withSpanSync(\n \"video.paint\",\n {\n elementId: this.id || \"unknown\",\n seekToMs,\n src: this.src || \"none\",\n },\n parentContext,\n (span) => {\n const t0 = performance.now();\n\n // Check if we're in production rendering mode vs preview mode\n const isProductionRendering = this.isInProductionRenderingMode();\n const t1 = performance.now();\n span.setAttribute(\"isProductionRendering\", isProductionRendering);\n span.setAttribute(\"modeCheckMs\", t1 - t0);\n\n // Unified video system: smart routing to scrub or main, with background upgrades\n // Note: frameTask guarantees unifiedVideoSeekTask is complete before calling paint\n try {\n const t2 = performance.now();\n const videoSample = this.unifiedVideoSeekTask.value;\n span.setAttribute(\"hasVideoSample\", !!videoSample);\n span.setAttribute(\"valueAccessMs\", t2 - t1);\n\n if (videoSample) {\n const t3 = performance.now();\n const videoFrame = videoSample.toVideoFrame();\n const t4 = performance.now();\n span.setAttribute(\"toVideoFrameMs\", t4 - t3);\n\n try {\n const t5 = performance.now();\n this.displayFrame(videoFrame, seekToMs, span);\n const t6 = performance.now();\n span.setAttribute(\"displayFrameMs\", t6 - t5);\n } finally {\n videoFrame.close();\n }\n }\n } catch (error) {\n console.warn(\"Unified video pipeline error:\", error);\n }\n\n // EF_FRAMEGEN-aware rendering mode detection\n if (!isProductionRendering) {\n // Preview mode: skip rendering during initialization to prevent artifacts\n if (\n !this.rootTimegroup ||\n (this.rootTimegroup.currentTimeMs === 0 &&\n this.desiredSeekTimeMs === 0)\n ) {\n span.setAttribute(\"skipped\", \"preview-initialization\");\n return; // Skip initialization frame in preview mode\n }\n // Preview mode: proceed with rendering\n } else {\n // Production rendering mode: only render when EF_FRAMEGEN has explicitly started frame rendering\n // This prevents initialization frames before the actual render sequence begins\n if (!this.rootTimegroup) {\n span.setAttribute(\"skipped\", \"no-root-timegroup\");\n return;\n }\n\n if (!this.isFrameRenderingActive()) {\n span.setAttribute(\"skipped\", \"frame-rendering-not-active\");\n return; // Wait for EF_FRAMEGEN to start frame sequence\n }\n\n // Production mode: EF_FRAMEGEN has started frame sequence, proceed with rendering\n }\n\n const tEnd = performance.now();\n span.setAttribute(\"totalPaintMs\", tEnd - t0);\n },\n );\n }\n\n /**\n * Clear the canvas when element becomes inactive\n */\n clearCanvas(): void {\n if (!this.canvasElement) return;\n\n const ctx = this.canvasElement.getContext(\"2d\");\n if (ctx) {\n ctx.clearRect(0, 0, this.canvasElement.width, this.canvasElement.height);\n }\n }\n\n /**\n * Display a video frame on the canvas\n */\n displayFrame(frame: VideoFrame, seekToMs: number, parentSpan?: any): void {\n const parentContext = parentSpan\n ? trace.setSpan(context.active(), parentSpan)\n : undefined;\n\n withSpanSync(\n \"video.displayFrame\",\n {\n elementId: this.id || \"unknown\",\n seekToMs,\n format: frame.format || \"unknown\",\n width: frame.codedWidth,\n height: frame.codedHeight,\n },\n parentContext,\n (span) => {\n const t0 = performance.now();\n\n log(\"trace: displayFrame start\", {\n seekToMs,\n frameFormat: frame.format,\n });\n\n if (!this.canvasElement) {\n log(\"trace: displayFrame aborted - no canvas element\");\n throw new Error(\n `Frame display failed: Canvas element is not available at time ${seekToMs}ms. The video component may not be properly initialized.`,\n );\n }\n const t1 = performance.now();\n span.setAttribute(\"getCanvasMs\", Math.round((t1 - t0) * 100) / 100);\n\n const ctx = this.canvasElement.getContext(\"2d\");\n const t2 = performance.now();\n span.setAttribute(\"getCtxMs\", Math.round((t2 - t1) * 100) / 100);\n\n if (!ctx) {\n log(\"trace: displayFrame aborted - no canvas context\");\n throw new Error(\n `Frame display failed: Unable to get 2D canvas context at time ${seekToMs}ms. This may indicate a browser compatibility issue or canvas corruption.`,\n );\n }\n\n let resized = false;\n if (frame?.codedWidth && frame?.codedHeight) {\n if (\n this.canvasElement.width !== frame.codedWidth ||\n this.canvasElement.height !== frame.codedHeight\n ) {\n log(\"trace: updating canvas dimensions\", {\n width: frame.codedWidth,\n height: frame.codedHeight,\n });\n this.canvasElement.width = frame.codedWidth;\n this.canvasElement.height = frame.codedHeight;\n resized = true;\n const t3 = performance.now();\n span.setAttribute(\"resizeMs\", Math.round((t3 - t2) * 100) / 100);\n }\n }\n span.setAttribute(\"canvasResized\", resized);\n\n if (frame.format === null) {\n log(\"trace: displayFrame aborted - null frame format\");\n throw new Error(\n `Frame display failed: Video frame has null format at time ${seekToMs}ms. This indicates corrupted or incompatible video data.`,\n );\n }\n\n const tDrawStart = performance.now();\n ctx.drawImage(\n frame,\n 0,\n 0,\n this.canvasElement.width,\n this.canvasElement.height,\n );\n const tDrawEnd = performance.now();\n span.setAttribute(\n \"drawImageMs\",\n Math.round((tDrawEnd - tDrawStart) * 100) / 100,\n );\n span.setAttribute(\n \"totalDisplayMs\",\n Math.round((tDrawEnd - t0) * 100) / 100,\n );\n span.setAttribute(\"canvasWidth\", this.canvasElement.width);\n span.setAttribute(\"canvasHeight\", this.canvasElement.height);\n\n log(\"trace: frame drawn to canvas\", { seekToMs });\n },\n );\n }\n\n /**\n * Check if we're in production rendering mode (EF_FRAMEGEN active) vs preview mode\n */\n private isInProductionRenderingMode(): boolean {\n // Check if EF_RENDERING function exists and returns true (production rendering)\n if (typeof window.EF_RENDERING === \"function\") {\n return window.EF_RENDERING();\n }\n\n // Check if workbench is in rendering mode\n const workbench = document.querySelector(\"ef-workbench\") as any;\n if (workbench?.rendering) {\n return true;\n }\n\n // Check if EF_FRAMEGEN exists and has render options (indicates active rendering)\n if (window.EF_FRAMEGEN?.renderOptions) {\n return true;\n }\n\n // Default to preview mode\n return false;\n }\n\n /**\n * Check if EF_FRAMEGEN has explicitly started frame rendering (not just initialization)\n */\n private isFrameRenderingActive(): boolean {\n if (!window.EF_FRAMEGEN?.renderOptions) {\n return false;\n }\n\n // In production mode, only render when EF_FRAMEGEN has actually begun frame sequence\n // Check if we're past the initialization phase by looking for explicit frame control\n const renderOptions = window.EF_FRAMEGEN.renderOptions;\n const renderStartTime = renderOptions.encoderOptions.fromMs;\n const currentTime = this.rootTimegroup?.currentTimeMs || 0;\n\n // We're in active frame rendering if:\n // 1. currentTime >= renderStartTime (includes the starting frame)\n return currentTime >= renderStartTime;\n }\n\n /**\n * Legacy getter for fragment index task\n * Still used by EFCaptions - maps to unified video seek task\n */\n get fragmentIndexTask() {\n return this.unifiedVideoSeekTask;\n }\n\n /**\n * Helper method for tests: wait for the current frame to be ready\n * This encapsulates the complexity of ensuring the video has updated\n * and its frameTask has completed.\n *\n * @returns Promise that resolves when the frame is ready\n */\n async waitForFrameReady(): Promise<void> {\n await this.updateComplete;\n await this.frameTask.run();\n }\n\n /**\n * Clean up resources when component is disconnected\n */\n disconnectedCallback(): void {\n super.disconnectedCallback();\n\n // Clean up delayed loading state\n this.delayedLoadingState.clearAllLoading();\n }\n\n didBecomeRoot() {\n super.didBecomeRoot();\n }\n didBecomeChild() {\n super.didBecomeChild();\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"ef-video\": EFVideo;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"EFVideo.js","names":["EFVideo","uncachedSegmentIds: number[]"],"sources":["../../src/elements/EFVideo.ts"],"sourcesContent":["import { Task } from \"@lit/task\";\nimport { context, trace } from \"@opentelemetry/api\";\nimport debug from \"debug\";\nimport { css, html, type PropertyValueMap } from \"lit\";\nimport { customElement, property, state } from \"lit/decorators.js\";\nimport { createRef, ref } from \"lit/directives/ref.js\";\nimport { DelayedLoadingState } from \"../DelayedLoadingState.js\";\nimport { TWMixin } from \"../gui/TWMixin.js\";\nimport { withSpan, withSpanSync } from \"../otel/tracingHelpers.js\";\nimport { makeScrubVideoBufferTask } from \"./EFMedia/videoTasks/makeScrubVideoBufferTask.ts\";\nimport { makeScrubVideoInitSegmentFetchTask } from \"./EFMedia/videoTasks/makeScrubVideoInitSegmentFetchTask.ts\";\nimport { makeScrubVideoInputTask } from \"./EFMedia/videoTasks/makeScrubVideoInputTask.ts\";\nimport { makeScrubVideoSeekTask } from \"./EFMedia/videoTasks/makeScrubVideoSeekTask.ts\";\nimport { makeScrubVideoSegmentFetchTask } from \"./EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.ts\";\nimport { makeScrubVideoSegmentIdTask } from \"./EFMedia/videoTasks/makeScrubVideoSegmentIdTask.ts\";\nimport { makeUnifiedVideoSeekTask } from \"./EFMedia/videoTasks/makeUnifiedVideoSeekTask.ts\";\nimport { makeVideoBufferTask } from \"./EFMedia/videoTasks/makeVideoBufferTask.ts\";\nimport { EFMedia } from \"./EFMedia.js\";\nimport { updateAnimations } from \"./updateAnimations.js\";\n\n// EF_FRAMEGEN is a global instance created in EF_FRAMEGEN.ts\ndeclare global {\n var EF_FRAMEGEN: import(\"../EF_FRAMEGEN.js\").EFFramegen;\n}\n\nconst log = debug(\"ef:elements:EFVideo\");\n\ninterface LoadingState {\n isLoading: boolean;\n operation: \"scrub-segment\" | \"video-segment\" | \"seeking\" | \"decoding\" | null;\n message: string;\n}\n\n/**\n * Event detail for scrub segment loading progress.\n * Dispatched during prefetchScrubSegments to indicate network activity.\n */\nexport interface ScrubSegmentLoadingDetail {\n /** The segment ID being loaded (0-indexed) */\n segmentId: number;\n /** Time range covered by this segment [startMs, endMs] */\n timeRangeMs: [number, number];\n /** Number of segments loaded so far */\n loaded: number;\n /** Total number of segments to load */\n total: number;\n /** Current status: \"loading\" or \"loaded\" */\n status: \"loading\" | \"loaded\";\n}\n\n@customElement(\"ef-video\")\nexport class EFVideo extends TWMixin(EFMedia) {\n static styles = [\n css`\n :host {\n display: block;\n position: relative;\n }\n canvas {\n overflow: hidden;\n position: static;\n width: 100%;\n height: 100%;\n margin: 0;\n padding: 0;\n overflow: hidden;\n border: none;\n outline: none;\n box-shadow: none;\n }\n .loading-overlay {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.6);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10;\n backdrop-filter: blur(2px);\n }\n .loading-content {\n background: rgba(0, 0, 0, 0.8);\n border-radius: 8px;\n padding: 16px 24px;\n display: flex;\n align-items: center;\n gap: 12px;\n color: white;\n font-size: 14px;\n font-weight: 500;\n }\n .loading-spinner {\n width: 20px;\n height: 20px;\n border: 2px solid rgba(255, 255, 255, 0.2);\n border-left: 2px solid #fff;\n border-radius: 50%;\n animation: spin 1s linear infinite;\n }\n @keyframes spin {\n 0% { transform: rotate(0deg); }\n 100% { transform: rotate(360deg); }\n }\n .loading-message {\n font-size: 12px;\n opacity: 0.8;\n }\n `,\n ];\n canvasRef = createRef<HTMLCanvasElement>();\n\n /**\n * Duration in milliseconds for video buffering ahead of current time\n * @domAttribute \"video-buffer-duration\"\n */\n @property({ type: Number, attribute: \"video-buffer-duration\" })\n videoBufferDurationMs = 10000; // 10 seconds - reasonable for JIT encoding\n\n /**\n * Maximum number of concurrent video segment fetches for buffering\n * @domAttribute \"max-video-buffer-fetches\"\n */\n @property({ type: Number, attribute: \"max-video-buffer-fetches\" })\n maxVideoBufferFetches = 2;\n\n /**\n * Enable/disable video buffering system\n * @domAttribute \"enable-video-buffering\"\n */\n @property({ type: Boolean, attribute: \"enable-video-buffering\" })\n enableVideoBuffering = true;\n\n // Unified video system - single smart seek task that routes to scrub or main\n unifiedVideoSeekTask = makeUnifiedVideoSeekTask(this);\n videoBufferTask = makeVideoBufferTask(this); // Keep for main video buffering\n\n // Scrub video preloading system\n scrubVideoBufferTask = makeScrubVideoBufferTask(this);\n scrubVideoInputTask = makeScrubVideoInputTask(this);\n scrubVideoSeekTask = makeScrubVideoSeekTask(this);\n scrubVideoSegmentIdTask = makeScrubVideoSegmentIdTask(this);\n scrubVideoSegmentFetchTask = makeScrubVideoSegmentFetchTask(this);\n scrubVideoInitSegmentFetchTask = makeScrubVideoInitSegmentFetchTask(this);\n\n /**\n * Delayed loading state manager for user feedback\n */\n private delayedLoadingState: DelayedLoadingState;\n\n /**\n * Loading state for user feedback\n */\n @state()\n loadingState = {\n isLoading: false,\n operation: null as LoadingState[\"operation\"],\n message: \"\",\n };\n\n constructor() {\n super();\n\n // Initialize delayed loading state with callback to update UI\n this.delayedLoadingState = new DelayedLoadingState(\n 250,\n (isLoading, message) => {\n this.setLoadingState(isLoading, null, message);\n },\n );\n }\n\n protected updated(\n changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>,\n ): void {\n super.updated(changedProperties);\n\n // No need to clear canvas - displayFrame() overwrites it completely\n // and clearing creates blank frame gaps during transitions\n }\n\n render() {\n return html`\n <canvas ${ref(this.canvasRef)}></canvas>\n ${\n this.loadingState.isLoading\n ? html`\n <div class=\"loading-overlay\">\n <div class=\"loading-content\">\n <div class=\"loading-spinner\"></div>\n <div>\n <div>Loading Video...</div>\n <div class=\"loading-message\">${this.loadingState.message}</div>\n </div>\n </div>\n </div>\n `\n : \"\"\n }\n `;\n }\n\n get canvasElement() {\n const referencedCanvas = this.canvasRef.value;\n if (referencedCanvas) {\n return referencedCanvas;\n }\n const shadowCanvas = this.shadowRoot?.querySelector(\"canvas\");\n if (shadowCanvas) {\n return shadowCanvas;\n }\n return undefined;\n }\n\n frameTask = new Task(this, {\n autoRun: false,\n args: () => [this.desiredSeekTimeMs] as const,\n onError: (error) => {\n // CRITICAL: Attach .catch() handler to taskComplete BEFORE the promise is rejected.\n // This prevents unhandled rejection when hostUpdate() triggers _performTask() without awaiting.\n this.frameTask.taskComplete.catch(() => {});\n \n // Don't log AbortErrors - these are expected when tasks are cancelled\n const isAbortError = \n (error instanceof DOMException && error.name === \"AbortError\") ||\n (error instanceof Error && (\n error.name === \"AbortError\" ||\n error.message?.includes(\"signal is aborted\") ||\n error.message?.includes(\"The user aborted a request\")\n ));\n \n if (isAbortError) {\n return;\n }\n \n // Only log unexpected errors - expected conditions handled gracefully\n if (\n error instanceof Error &&\n !error.message.includes(\"Video rendition unavailable\") &&\n !error.message.includes(\"No valid media source\") &&\n !error.message.includes(\"Sample not found for time\") // Seeking beyond video duration\n ) {\n console.error(\"frameTask error\", error);\n }\n },\n onComplete: () => {},\n task: async ([_desiredSeekTimeMs], { signal }) => {\n const t0 = performance.now();\n\n await withSpan(\n \"video.frameTask\",\n {\n elementId: this.id || \"unknown\",\n desiredSeekTimeMs: _desiredSeekTimeMs,\n src: this.src || \"none\",\n },\n undefined,\n async (span) => {\n const t1 = performance.now();\n span.setAttribute(\"preworkMs\", t1 - t0);\n\n // Attach .catch() to prevent unhandled rejection - errors handled via taskComplete\n this.unifiedVideoSeekTask.run().catch(() => {});\n const t2 = performance.now();\n span.setAttribute(\"seekRunMs\", t2 - t1);\n\n try {\n await this.unifiedVideoSeekTask.taskComplete;\n } catch (error) {\n // If aborted, check our signal and return early if it's also aborted\n if (error instanceof DOMException && error.name === \"AbortError\") {\n signal?.throwIfAborted();\n return; // Our signal not aborted, but seek task was - exit gracefully\n }\n throw error;\n }\n const t3 = performance.now();\n span.setAttribute(\"seekAwaitMs\", t3 - t2);\n // Check abort after async operation\n signal?.throwIfAborted();\n\n const t4 = performance.now();\n this.paint(_desiredSeekTimeMs, span);\n const t5 = performance.now();\n span.setAttribute(\"paintMs\", t5 - t4);\n\n if (!this.parentTimegroup) {\n updateAnimations(this);\n }\n\n span.setAttribute(\"totalFrameMs\", t5 - t0);\n },\n );\n },\n });\n\n /**\n * Start a delayed loading operation for testing\n */\n startDelayedLoading(\n operationId: string,\n message: string,\n options: { background?: boolean } = {},\n ): void {\n this.delayedLoadingState.startLoading(operationId, message, options);\n }\n\n /**\n * Clear a delayed loading operation for testing\n */\n clearDelayedLoading(operationId: string): void {\n this.delayedLoadingState.clearLoading(operationId);\n }\n\n /**\n * Set loading state for user feedback\n */\n private setLoadingState(\n isLoading: boolean,\n operation: LoadingState[\"operation\"] = null,\n message = \"\",\n ): void {\n this.loadingState = {\n isLoading,\n operation,\n message,\n };\n }\n\n /**\n * Paint the current video frame to canvas\n * Called by frameTask after seek is complete\n */\n paint(seekToMs: number, parentSpan?: any): void {\n const parentContext = parentSpan\n ? trace.setSpan(context.active(), parentSpan)\n : undefined;\n\n withSpanSync(\n \"video.paint\",\n {\n elementId: this.id || \"unknown\",\n seekToMs,\n src: this.src || \"none\",\n },\n parentContext,\n (span) => {\n const t0 = performance.now();\n\n // Check if we're in production rendering mode vs preview mode\n const isProductionRendering = this.isInProductionRenderingMode();\n const t1 = performance.now();\n span.setAttribute(\"isProductionRendering\", isProductionRendering);\n span.setAttribute(\"modeCheckMs\", t1 - t0);\n\n // Unified video system: smart routing to scrub or main, with background upgrades\n // Note: frameTask guarantees unifiedVideoSeekTask is complete before calling paint\n try {\n const t2 = performance.now();\n const videoSample = this.unifiedVideoSeekTask.value;\n span.setAttribute(\"hasVideoSample\", !!videoSample);\n span.setAttribute(\"valueAccessMs\", t2 - t1);\n\n if (videoSample) {\n const t3 = performance.now();\n const videoFrame = videoSample.toVideoFrame();\n const t4 = performance.now();\n span.setAttribute(\"toVideoFrameMs\", t4 - t3);\n\n try {\n const t5 = performance.now();\n this.displayFrame(videoFrame, seekToMs, span);\n const t6 = performance.now();\n span.setAttribute(\"displayFrameMs\", t6 - t5);\n } finally {\n videoFrame.close();\n }\n }\n } catch (error) {\n console.warn(\"Unified video pipeline error:\", error);\n }\n\n // EF_FRAMEGEN-aware rendering mode detection\n if (!isProductionRendering) {\n // Check if we're in a render clone (used for thumbnails, video export, etc.)\n // Render clones should ALWAYS render, even at time 0\n const isInRenderClone = !!this.closest('.ef-render-clone-container');\n \n if (isInRenderClone) {\n span.setAttribute(\"renderClone\", true);\n }\n \n // Preview mode: skip rendering during initialization to prevent artifacts\n // BUT: Always render if we're in a render clone (for thumbnails/export)\n if (\n !isInRenderClone &&\n (!this.rootTimegroup ||\n (this.rootTimegroup.currentTimeMs === 0 &&\n this.desiredSeekTimeMs === 0))\n ) {\n span.setAttribute(\"skipped\", \"preview-initialization\");\n return; // Skip initialization frame in preview mode\n }\n // Preview mode: proceed with rendering\n } else {\n // Production rendering mode: only render when EF_FRAMEGEN has explicitly started frame rendering\n // This prevents initialization frames before the actual render sequence begins\n if (!this.rootTimegroup) {\n span.setAttribute(\"skipped\", \"no-root-timegroup\");\n return;\n }\n\n if (!this.isFrameRenderingActive()) {\n span.setAttribute(\"skipped\", \"frame-rendering-not-active\");\n return; // Wait for EF_FRAMEGEN to start frame sequence\n }\n\n // Production mode: EF_FRAMEGEN has started frame sequence, proceed with rendering\n }\n\n const tEnd = performance.now();\n span.setAttribute(\"totalPaintMs\", tEnd - t0);\n },\n );\n }\n\n /**\n * Clear the canvas when element becomes inactive\n */\n clearCanvas(): void {\n if (!this.canvasElement) return;\n\n const ctx = this.canvasElement.getContext(\"2d\");\n if (ctx) {\n ctx.clearRect(0, 0, this.canvasElement.width, this.canvasElement.height);\n }\n }\n\n /**\n * Display a video frame on the canvas\n */\n displayFrame(frame: VideoFrame, seekToMs: number, parentSpan?: any): void {\n const parentContext = parentSpan\n ? trace.setSpan(context.active(), parentSpan)\n : undefined;\n\n withSpanSync(\n \"video.displayFrame\",\n {\n elementId: this.id || \"unknown\",\n seekToMs,\n format: frame.format || \"unknown\",\n width: frame.codedWidth,\n height: frame.codedHeight,\n },\n parentContext,\n (span) => {\n const t0 = performance.now();\n\n log(\"trace: displayFrame start\", {\n seekToMs,\n frameFormat: frame.format,\n });\n\n if (!this.canvasElement) {\n log(\"trace: displayFrame aborted - no canvas element\");\n throw new Error(\n `Frame display failed: Canvas element is not available at time ${seekToMs}ms. The video component may not be properly initialized.`,\n );\n }\n const t1 = performance.now();\n span.setAttribute(\"getCanvasMs\", Math.round((t1 - t0) * 100) / 100);\n\n const ctx = this.canvasElement.getContext(\"2d\");\n const t2 = performance.now();\n span.setAttribute(\"getCtxMs\", Math.round((t2 - t1) * 100) / 100);\n\n if (!ctx) {\n log(\"trace: displayFrame aborted - no canvas context\");\n throw new Error(\n `Frame display failed: Unable to get 2D canvas context at time ${seekToMs}ms. This may indicate a browser compatibility issue or canvas corruption.`,\n );\n }\n\n let resized = false;\n if (frame?.codedWidth && frame?.codedHeight) {\n if (\n this.canvasElement.width !== frame.codedWidth ||\n this.canvasElement.height !== frame.codedHeight\n ) {\n log(\"trace: updating canvas dimensions\", {\n width: frame.codedWidth,\n height: frame.codedHeight,\n });\n this.canvasElement.width = frame.codedWidth;\n this.canvasElement.height = frame.codedHeight;\n resized = true;\n const t3 = performance.now();\n span.setAttribute(\"resizeMs\", Math.round((t3 - t2) * 100) / 100);\n }\n }\n span.setAttribute(\"canvasResized\", resized);\n\n if (frame.format === null) {\n log(\"trace: displayFrame aborted - null frame format\");\n throw new Error(\n `Frame display failed: Video frame has null format at time ${seekToMs}ms. This indicates corrupted or incompatible video data.`,\n );\n }\n\n const tDrawStart = performance.now();\n ctx.drawImage(\n frame,\n 0,\n 0,\n this.canvasElement.width,\n this.canvasElement.height,\n );\n const tDrawEnd = performance.now();\n span.setAttribute(\n \"drawImageMs\",\n Math.round((tDrawEnd - tDrawStart) * 100) / 100,\n );\n span.setAttribute(\n \"totalDisplayMs\",\n Math.round((tDrawEnd - t0) * 100) / 100,\n );\n span.setAttribute(\"canvasWidth\", this.canvasElement.width);\n span.setAttribute(\"canvasHeight\", this.canvasElement.height);\n\n log(\"trace: frame drawn to canvas\", { seekToMs });\n },\n );\n }\n\n /**\n * Check if we're in production rendering mode (EF_FRAMEGEN active) vs preview mode\n */\n private isInProductionRenderingMode(): boolean {\n // Check if EF_RENDERING function exists and returns true (production rendering)\n if (typeof window.EF_RENDERING === \"function\") {\n return window.EF_RENDERING();\n }\n\n // Check if workbench is in rendering mode\n const workbench = document.querySelector(\"ef-workbench\") as any;\n if (workbench?.rendering) {\n return true;\n }\n\n // Check if EF_FRAMEGEN exists and has render options (indicates active rendering)\n if (window.EF_FRAMEGEN?.renderOptions) {\n return true;\n }\n\n // Default to preview mode\n return false;\n }\n\n /**\n * Check if EF_FRAMEGEN has explicitly started frame rendering (not just initialization)\n */\n private isFrameRenderingActive(): boolean {\n if (!window.EF_FRAMEGEN?.renderOptions) {\n return false;\n }\n\n // In production mode, only render when EF_FRAMEGEN has actually begun frame sequence\n // Check if we're past the initialization phase by looking for explicit frame control\n const renderOptions = window.EF_FRAMEGEN.renderOptions;\n const renderStartTime = renderOptions.encoderOptions.fromMs;\n const currentTime = this.rootTimegroup?.currentTimeMs || 0;\n\n // We're in active frame rendering if:\n // 1. currentTime >= renderStartTime (includes the starting frame)\n return currentTime >= renderStartTime;\n }\n\n /**\n * Legacy getter for fragment index task\n * Still used by EFCaptions - maps to unified video seek task\n */\n get fragmentIndexTask() {\n return this.unifiedVideoSeekTask;\n }\n\n /**\n * Helper method for tests: wait for the current frame to be ready\n * This encapsulates the complexity of ensuring the video has updated\n * and its frameTask has completed.\n *\n * @returns Promise that resolves when the frame is ready\n */\n async waitForFrameReady(): Promise<void> {\n // CRITICAL: Sync desiredSeekTimeMs immediately from currentSourceTimeMs\n // The update cycle may not have processed yet, but currentSourceTimeMs\n // is a getter that already reflects the correct time from the parent.\n const currentTime = this.currentSourceTimeMs;\n if (this.desiredSeekTimeMs !== currentTime) {\n this.desiredSeekTimeMs = currentTime;\n }\n await this.updateComplete;\n \n try {\n await this.frameTask.run();\n } catch (error) {\n // AbortErrors are expected when element is disconnected or task is cancelled\n // Return gracefully instead of propagating the error\n const isAbortError = \n error instanceof DOMException && error.name === \"AbortError\" ||\n error instanceof Error && (\n error.name === \"AbortError\" ||\n error.message?.includes(\"signal is aborted\") ||\n error.message?.includes(\"The user aborted a request\")\n );\n \n if (isAbortError) {\n return;\n }\n throw error;\n }\n }\n\n /**\n * Pre-fetch scrub segments for given timestamps.\n * Loads 30-second segments sequentially, emitting progress events.\n * This ensures scrub track is cached for fast thumbnail generation.\n *\n * @param timestamps - Array of timestamps (in ms) that will be captured\n * @param onProgress - Optional callback for loading progress\n * @returns Promise that resolves when all segments are cached\n * @public\n */\n async prefetchScrubSegments(\n timestamps: number[],\n onProgress?: (loaded: number, total: number, segmentTimeRange: [number, number]) => void,\n ): Promise<void> {\n // Wait for media engine to be ready\n const mediaEngine = await this.mediaEngineTask.taskComplete;\n if (!mediaEngine) {\n log(\"prefetchScrubSegments: no media engine available\");\n return;\n }\n\n // Get scrub rendition\n const scrubRendition = mediaEngine.getScrubVideoRendition();\n if (!scrubRendition) {\n log(\"prefetchScrubSegments: no scrub rendition available\");\n return;\n }\n\n const scrubRenditionWithSrc = {\n ...scrubRendition,\n src: mediaEngine.src,\n };\n\n // Compute unique segment IDs needed for all timestamps\n const segmentIds = new Set<number>();\n for (const ts of timestamps) {\n const segmentId = mediaEngine.computeSegmentId(ts, scrubRenditionWithSrc);\n if (segmentId !== undefined) {\n segmentIds.add(segmentId);\n }\n }\n\n if (segmentIds.size === 0) {\n log(\"prefetchScrubSegments: no segments to prefetch\");\n return;\n }\n\n // For AssetMediaEngine, the scrub track is a single file (not segmented).\n // We just need to fetch it once, and all segments become cached.\n // Check if ANY segment is already cached (meaning the file is loaded).\n const firstSegmentId = Array.from(segmentIds)[0]!;\n if (mediaEngine.isSegmentCached(firstSegmentId, scrubRenditionWithSrc)) {\n log(\"prefetchScrubSegments: scrub track already cached\");\n return;\n }\n\n log(`prefetchScrubSegments: fetching scrub track for ${segmentIds.size} segments...`);\n\n // Emit loading event for the entire duration\n const durationMs = mediaEngine.durationMs || 0;\n this.dispatchEvent(\n new CustomEvent(\"scrub-segment-loading\", {\n detail: {\n segmentId: 0,\n timeRangeMs: [0, durationMs] as [number, number],\n loaded: 0,\n total: 1,\n status: \"loading\",\n },\n bubbles: true,\n composed: true,\n }),\n );\n\n // Fetch the scrub track (single file for all segments)\n try {\n await mediaEngine.fetchMediaSegment(firstSegmentId, scrubRenditionWithSrc);\n log(`prefetchScrubSegments: scrub track loaded`);\n } catch (error) {\n log(`prefetchScrubSegments: failed to load scrub track`, error);\n }\n\n // Emit loaded event\n this.dispatchEvent(\n new CustomEvent(\"scrub-segment-loading\", {\n detail: {\n segmentId: 0,\n timeRangeMs: [0, durationMs] as [number, number],\n loaded: 1,\n total: 1,\n status: \"loaded\",\n },\n bubbles: true,\n composed: true,\n }),\n );\n\n // Report progress\n onProgress?.(1, 1, [0, durationMs]);\n log(`prefetchScrubSegments: complete`);\n }\n\n /**\n * Pre-fetch main video segments for given timestamps.\n * This ensures segments are cached for fast seeking during video export.\n *\n * @param timestamps - Array of timestamps (in ms) that will be captured\n * @param onProgress - Optional callback for loading progress\n * @returns Promise that resolves when all segments are cached\n * @public\n */\n async prefetchMainVideoSegments(\n timestamps: number[],\n onProgress?: (loaded: number, total: number) => void,\n ): Promise<void> {\n // Wait for media engine to be ready\n const mediaEngine = await this.mediaEngineTask.taskComplete;\n if (!mediaEngine) {\n log(\"prefetchMainVideoSegments: no media engine available\");\n return;\n }\n\n // Get main video rendition\n const videoRendition = mediaEngine.getVideoRendition?.() || mediaEngine.videoRendition;\n if (!videoRendition) {\n log(\"prefetchMainVideoSegments: no video rendition available\");\n return;\n }\n\n // Compute unique segment IDs needed for all timestamps\n const segmentIds = new Set<number>();\n for (const ts of timestamps) {\n const segmentId = mediaEngine.computeSegmentId(ts, videoRendition);\n if (segmentId !== undefined) {\n segmentIds.add(segmentId);\n }\n }\n\n if (segmentIds.size === 0) {\n log(\"prefetchMainVideoSegments: no segments to prefetch\");\n return;\n }\n\n // Filter to segments not already cached\n const uncachedSegmentIds: number[] = [];\n for (const segmentId of segmentIds) {\n if (!mediaEngine.isSegmentCached(segmentId, videoRendition)) {\n uncachedSegmentIds.push(segmentId);\n }\n }\n\n if (uncachedSegmentIds.length === 0) {\n log(\"prefetchMainVideoSegments: all segments already cached\");\n onProgress?.(segmentIds.size, segmentIds.size);\n return;\n }\n\n log(`prefetchMainVideoSegments: fetching ${uncachedSegmentIds.length} segments...`);\n\n // Fetch init segment first (needed for all media segments)\n try {\n await (mediaEngine as any).fetchInitSegment(videoRendition);\n } catch (error) {\n log(\"prefetchMainVideoSegments: failed to fetch init segment\", error);\n return;\n }\n\n // Fetch media segments sequentially to avoid overwhelming the network\n let loaded = segmentIds.size - uncachedSegmentIds.length;\n for (const segmentId of uncachedSegmentIds) {\n try {\n await mediaEngine.fetchMediaSegment(segmentId, videoRendition);\n loaded++;\n onProgress?.(loaded, segmentIds.size);\n } catch (error) {\n log(`prefetchMainVideoSegments: failed to fetch segment ${segmentId}`, error);\n // Continue with other segments\n }\n }\n\n log(`prefetchMainVideoSegments: complete (${loaded}/${segmentIds.size} segments)`);\n }\n\n /**\n * Clean up resources when component is disconnected\n */\n disconnectedCallback(): void {\n super.disconnectedCallback();\n\n // Clean up delayed loading state\n this.delayedLoadingState.clearAllLoading();\n }\n\n didBecomeRoot() {\n super.didBecomeRoot();\n }\n didBecomeChild() {\n super.didBecomeChild();\n }\n\n /**\n * Get the natural dimensions of the video (coded width and height).\n * Returns null if the video hasn't loaded yet or canvas isn't available.\n *\n * @public\n */\n getNaturalDimensions(): { width: number; height: number } | null {\n const canvas = this.canvasElement;\n if (!canvas || canvas.width === 0 || canvas.height === 0) {\n return null;\n }\n return {\n width: canvas.width,\n height: canvas.height,\n };\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"ef-video\": EFVideo;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAyBA,MAAM,MAAM,MAAM,sBAAsB;AA0BjC,oBAAMA,kBAAgB,QAAQ,QAAQ,CAAC;;gBAC5B,CACd,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MA0DJ;;CAmDD,cAAc;AACZ,SAAO;mBAnDG,WAA8B;+BAOlB;+BAOA;8BAOD;8BAGA,yBAAyB,KAAK;yBACnC,oBAAoB,KAAK;8BAGpB,yBAAyB,KAAK;6BAC/B,wBAAwB,KAAK;4BAC9B,uBAAuB,KAAK;iCACvB,4BAA4B,KAAK;oCAC9B,+BAA+B,KAAK;wCAChC,mCAAmC,KAAK;sBAW1D;GACb,WAAW;GACX,WAAW;GACX,SAAS;GACV;mBAwDW,IAAI,KAAK,MAAM;GACzB,SAAS;GACT,YAAY,CAAC,KAAK,kBAAkB;GACpC,UAAU,UAAU;AAGlB,SAAK,UAAU,aAAa,YAAY,GAAG;AAW3C,QAPG,iBAAiB,gBAAgB,MAAM,SAAS,gBAChD,iBAAiB,UAChB,MAAM,SAAS,gBACf,MAAM,SAAS,SAAS,oBAAoB,IAC5C,MAAM,SAAS,SAAS,6BAA6B,EAIvD;AAIF,QACE,iBAAiB,SACjB,CAAC,MAAM,QAAQ,SAAS,8BAA8B,IACtD,CAAC,MAAM,QAAQ,SAAS,wBAAwB,IAChD,CAAC,MAAM,QAAQ,SAAS,4BAA4B,CAEpD,SAAQ,MAAM,mBAAmB,MAAM;;GAG3C,kBAAkB;GAClB,MAAM,OAAO,CAAC,qBAAqB,EAAE,aAAa;IAChD,MAAM,KAAK,YAAY,KAAK;AAE5B,UAAM,SACJ,mBACA;KACE,WAAW,KAAK,MAAM;KACtB,mBAAmB;KACnB,KAAK,KAAK,OAAO;KAClB,EACD,QACA,OAAO,SAAS;KACd,MAAM,KAAK,YAAY,KAAK;AAC5B,UAAK,aAAa,aAAa,KAAK,GAAG;AAGvC,UAAK,qBAAqB,KAAK,CAAC,YAAY,GAAG;KAC/C,MAAM,KAAK,YAAY,KAAK;AAC5B,UAAK,aAAa,aAAa,KAAK,GAAG;AAEvC,SAAI;AACF,YAAM,KAAK,qBAAqB;cACzB,OAAO;AAEd,UAAI,iBAAiB,gBAAgB,MAAM,SAAS,cAAc;AAChE,eAAQ,gBAAgB;AACxB;;AAEF,YAAM;;KAER,MAAM,KAAK,YAAY,KAAK;AAC5B,UAAK,aAAa,eAAe,KAAK,GAAG;AAEzC,aAAQ,gBAAgB;KAExB,MAAM,KAAK,YAAY,KAAK;AAC5B,UAAK,MAAM,oBAAoB,KAAK;KACpC,MAAM,KAAK,YAAY,KAAK;AAC5B,UAAK,aAAa,WAAW,KAAK,GAAG;AAErC,SAAI,CAAC,KAAK,gBACR,kBAAiB,KAAK;AAGxB,UAAK,aAAa,gBAAgB,KAAK,GAAG;MAE7C;;GAEJ,CAAC;AAlIA,OAAK,sBAAsB,IAAI,oBAC7B,MACC,WAAW,YAAY;AACtB,QAAK,gBAAgB,WAAW,MAAM,QAAQ;IAEjD;;CAGH,AAAU,QACR,mBACM;AACN,QAAM,QAAQ,kBAAkB;;CAMlC,SAAS;AACP,SAAO,IAAI;gBACC,IAAI,KAAK,UAAU,CAAC;QAE5B,KAAK,aAAa,YACd,IAAI;;;;;;6CAM6B,KAAK,aAAa,QAAQ;;;;UAK3D,GACL;;;CAIL,IAAI,gBAAgB;EAClB,MAAM,mBAAmB,KAAK,UAAU;AACxC,MAAI,iBACF,QAAO;EAET,MAAM,eAAe,KAAK,YAAY,cAAc,SAAS;AAC7D,MAAI,aACF,QAAO;;;;;CA0FX,oBACE,aACA,SACA,UAAoC,EAAE,EAChC;AACN,OAAK,oBAAoB,aAAa,aAAa,SAAS,QAAQ;;;;;CAMtE,oBAAoB,aAA2B;AAC7C,OAAK,oBAAoB,aAAa,YAAY;;;;;CAMpD,AAAQ,gBACN,WACA,YAAuC,MACvC,UAAU,IACJ;AACN,OAAK,eAAe;GAClB;GACA;GACA;GACD;;;;;;CAOH,MAAM,UAAkB,YAAwB;EAC9C,MAAM,gBAAgB,aAClB,MAAM,QAAQ,QAAQ,QAAQ,EAAE,WAAW,GAC3C;AAEJ,eACE,eACA;GACE,WAAW,KAAK,MAAM;GACtB;GACA,KAAK,KAAK,OAAO;GAClB,EACD,gBACC,SAAS;GACR,MAAM,KAAK,YAAY,KAAK;GAG5B,MAAM,wBAAwB,KAAK,6BAA6B;GAChE,MAAM,KAAK,YAAY,KAAK;AAC5B,QAAK,aAAa,yBAAyB,sBAAsB;AACjE,QAAK,aAAa,eAAe,KAAK,GAAG;AAIzC,OAAI;IACF,MAAM,KAAK,YAAY,KAAK;IAC5B,MAAM,cAAc,KAAK,qBAAqB;AAC9C,SAAK,aAAa,kBAAkB,CAAC,CAAC,YAAY;AAClD,SAAK,aAAa,iBAAiB,KAAK,GAAG;AAE3C,QAAI,aAAa;KACf,MAAM,KAAK,YAAY,KAAK;KAC5B,MAAM,aAAa,YAAY,cAAc;KAC7C,MAAM,KAAK,YAAY,KAAK;AAC5B,UAAK,aAAa,kBAAkB,KAAK,GAAG;AAE5C,SAAI;MACF,MAAM,KAAK,YAAY,KAAK;AAC5B,WAAK,aAAa,YAAY,UAAU,KAAK;MAC7C,MAAM,KAAK,YAAY,KAAK;AAC5B,WAAK,aAAa,kBAAkB,KAAK,GAAG;eACpC;AACR,iBAAW,OAAO;;;YAGf,OAAO;AACd,YAAQ,KAAK,iCAAiC,MAAM;;AAItD,OAAI,CAAC,uBAAuB;IAG1B,MAAM,kBAAkB,CAAC,CAAC,KAAK,QAAQ,6BAA6B;AAEpE,QAAI,gBACF,MAAK,aAAa,eAAe,KAAK;AAKxC,QACE,CAAC,oBACA,CAAC,KAAK,iBACN,KAAK,cAAc,kBAAkB,KACpC,KAAK,sBAAsB,IAC7B;AACA,UAAK,aAAa,WAAW,yBAAyB;AACtD;;UAGG;AAGL,QAAI,CAAC,KAAK,eAAe;AACvB,UAAK,aAAa,WAAW,oBAAoB;AACjD;;AAGF,QAAI,CAAC,KAAK,wBAAwB,EAAE;AAClC,UAAK,aAAa,WAAW,6BAA6B;AAC1D;;;GAMJ,MAAM,OAAO,YAAY,KAAK;AAC9B,QAAK,aAAa,gBAAgB,OAAO,GAAG;IAE/C;;;;;CAMH,cAAoB;AAClB,MAAI,CAAC,KAAK,cAAe;EAEzB,MAAM,MAAM,KAAK,cAAc,WAAW,KAAK;AAC/C,MAAI,IACF,KAAI,UAAU,GAAG,GAAG,KAAK,cAAc,OAAO,KAAK,cAAc,OAAO;;;;;CAO5E,aAAa,OAAmB,UAAkB,YAAwB;EACxE,MAAM,gBAAgB,aAClB,MAAM,QAAQ,QAAQ,QAAQ,EAAE,WAAW,GAC3C;AAEJ,eACE,sBACA;GACE,WAAW,KAAK,MAAM;GACtB;GACA,QAAQ,MAAM,UAAU;GACxB,OAAO,MAAM;GACb,QAAQ,MAAM;GACf,EACD,gBACC,SAAS;GACR,MAAM,KAAK,YAAY,KAAK;AAE5B,OAAI,6BAA6B;IAC/B;IACA,aAAa,MAAM;IACpB,CAAC;AAEF,OAAI,CAAC,KAAK,eAAe;AACvB,QAAI,kDAAkD;AACtD,UAAM,IAAI,MACR,iEAAiE,SAAS,0DAC3E;;GAEH,MAAM,KAAK,YAAY,KAAK;AAC5B,QAAK,aAAa,eAAe,KAAK,OAAO,KAAK,MAAM,IAAI,GAAG,IAAI;GAEnE,MAAM,MAAM,KAAK,cAAc,WAAW,KAAK;GAC/C,MAAM,KAAK,YAAY,KAAK;AAC5B,QAAK,aAAa,YAAY,KAAK,OAAO,KAAK,MAAM,IAAI,GAAG,IAAI;AAEhE,OAAI,CAAC,KAAK;AACR,QAAI,kDAAkD;AACtD,UAAM,IAAI,MACR,iEAAiE,SAAS,2EAC3E;;GAGH,IAAI,UAAU;AACd,OAAI,OAAO,cAAc,OAAO,aAC9B;QACE,KAAK,cAAc,UAAU,MAAM,cACnC,KAAK,cAAc,WAAW,MAAM,aACpC;AACA,SAAI,qCAAqC;MACvC,OAAO,MAAM;MACb,QAAQ,MAAM;MACf,CAAC;AACF,UAAK,cAAc,QAAQ,MAAM;AACjC,UAAK,cAAc,SAAS,MAAM;AAClC,eAAU;KACV,MAAM,KAAK,YAAY,KAAK;AAC5B,UAAK,aAAa,YAAY,KAAK,OAAO,KAAK,MAAM,IAAI,GAAG,IAAI;;;AAGpE,QAAK,aAAa,iBAAiB,QAAQ;AAE3C,OAAI,MAAM,WAAW,MAAM;AACzB,QAAI,kDAAkD;AACtD,UAAM,IAAI,MACR,6DAA6D,SAAS,0DACvE;;GAGH,MAAM,aAAa,YAAY,KAAK;AACpC,OAAI,UACF,OACA,GACA,GACA,KAAK,cAAc,OACnB,KAAK,cAAc,OACpB;GACD,MAAM,WAAW,YAAY,KAAK;AAClC,QAAK,aACH,eACA,KAAK,OAAO,WAAW,cAAc,IAAI,GAAG,IAC7C;AACD,QAAK,aACH,kBACA,KAAK,OAAO,WAAW,MAAM,IAAI,GAAG,IACrC;AACD,QAAK,aAAa,eAAe,KAAK,cAAc,MAAM;AAC1D,QAAK,aAAa,gBAAgB,KAAK,cAAc,OAAO;AAE5D,OAAI,gCAAgC,EAAE,UAAU,CAAC;IAEpD;;;;;CAMH,AAAQ,8BAAuC;AAE7C,MAAI,OAAO,OAAO,iBAAiB,WACjC,QAAO,OAAO,cAAc;AAK9B,MADkB,SAAS,cAAc,eAAe,EACzC,UACb,QAAO;AAIT,MAAI,OAAO,aAAa,cACtB,QAAO;AAIT,SAAO;;;;;CAMT,AAAQ,yBAAkC;AACxC,MAAI,CAAC,OAAO,aAAa,cACvB,QAAO;EAMT,MAAM,kBADgB,OAAO,YAAY,cACH,eAAe;AAKrD,UAJoB,KAAK,eAAe,iBAAiB,MAInC;;;;;;CAOxB,IAAI,oBAAoB;AACtB,SAAO,KAAK;;;;;;;;;CAUd,MAAM,oBAAmC;EAIvC,MAAM,cAAc,KAAK;AACzB,MAAI,KAAK,sBAAsB,YAC7B,MAAK,oBAAoB;AAE3B,QAAM,KAAK;AAEX,MAAI;AACF,SAAM,KAAK,UAAU,KAAK;WACnB,OAAO;AAWd,OAPE,iBAAiB,gBAAgB,MAAM,SAAS,gBAChD,iBAAiB,UACf,MAAM,SAAS,gBACf,MAAM,SAAS,SAAS,oBAAoB,IAC5C,MAAM,SAAS,SAAS,6BAA6B,EAIvD;AAEF,SAAM;;;;;;;;;;;;;CAcV,MAAM,sBACJ,YACA,YACe;EAEf,MAAM,cAAc,MAAM,KAAK,gBAAgB;AAC/C,MAAI,CAAC,aAAa;AAChB,OAAI,mDAAmD;AACvD;;EAIF,MAAM,iBAAiB,YAAY,wBAAwB;AAC3D,MAAI,CAAC,gBAAgB;AACnB,OAAI,sDAAsD;AAC1D;;EAGF,MAAM,wBAAwB;GAC5B,GAAG;GACH,KAAK,YAAY;GAClB;EAGD,MAAM,6BAAa,IAAI,KAAa;AACpC,OAAK,MAAM,MAAM,YAAY;GAC3B,MAAM,YAAY,YAAY,iBAAiB,IAAI,sBAAsB;AACzE,OAAI,cAAc,OAChB,YAAW,IAAI,UAAU;;AAI7B,MAAI,WAAW,SAAS,GAAG;AACzB,OAAI,iDAAiD;AACrD;;EAMF,MAAM,iBAAiB,MAAM,KAAK,WAAW,CAAC;AAC9C,MAAI,YAAY,gBAAgB,gBAAgB,sBAAsB,EAAE;AACtE,OAAI,oDAAoD;AACxD;;AAGF,MAAI,mDAAmD,WAAW,KAAK,cAAc;EAGrF,MAAM,aAAa,YAAY,cAAc;AAC7C,OAAK,cACH,IAAI,YAAY,yBAAyB;GACvC,QAAQ;IACN,WAAW;IACX,aAAa,CAAC,GAAG,WAAW;IAC5B,QAAQ;IACR,OAAO;IACP,QAAQ;IACT;GACD,SAAS;GACT,UAAU;GACX,CAAC,CACH;AAGD,MAAI;AACF,SAAM,YAAY,kBAAkB,gBAAgB,sBAAsB;AAC1E,OAAI,4CAA4C;WACzC,OAAO;AACd,OAAI,qDAAqD,MAAM;;AAIjE,OAAK,cACH,IAAI,YAAY,yBAAyB;GACvC,QAAQ;IACN,WAAW;IACX,aAAa,CAAC,GAAG,WAAW;IAC5B,QAAQ;IACR,OAAO;IACP,QAAQ;IACT;GACD,SAAS;GACT,UAAU;GACX,CAAC,CACH;AAGD,eAAa,GAAG,GAAG,CAAC,GAAG,WAAW,CAAC;AACnC,MAAI,kCAAkC;;;;;;;;;;;CAYxC,MAAM,0BACJ,YACA,YACe;EAEf,MAAM,cAAc,MAAM,KAAK,gBAAgB;AAC/C,MAAI,CAAC,aAAa;AAChB,OAAI,uDAAuD;AAC3D;;EAIF,MAAM,iBAAiB,YAAY,qBAAqB,IAAI,YAAY;AACxE,MAAI,CAAC,gBAAgB;AACnB,OAAI,0DAA0D;AAC9D;;EAIF,MAAM,6BAAa,IAAI,KAAa;AACpC,OAAK,MAAM,MAAM,YAAY;GAC3B,MAAM,YAAY,YAAY,iBAAiB,IAAI,eAAe;AAClE,OAAI,cAAc,OAChB,YAAW,IAAI,UAAU;;AAI7B,MAAI,WAAW,SAAS,GAAG;AACzB,OAAI,qDAAqD;AACzD;;EAIF,MAAMC,qBAA+B,EAAE;AACvC,OAAK,MAAM,aAAa,WACtB,KAAI,CAAC,YAAY,gBAAgB,WAAW,eAAe,CACzD,oBAAmB,KAAK,UAAU;AAItC,MAAI,mBAAmB,WAAW,GAAG;AACnC,OAAI,yDAAyD;AAC7D,gBAAa,WAAW,MAAM,WAAW,KAAK;AAC9C;;AAGF,MAAI,uCAAuC,mBAAmB,OAAO,cAAc;AAGnF,MAAI;AACF,SAAO,YAAoB,iBAAiB,eAAe;WACpD,OAAO;AACd,OAAI,2DAA2D,MAAM;AACrE;;EAIF,IAAI,SAAS,WAAW,OAAO,mBAAmB;AAClD,OAAK,MAAM,aAAa,mBACtB,KAAI;AACF,SAAM,YAAY,kBAAkB,WAAW,eAAe;AAC9D;AACA,gBAAa,QAAQ,WAAW,KAAK;WAC9B,OAAO;AACd,OAAI,sDAAsD,aAAa,MAAM;;AAKjF,MAAI,wCAAwC,OAAO,GAAG,WAAW,KAAK,YAAY;;;;;CAMpF,uBAA6B;AAC3B,QAAM,sBAAsB;AAG5B,OAAK,oBAAoB,iBAAiB;;CAG5C,gBAAgB;AACd,QAAM,eAAe;;CAEvB,iBAAiB;AACf,QAAM,gBAAgB;;;;;;;;CASxB,uBAAiE;EAC/D,MAAM,SAAS,KAAK;AACpB,MAAI,CAAC,UAAU,OAAO,UAAU,KAAK,OAAO,WAAW,EACrD,QAAO;AAET,SAAO;GACL,OAAO,OAAO;GACd,QAAQ,OAAO;GAChB;;;YAjtBF,SAAS;CAAE,MAAM;CAAQ,WAAW;CAAyB,CAAC;YAO9D,SAAS;CAAE,MAAM;CAAQ,WAAW;CAA4B,CAAC;YAOjE,SAAS;CAAE,MAAM;CAAS,WAAW;CAA0B,CAAC;YAuBhE,OAAO;sBAzGT,cAAc,WAAW"}
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
import { TemporalMixinInterface } from "./EFTemporal.js";
|
|
2
|
-
import { EFAudio } from "./EFAudio.js";
|
|
3
2
|
import { EFVideo } from "./EFVideo.js";
|
|
3
|
+
import { EFAudio } from "./EFAudio.js";
|
|
4
4
|
import { TargetController } from "./TargetController.js";
|
|
5
5
|
import { Task } from "@lit/task";
|
|
6
|
-
import * as
|
|
6
|
+
import * as lit10 from "lit";
|
|
7
7
|
import { LitElement, PropertyValueMap } from "lit";
|
|
8
8
|
import { Ref } from "lit/directives/ref.js";
|
|
9
|
-
import * as
|
|
9
|
+
import * as lit_html10 from "lit-html";
|
|
10
10
|
|
|
11
11
|
//#region src/elements/EFWaveform.d.ts
|
|
12
12
|
declare const EFWaveform_base: (new (...args: any[]) => TemporalMixinInterface) & typeof LitElement;
|
|
13
13
|
declare class EFWaveform extends EFWaveform_base {
|
|
14
|
-
static styles:
|
|
14
|
+
static styles: lit10.CSSResult;
|
|
15
15
|
canvasRef: Ref<HTMLCanvasElement>;
|
|
16
16
|
private ctx;
|
|
17
17
|
private styleObserver;
|
|
18
18
|
private resizeObserver?;
|
|
19
19
|
private mutationObserver?;
|
|
20
|
-
render():
|
|
20
|
+
render(): lit_html10.TemplateResult<1>;
|
|
21
21
|
mode: "roundBars" | "bars" | "bricks" | "line" | "curve" | "pixel" | "wave" | "spikes";
|
|
22
22
|
color: string;
|
|
23
23
|
target: string;
|
|
@@ -37,7 +37,7 @@ declare class EFWaveform extends EFWaveform_base {
|
|
|
37
37
|
protected drawPixel(ctx: CanvasRenderingContext2D, frequencyData: Uint8Array): void;
|
|
38
38
|
protected drawWave(ctx: CanvasRenderingContext2D, frequencyData: Uint8Array): void;
|
|
39
39
|
protected drawSpikes(ctx: CanvasRenderingContext2D, frequencyData: Uint8Array): void;
|
|
40
|
-
frameTask: Task<readonly [EFAudio | EFVideo | null, Uint8Array | null | undefined], void>;
|
|
40
|
+
frameTask: Task<readonly [EFAudio | EFVideo | null, Uint8Array<ArrayBufferLike> | null | undefined], void>;
|
|
41
41
|
get durationMs(): number;
|
|
42
42
|
protected updated(changedProperties: PropertyValueMap<this>): void;
|
|
43
43
|
}
|
|
@@ -29,9 +29,10 @@ let EFWaveform = class EFWaveform$1 extends EFTemporal(TWMixin(LitElement)) {
|
|
|
29
29
|
args: () => {
|
|
30
30
|
return [this.targetElement, this.targetElement?.frequencyDataTask.value];
|
|
31
31
|
},
|
|
32
|
-
task: async () => {
|
|
32
|
+
task: async ([_targetElement, _frequencyData], { signal }) => {
|
|
33
33
|
if (!this.targetElement) return;
|
|
34
34
|
await this.targetElement.frequencyDataTask.taskComplete;
|
|
35
|
+
signal?.throwIfAborted();
|
|
35
36
|
this.ctx ||= this.initCanvas();
|
|
36
37
|
const ctx = this.ctx;
|
|
37
38
|
if (!ctx) return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EFWaveform.js","names":["EFWaveform"],"sources":["../../src/elements/EFWaveform.ts"],"sourcesContent":["import { CSSStyleObserver } from \"@bramus/style-observer\";\nimport { Task } from \"@lit/task\";\nimport { css, html, LitElement, type PropertyValueMap } from \"lit\";\nimport { customElement, property, state } from \"lit/decorators.js\";\nimport { createRef, type Ref, ref } from \"lit/directives/ref.js\";\nimport { EF_RENDERING } from \"../EF_RENDERING.js\";\nimport { TWMixin } from \"../gui/TWMixin.js\";\nimport { CrossUpdateController } from \"./CrossUpdateController.js\";\nimport type { EFAudio } from \"./EFAudio.js\";\nimport { EFTemporal } from \"./EFTemporal.js\";\nimport type { EFVideo } from \"./EFVideo.js\";\nimport { TargetController } from \"./TargetController.ts\";\n\n@customElement(\"ef-waveform\")\nexport class EFWaveform extends EFTemporal(TWMixin(LitElement)) {\n static styles = css`\n :host {\n all: inherit;\n display: block;\n position: relative;\n }\n\n canvas {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n }\n `;\n\n canvasRef: Ref<HTMLCanvasElement> = createRef();\n private ctx: CanvasRenderingContext2D | null = null;\n private styleObserver: CSSStyleObserver | null = null;\n\n private resizeObserver?: ResizeObserver;\n private mutationObserver?: MutationObserver;\n\n render() {\n return html`<canvas ${ref(this.canvasRef)}></canvas>`;\n }\n\n @property({\n type: String,\n attribute: \"mode\",\n })\n mode:\n | \"roundBars\"\n | \"bars\"\n | \"bricks\"\n | \"line\"\n | \"curve\"\n | \"pixel\"\n | \"wave\"\n | \"spikes\" = \"bars\";\n\n @property({ type: String })\n color = \"currentColor\";\n\n @property({ type: String, reflect: true })\n target = \"\";\n\n @property({ type: Number, attribute: \"bar-spacing\" })\n barSpacing = 0.5;\n\n @state()\n targetElement: EFAudio | EFVideo | null = null;\n\n @property({ type: Number, attribute: \"line-width\" })\n lineWidth = 4;\n\n targetController: TargetController = new TargetController(this);\n\n connectedCallback() {\n super.connectedCallback();\n try {\n if (this.targetElement) {\n new CrossUpdateController(this.targetElement, this);\n }\n } catch (_error) {\n // TODO: determine if this is a critical error, or if we should just ignore it\n // currenty evidence suggests everything still works\n // no target element, no cross update controller\n }\n\n // Initialize ResizeObserver\n this.resizeObserver = new ResizeObserver(() => {\n this.resizeCanvas();\n });\n\n // Observe the host element\n this.resizeObserver.observe(this);\n\n // Initialize MutationObserver\n this.mutationObserver = new MutationObserver((mutationsList) => {\n for (const mutation of mutationsList) {\n if (mutation.type === \"attributes\") {\n this.frameTask.run();\n }\n }\n });\n\n // Observe attribute changes on the element\n this.mutationObserver.observe(this, { attributes: true });\n\n if (!EF_RENDERING()) {\n this.styleObserver = new CSSStyleObserver([\"color\"], () => {\n this.frameTask.run();\n });\n this.styleObserver.attach(this);\n }\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n // Disconnect the observers when the element is removed from the DOM\n this.resizeObserver?.disconnect();\n this.mutationObserver?.disconnect();\n this.styleObserver?.detach();\n }\n\n private resizeCanvas() {\n this.ctx = this.initCanvas();\n if (this.ctx) {\n this.frameTask.run(); // Redraw the canvas\n }\n }\n\n protected initCanvas() {\n const canvas = this.canvasRef.value;\n if (!canvas) return null;\n\n const rect = {\n width: this.offsetWidth,\n height: this.offsetHeight,\n };\n const dpr = window.devicePixelRatio;\n\n canvas.style.width = `${rect.width}px`;\n canvas.style.height = `${rect.height}px`;\n\n canvas.width = rect.width * dpr;\n canvas.height = rect.height * dpr;\n\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return null;\n ctx.reset();\n\n // Scale all drawing operations by dpr\n return ctx;\n }\n\n protected drawBars(ctx: CanvasRenderingContext2D, frequencyData: Uint8Array) {\n const canvas = ctx.canvas;\n const waveWidth = canvas.width;\n const waveHeight = canvas.height;\n\n const totalBars = frequencyData.length;\n const paddingInner = this.barSpacing;\n const paddingOuter = 0.01;\n const availableWidth = waveWidth;\n const barWidth =\n availableWidth / (totalBars + (totalBars - 1) * paddingInner);\n\n ctx.clearRect(0, 0, waveWidth, waveHeight);\n const path = new Path2D();\n\n frequencyData.forEach((value, i) => {\n const normalizedValue = value / 255;\n const barHeight = normalizedValue * waveHeight;\n const y = (waveHeight - barHeight) / 2;\n const x = waveWidth * paddingOuter + i * (barWidth * (1 + paddingInner));\n path.rect(x, y, barWidth, barHeight);\n });\n\n ctx.fill(path);\n }\n\n protected drawBricks(\n ctx: CanvasRenderingContext2D,\n frequencyData: Uint8Array,\n ) {\n const canvas = ctx.canvas;\n const waveWidth = canvas.width;\n const waveHeight = canvas.height;\n ctx.clearRect(0, 0, waveWidth, waveHeight);\n const path = new Path2D();\n\n const columnWidth = waveWidth / frequencyData.length;\n const boxSize = columnWidth * 0.9;\n const verticalGap = boxSize * 0.2; // Add spacing between bricks\n const maxBricks = Math.floor(waveHeight / (boxSize + verticalGap)); // Account for gaps in height calculation\n\n frequencyData.forEach((value, i) => {\n const normalizedValue = value / 255;\n const brickCount = Math.floor(normalizedValue * maxBricks);\n\n for (let j = 0; j < brickCount; j++) {\n const x = columnWidth * i;\n const y = waveHeight - (j + 1) * (boxSize + verticalGap); // Include gap in position calculation\n path.rect(x, y, boxSize, boxSize);\n }\n });\n\n ctx.fill(path);\n }\n\n protected drawRoundBars(\n ctx: CanvasRenderingContext2D,\n frequencyData: Uint8Array,\n ) {\n const canvas = ctx.canvas;\n const waveWidth = canvas.width;\n const waveHeight = canvas.height;\n\n // Similar padding calculation as drawBars\n const totalBars = frequencyData.length;\n const paddingInner = this.barSpacing;\n const paddingOuter = 0.01;\n const availableWidth = waveWidth;\n const barWidth =\n availableWidth / (totalBars + (totalBars - 1) * paddingInner);\n\n ctx.clearRect(0, 0, waveWidth, waveHeight);\n\n // Create a single Path2D object for all rounded bars\n const path = new Path2D();\n\n frequencyData.forEach((value, i) => {\n const normalizedValue = value / 255;\n const height = normalizedValue * waveHeight; // Use full wave height like in drawBars\n const x = waveWidth * paddingOuter + i * (barWidth * (1 + paddingInner));\n const y = (waveHeight - height) / 2; // Center vertically\n\n // Add rounded rectangle to path\n path.roundRect(x, y, barWidth, height, barWidth / 2);\n });\n\n // Single fill operation for all bars\n ctx.fill(path);\n }\n\n protected drawLine(ctx: CanvasRenderingContext2D, frequencyData: Uint8Array) {\n const canvas = ctx.canvas;\n const waveWidth = canvas.width;\n const waveHeight = canvas.height;\n\n ctx.clearRect(0, 0, waveWidth, waveHeight);\n const path = new Path2D();\n\n // Sample fewer points to make sharp angles more visible\n const sampleRate = 1; // Only use every 4th point\n\n for (let i = 0; i < frequencyData.length; i += sampleRate) {\n const x = (i / frequencyData.length) * waveWidth;\n const y = (1 - (frequencyData[i] ?? 0) / 255) * waveHeight;\n\n if (i === 0) {\n path.moveTo(x, y);\n } else {\n path.lineTo(x, y);\n }\n }\n // Ensure we draw to the end\n const lastX = waveWidth;\n const lastY =\n (1 - (frequencyData[frequencyData.length - 1] ?? 0) / 255) * waveHeight;\n path.lineTo(lastX, lastY);\n\n ctx.lineWidth = this.lineWidth;\n ctx.stroke(path);\n }\n\n protected drawCurve(\n ctx: CanvasRenderingContext2D,\n frequencyData: Uint8Array,\n ) {\n const canvas = ctx.canvas;\n const waveWidth = canvas.width;\n const waveHeight = canvas.height;\n\n ctx.clearRect(0, 0, waveWidth, waveHeight);\n const path = new Path2D();\n\n // Draw smooth curves between points using quadratic curves\n frequencyData.forEach((value, i) => {\n const x = (i / frequencyData.length) * waveWidth;\n const y = (1 - value / 255) * waveHeight;\n\n if (i === 0) {\n path.moveTo(x, y);\n } else {\n const prevX = ((i - 1) / frequencyData.length) * waveWidth;\n const prevY = (1 - (frequencyData[i - 1] ?? 0) / 255) * waveHeight;\n const xc = (prevX + x) / 2;\n const yc = (prevY + y) / 2;\n path.quadraticCurveTo(prevX, prevY, xc, yc);\n }\n });\n\n ctx.lineWidth = this.lineWidth;\n ctx.stroke(path);\n }\n\n protected drawPixel(\n ctx: CanvasRenderingContext2D,\n frequencyData: Uint8Array,\n ) {\n const canvas = ctx.canvas;\n const waveWidth = canvas.width;\n const waveHeight = canvas.height;\n const baseline = waveHeight / 2;\n const barWidth = waveWidth / frequencyData.length;\n\n ctx.clearRect(0, 0, waveWidth, waveHeight);\n const path = new Path2D();\n\n frequencyData.forEach((value, i) => {\n const normalizedValue = value / 255;\n const x = i * (waveWidth / frequencyData.length);\n const barHeight = normalizedValue * (waveHeight / 2); // Half height since we extend both ways\n const y = baseline - barHeight;\n path.rect(x, y, barWidth, barHeight * 2); // Double height to extend both ways\n });\n\n ctx.fill(path);\n }\n\n protected drawWave(ctx: CanvasRenderingContext2D, frequencyData: Uint8Array) {\n const canvas = ctx.canvas;\n const waveWidth = canvas.width;\n const waveHeight = canvas.height;\n const paddingOuter = 0.01;\n const availableWidth = waveWidth * (1 - 2 * paddingOuter);\n const startX = waveWidth * paddingOuter;\n\n ctx.clearRect(0, 0, waveWidth, waveHeight);\n const path = new Path2D();\n\n // Draw top curve\n const firstValue = Math.min(((frequencyData[0] ?? 0) / 255) * 2, 1);\n const firstY = (waveHeight - firstValue * waveHeight) / 2;\n path.moveTo(startX, firstY);\n\n // Draw top half\n frequencyData.forEach((value, i) => {\n const normalizedValue = Math.min((value / 255) * 2, 1);\n const x = startX + (i / (frequencyData.length - 1)) * availableWidth;\n const barHeight = normalizedValue * waveHeight;\n const y = (waveHeight - barHeight) / 2;\n\n if (i === 0) {\n path.moveTo(x, y);\n } else {\n const prevX =\n startX + ((i - 1) / (frequencyData.length - 1)) * availableWidth;\n const prevValue = Math.min(((frequencyData[i - 1] ?? 0) / 255) * 2, 1);\n const prevBarHeight = prevValue * waveHeight;\n const prevY = (waveHeight - prevBarHeight) / 2;\n const xc = (prevX + x) / 2;\n const yc = (prevY + y) / 2;\n path.quadraticCurveTo(prevX, prevY, xc, yc);\n }\n });\n\n // Draw bottom half\n for (let i = frequencyData.length - 1; i >= 0; i--) {\n const normalizedValue = Math.min(((frequencyData[i] ?? 0) / 255) * 2, 1);\n const x = startX + (i / (frequencyData.length - 1)) * availableWidth;\n const barHeight = normalizedValue * waveHeight;\n const y = (waveHeight + barHeight) / 2;\n\n if (i === frequencyData.length - 1) {\n path.lineTo(x, y);\n } else {\n const nextX =\n startX + ((i + 1) / (frequencyData.length - 1)) * availableWidth;\n const nextValue = Math.min(((frequencyData[i + 1] ?? 0) / 255) * 2, 1);\n const nextBarHeight = nextValue * waveHeight;\n const nextY = (waveHeight + nextBarHeight) / 2;\n const xc = (nextX + x) / 2;\n const yc = (nextY + y) / 2;\n path.quadraticCurveTo(nextX, nextY, xc, yc);\n }\n }\n\n // Close the path with a smooth curve back to start\n const lastY = (waveHeight + firstValue * waveHeight) / 2;\n const controlX = startX;\n const controlY = (lastY + firstY) / 2;\n path.quadraticCurveTo(controlX, controlY, startX, firstY);\n\n ctx.fill(path);\n }\n\n protected drawSpikes(\n ctx: CanvasRenderingContext2D,\n frequencyData: Uint8Array,\n ) {\n const canvas = ctx.canvas;\n const waveWidth = canvas.width;\n const waveHeight = canvas.height;\n const paddingOuter = 0.01;\n const availableWidth = waveWidth * (1 - 2 * paddingOuter);\n const startX = waveWidth * paddingOuter;\n\n ctx.clearRect(0, 0, waveWidth, waveHeight);\n const path = new Path2D();\n\n // Draw top curve\n const firstValue = (frequencyData[0] ?? 0) / 255;\n const firstY = (waveHeight - firstValue * waveHeight) / 2;\n path.moveTo(startX, firstY);\n\n // Draw top half\n frequencyData.forEach((value, i) => {\n const normalizedValue = Math.min((value / 255) * 2, 1);\n const x = startX + (i / (frequencyData.length - 1)) * availableWidth;\n const barHeight = normalizedValue * (waveHeight / 2);\n const y = (waveHeight - barHeight * 2) / 2;\n\n if (i === 0) {\n path.moveTo(x, y);\n } else {\n const prevX =\n startX + ((i - 1) / (frequencyData.length - 1)) * availableWidth;\n const prevValue = (frequencyData[i - 1] ?? 0) / 255;\n const prevBarHeight = prevValue * (waveHeight / 2);\n const prevY = (waveHeight - prevBarHeight * 2) / 2;\n const xc = (prevX + x) / 2;\n const yc = (prevY + y) / 2;\n path.quadraticCurveTo(prevX, prevY, xc, yc);\n }\n });\n\n // Draw bottom half\n for (let i = frequencyData.length - 1; i >= 0; i--) {\n const normalizedValue = Math.min(((frequencyData[i] ?? 0) / 255) * 2, 1);\n const x = startX + (i / (frequencyData.length - 1)) * availableWidth;\n const barHeight = normalizedValue * (waveHeight / 2);\n const y = (waveHeight + barHeight * 2) / 2;\n\n if (i === frequencyData.length - 1) {\n path.lineTo(x, y);\n } else {\n const nextX =\n startX + ((i + 1) / (frequencyData.length - 1)) * availableWidth;\n const nextValue = (frequencyData[i + 1] ?? 0) / 255;\n const nextBarHeight = nextValue * (waveHeight / 2);\n const nextY = (waveHeight + nextBarHeight * 2) / 2;\n const xc = (nextX + x) / 2;\n const yc = (nextY + y) / 2;\n path.quadraticCurveTo(nextX, nextY, xc, yc);\n }\n }\n\n // Close the path with a smooth curve\n const lastY = (waveHeight + firstValue * waveHeight) / 2;\n const controlX = startX;\n const controlY = (lastY + firstY) / 2;\n path.quadraticCurveTo(controlX, controlY, startX, firstY);\n\n ctx.fill(path);\n }\n\n frameTask = new Task(this, {\n autoRun: false,\n args: () => {\n return [\n this.targetElement,\n this.targetElement?.frequencyDataTask.value,\n ] as const;\n },\n task: async () => {\n if (!this.targetElement) return;\n await this.targetElement.frequencyDataTask.taskComplete;\n this.ctx ||= this.initCanvas();\n const ctx = this.ctx;\n if (!ctx) return;\n\n const frequencyData = this.targetElement.frequencyDataTask.value;\n const byteTimeData = this.targetElement.byteTimeDomainTask.value;\n if (!frequencyData || !byteTimeData) return;\n\n ctx.save();\n if (this.color === \"currentColor\") {\n const computedStyle = getComputedStyle(this);\n const currentColor = computedStyle.color;\n ctx.strokeStyle = currentColor;\n ctx.fillStyle = currentColor;\n } else {\n ctx.strokeStyle = this.color;\n ctx.fillStyle = this.color;\n }\n\n switch (this.mode) {\n case \"bars\":\n this.drawBars(ctx, frequencyData);\n break;\n case \"bricks\":\n this.drawBricks(ctx, frequencyData);\n break;\n case \"line\":\n this.drawLine(ctx, byteTimeData);\n break;\n case \"curve\":\n this.drawCurve(ctx, byteTimeData);\n break;\n case \"pixel\":\n this.drawPixel(ctx, frequencyData);\n break;\n case \"wave\":\n this.drawWave(ctx, frequencyData);\n break;\n case \"spikes\":\n this.drawSpikes(ctx, frequencyData);\n break;\n case \"roundBars\":\n this.drawRoundBars(ctx, frequencyData);\n break;\n }\n\n ctx.restore();\n },\n });\n\n get durationMs() {\n if (!this.targetElement) return 0;\n return this.targetElement.durationMs;\n }\n\n protected updated(changedProperties: PropertyValueMap<this>): void {\n super.updated(changedProperties);\n\n // Trigger a redraw if any property changes\n if (changedProperties.size > 0) {\n // Request a new frame\n this.frameTask.run();\n }\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"ef-waveform\": EFWaveform & Element;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"EFWaveform.js","names":["EFWaveform"],"sources":["../../src/elements/EFWaveform.ts"],"sourcesContent":["import { CSSStyleObserver } from \"@bramus/style-observer\";\nimport { Task } from \"@lit/task\";\nimport { css, html, LitElement, type PropertyValueMap } from \"lit\";\nimport { customElement, property, state } from \"lit/decorators.js\";\nimport { createRef, type Ref, ref } from \"lit/directives/ref.js\";\nimport { EF_RENDERING } from \"../EF_RENDERING.js\";\nimport { TWMixin } from \"../gui/TWMixin.js\";\nimport { CrossUpdateController } from \"./CrossUpdateController.js\";\nimport type { EFAudio } from \"./EFAudio.js\";\nimport { EFTemporal } from \"./EFTemporal.js\";\nimport type { EFVideo } from \"./EFVideo.js\";\nimport { TargetController } from \"./TargetController.ts\";\n\n@customElement(\"ef-waveform\")\nexport class EFWaveform extends EFTemporal(TWMixin(LitElement)) {\n static styles = css`\n :host {\n all: inherit;\n display: block;\n position: relative;\n }\n\n canvas {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n }\n `;\n\n canvasRef: Ref<HTMLCanvasElement> = createRef();\n private ctx: CanvasRenderingContext2D | null = null;\n private styleObserver: CSSStyleObserver | null = null;\n\n private resizeObserver?: ResizeObserver;\n private mutationObserver?: MutationObserver;\n\n render() {\n return html`<canvas ${ref(this.canvasRef)}></canvas>`;\n }\n\n @property({\n type: String,\n attribute: \"mode\",\n })\n mode:\n | \"roundBars\"\n | \"bars\"\n | \"bricks\"\n | \"line\"\n | \"curve\"\n | \"pixel\"\n | \"wave\"\n | \"spikes\" = \"bars\";\n\n @property({ type: String })\n color = \"currentColor\";\n\n @property({ type: String, reflect: true })\n target = \"\";\n\n @property({ type: Number, attribute: \"bar-spacing\" })\n barSpacing = 0.5;\n\n @state()\n targetElement: EFAudio | EFVideo | null = null;\n\n @property({ type: Number, attribute: \"line-width\" })\n lineWidth = 4;\n\n targetController: TargetController = new TargetController(this);\n\n connectedCallback() {\n super.connectedCallback();\n try {\n if (this.targetElement) {\n new CrossUpdateController(this.targetElement, this);\n }\n } catch (_error) {\n // TODO: determine if this is a critical error, or if we should just ignore it\n // currenty evidence suggests everything still works\n // no target element, no cross update controller\n }\n\n // Initialize ResizeObserver\n this.resizeObserver = new ResizeObserver(() => {\n this.resizeCanvas();\n });\n\n // Observe the host element\n this.resizeObserver.observe(this);\n\n // Initialize MutationObserver\n this.mutationObserver = new MutationObserver((mutationsList) => {\n for (const mutation of mutationsList) {\n if (mutation.type === \"attributes\") {\n this.frameTask.run();\n }\n }\n });\n\n // Observe attribute changes on the element\n this.mutationObserver.observe(this, { attributes: true });\n\n if (!EF_RENDERING()) {\n this.styleObserver = new CSSStyleObserver([\"color\"], () => {\n this.frameTask.run();\n });\n this.styleObserver.attach(this);\n }\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n // Disconnect the observers when the element is removed from the DOM\n this.resizeObserver?.disconnect();\n this.mutationObserver?.disconnect();\n this.styleObserver?.detach();\n }\n\n private resizeCanvas() {\n this.ctx = this.initCanvas();\n if (this.ctx) {\n this.frameTask.run(); // Redraw the canvas\n }\n }\n\n protected initCanvas() {\n const canvas = this.canvasRef.value;\n if (!canvas) return null;\n\n const rect = {\n width: this.offsetWidth,\n height: this.offsetHeight,\n };\n const dpr = window.devicePixelRatio;\n\n canvas.style.width = `${rect.width}px`;\n canvas.style.height = `${rect.height}px`;\n\n canvas.width = rect.width * dpr;\n canvas.height = rect.height * dpr;\n\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return null;\n ctx.reset();\n\n // Scale all drawing operations by dpr\n return ctx;\n }\n\n protected drawBars(ctx: CanvasRenderingContext2D, frequencyData: Uint8Array) {\n const canvas = ctx.canvas;\n const waveWidth = canvas.width;\n const waveHeight = canvas.height;\n\n const totalBars = frequencyData.length;\n const paddingInner = this.barSpacing;\n const paddingOuter = 0.01;\n const availableWidth = waveWidth;\n const barWidth =\n availableWidth / (totalBars + (totalBars - 1) * paddingInner);\n\n ctx.clearRect(0, 0, waveWidth, waveHeight);\n const path = new Path2D();\n\n frequencyData.forEach((value, i) => {\n const normalizedValue = value / 255;\n const barHeight = normalizedValue * waveHeight;\n const y = (waveHeight - barHeight) / 2;\n const x = waveWidth * paddingOuter + i * (barWidth * (1 + paddingInner));\n path.rect(x, y, barWidth, barHeight);\n });\n\n ctx.fill(path);\n }\n\n protected drawBricks(\n ctx: CanvasRenderingContext2D,\n frequencyData: Uint8Array,\n ) {\n const canvas = ctx.canvas;\n const waveWidth = canvas.width;\n const waveHeight = canvas.height;\n ctx.clearRect(0, 0, waveWidth, waveHeight);\n const path = new Path2D();\n\n const columnWidth = waveWidth / frequencyData.length;\n const boxSize = columnWidth * 0.9;\n const verticalGap = boxSize * 0.2; // Add spacing between bricks\n const maxBricks = Math.floor(waveHeight / (boxSize + verticalGap)); // Account for gaps in height calculation\n\n frequencyData.forEach((value, i) => {\n const normalizedValue = value / 255;\n const brickCount = Math.floor(normalizedValue * maxBricks);\n\n for (let j = 0; j < brickCount; j++) {\n const x = columnWidth * i;\n const y = waveHeight - (j + 1) * (boxSize + verticalGap); // Include gap in position calculation\n path.rect(x, y, boxSize, boxSize);\n }\n });\n\n ctx.fill(path);\n }\n\n protected drawRoundBars(\n ctx: CanvasRenderingContext2D,\n frequencyData: Uint8Array,\n ) {\n const canvas = ctx.canvas;\n const waveWidth = canvas.width;\n const waveHeight = canvas.height;\n\n // Similar padding calculation as drawBars\n const totalBars = frequencyData.length;\n const paddingInner = this.barSpacing;\n const paddingOuter = 0.01;\n const availableWidth = waveWidth;\n const barWidth =\n availableWidth / (totalBars + (totalBars - 1) * paddingInner);\n\n ctx.clearRect(0, 0, waveWidth, waveHeight);\n\n // Create a single Path2D object for all rounded bars\n const path = new Path2D();\n\n frequencyData.forEach((value, i) => {\n const normalizedValue = value / 255;\n const height = normalizedValue * waveHeight; // Use full wave height like in drawBars\n const x = waveWidth * paddingOuter + i * (barWidth * (1 + paddingInner));\n const y = (waveHeight - height) / 2; // Center vertically\n\n // Add rounded rectangle to path\n path.roundRect(x, y, barWidth, height, barWidth / 2);\n });\n\n // Single fill operation for all bars\n ctx.fill(path);\n }\n\n protected drawLine(ctx: CanvasRenderingContext2D, frequencyData: Uint8Array) {\n const canvas = ctx.canvas;\n const waveWidth = canvas.width;\n const waveHeight = canvas.height;\n\n ctx.clearRect(0, 0, waveWidth, waveHeight);\n const path = new Path2D();\n\n // Sample fewer points to make sharp angles more visible\n const sampleRate = 1; // Only use every 4th point\n\n for (let i = 0; i < frequencyData.length; i += sampleRate) {\n const x = (i / frequencyData.length) * waveWidth;\n const y = (1 - (frequencyData[i] ?? 0) / 255) * waveHeight;\n\n if (i === 0) {\n path.moveTo(x, y);\n } else {\n path.lineTo(x, y);\n }\n }\n // Ensure we draw to the end\n const lastX = waveWidth;\n const lastY =\n (1 - (frequencyData[frequencyData.length - 1] ?? 0) / 255) * waveHeight;\n path.lineTo(lastX, lastY);\n\n ctx.lineWidth = this.lineWidth;\n ctx.stroke(path);\n }\n\n protected drawCurve(\n ctx: CanvasRenderingContext2D,\n frequencyData: Uint8Array,\n ) {\n const canvas = ctx.canvas;\n const waveWidth = canvas.width;\n const waveHeight = canvas.height;\n\n ctx.clearRect(0, 0, waveWidth, waveHeight);\n const path = new Path2D();\n\n // Draw smooth curves between points using quadratic curves\n frequencyData.forEach((value, i) => {\n const x = (i / frequencyData.length) * waveWidth;\n const y = (1 - value / 255) * waveHeight;\n\n if (i === 0) {\n path.moveTo(x, y);\n } else {\n const prevX = ((i - 1) / frequencyData.length) * waveWidth;\n const prevY = (1 - (frequencyData[i - 1] ?? 0) / 255) * waveHeight;\n const xc = (prevX + x) / 2;\n const yc = (prevY + y) / 2;\n path.quadraticCurveTo(prevX, prevY, xc, yc);\n }\n });\n\n ctx.lineWidth = this.lineWidth;\n ctx.stroke(path);\n }\n\n protected drawPixel(\n ctx: CanvasRenderingContext2D,\n frequencyData: Uint8Array,\n ) {\n const canvas = ctx.canvas;\n const waveWidth = canvas.width;\n const waveHeight = canvas.height;\n const baseline = waveHeight / 2;\n const barWidth = waveWidth / frequencyData.length;\n\n ctx.clearRect(0, 0, waveWidth, waveHeight);\n const path = new Path2D();\n\n frequencyData.forEach((value, i) => {\n const normalizedValue = value / 255;\n const x = i * (waveWidth / frequencyData.length);\n const barHeight = normalizedValue * (waveHeight / 2); // Half height since we extend both ways\n const y = baseline - barHeight;\n path.rect(x, y, barWidth, barHeight * 2); // Double height to extend both ways\n });\n\n ctx.fill(path);\n }\n\n protected drawWave(ctx: CanvasRenderingContext2D, frequencyData: Uint8Array) {\n const canvas = ctx.canvas;\n const waveWidth = canvas.width;\n const waveHeight = canvas.height;\n const paddingOuter = 0.01;\n const availableWidth = waveWidth * (1 - 2 * paddingOuter);\n const startX = waveWidth * paddingOuter;\n\n ctx.clearRect(0, 0, waveWidth, waveHeight);\n const path = new Path2D();\n\n // Draw top curve\n const firstValue = Math.min(((frequencyData[0] ?? 0) / 255) * 2, 1);\n const firstY = (waveHeight - firstValue * waveHeight) / 2;\n path.moveTo(startX, firstY);\n\n // Draw top half\n frequencyData.forEach((value, i) => {\n const normalizedValue = Math.min((value / 255) * 2, 1);\n const x = startX + (i / (frequencyData.length - 1)) * availableWidth;\n const barHeight = normalizedValue * waveHeight;\n const y = (waveHeight - barHeight) / 2;\n\n if (i === 0) {\n path.moveTo(x, y);\n } else {\n const prevX =\n startX + ((i - 1) / (frequencyData.length - 1)) * availableWidth;\n const prevValue = Math.min(((frequencyData[i - 1] ?? 0) / 255) * 2, 1);\n const prevBarHeight = prevValue * waveHeight;\n const prevY = (waveHeight - prevBarHeight) / 2;\n const xc = (prevX + x) / 2;\n const yc = (prevY + y) / 2;\n path.quadraticCurveTo(prevX, prevY, xc, yc);\n }\n });\n\n // Draw bottom half\n for (let i = frequencyData.length - 1; i >= 0; i--) {\n const normalizedValue = Math.min(((frequencyData[i] ?? 0) / 255) * 2, 1);\n const x = startX + (i / (frequencyData.length - 1)) * availableWidth;\n const barHeight = normalizedValue * waveHeight;\n const y = (waveHeight + barHeight) / 2;\n\n if (i === frequencyData.length - 1) {\n path.lineTo(x, y);\n } else {\n const nextX =\n startX + ((i + 1) / (frequencyData.length - 1)) * availableWidth;\n const nextValue = Math.min(((frequencyData[i + 1] ?? 0) / 255) * 2, 1);\n const nextBarHeight = nextValue * waveHeight;\n const nextY = (waveHeight + nextBarHeight) / 2;\n const xc = (nextX + x) / 2;\n const yc = (nextY + y) / 2;\n path.quadraticCurveTo(nextX, nextY, xc, yc);\n }\n }\n\n // Close the path with a smooth curve back to start\n const lastY = (waveHeight + firstValue * waveHeight) / 2;\n const controlX = startX;\n const controlY = (lastY + firstY) / 2;\n path.quadraticCurveTo(controlX, controlY, startX, firstY);\n\n ctx.fill(path);\n }\n\n protected drawSpikes(\n ctx: CanvasRenderingContext2D,\n frequencyData: Uint8Array,\n ) {\n const canvas = ctx.canvas;\n const waveWidth = canvas.width;\n const waveHeight = canvas.height;\n const paddingOuter = 0.01;\n const availableWidth = waveWidth * (1 - 2 * paddingOuter);\n const startX = waveWidth * paddingOuter;\n\n ctx.clearRect(0, 0, waveWidth, waveHeight);\n const path = new Path2D();\n\n // Draw top curve\n const firstValue = (frequencyData[0] ?? 0) / 255;\n const firstY = (waveHeight - firstValue * waveHeight) / 2;\n path.moveTo(startX, firstY);\n\n // Draw top half\n frequencyData.forEach((value, i) => {\n const normalizedValue = Math.min((value / 255) * 2, 1);\n const x = startX + (i / (frequencyData.length - 1)) * availableWidth;\n const barHeight = normalizedValue * (waveHeight / 2);\n const y = (waveHeight - barHeight * 2) / 2;\n\n if (i === 0) {\n path.moveTo(x, y);\n } else {\n const prevX =\n startX + ((i - 1) / (frequencyData.length - 1)) * availableWidth;\n const prevValue = (frequencyData[i - 1] ?? 0) / 255;\n const prevBarHeight = prevValue * (waveHeight / 2);\n const prevY = (waveHeight - prevBarHeight * 2) / 2;\n const xc = (prevX + x) / 2;\n const yc = (prevY + y) / 2;\n path.quadraticCurveTo(prevX, prevY, xc, yc);\n }\n });\n\n // Draw bottom half\n for (let i = frequencyData.length - 1; i >= 0; i--) {\n const normalizedValue = Math.min(((frequencyData[i] ?? 0) / 255) * 2, 1);\n const x = startX + (i / (frequencyData.length - 1)) * availableWidth;\n const barHeight = normalizedValue * (waveHeight / 2);\n const y = (waveHeight + barHeight * 2) / 2;\n\n if (i === frequencyData.length - 1) {\n path.lineTo(x, y);\n } else {\n const nextX =\n startX + ((i + 1) / (frequencyData.length - 1)) * availableWidth;\n const nextValue = (frequencyData[i + 1] ?? 0) / 255;\n const nextBarHeight = nextValue * (waveHeight / 2);\n const nextY = (waveHeight + nextBarHeight * 2) / 2;\n const xc = (nextX + x) / 2;\n const yc = (nextY + y) / 2;\n path.quadraticCurveTo(nextX, nextY, xc, yc);\n }\n }\n\n // Close the path with a smooth curve\n const lastY = (waveHeight + firstValue * waveHeight) / 2;\n const controlX = startX;\n const controlY = (lastY + firstY) / 2;\n path.quadraticCurveTo(controlX, controlY, startX, firstY);\n\n ctx.fill(path);\n }\n\n frameTask = new Task(this, {\n autoRun: false,\n args: () => {\n return [\n this.targetElement,\n this.targetElement?.frequencyDataTask.value,\n ] as const;\n },\n task: async ([_targetElement, _frequencyData], { signal }) => {\n if (!this.targetElement) return;\n await this.targetElement.frequencyDataTask.taskComplete;\n signal?.throwIfAborted();\n this.ctx ||= this.initCanvas();\n const ctx = this.ctx;\n if (!ctx) return;\n\n const frequencyData = this.targetElement.frequencyDataTask.value;\n const byteTimeData = this.targetElement.byteTimeDomainTask.value;\n if (!frequencyData || !byteTimeData) return;\n\n ctx.save();\n if (this.color === \"currentColor\") {\n const computedStyle = getComputedStyle(this);\n const currentColor = computedStyle.color;\n ctx.strokeStyle = currentColor;\n ctx.fillStyle = currentColor;\n } else {\n ctx.strokeStyle = this.color;\n ctx.fillStyle = this.color;\n }\n\n switch (this.mode) {\n case \"bars\":\n this.drawBars(ctx, frequencyData);\n break;\n case \"bricks\":\n this.drawBricks(ctx, frequencyData);\n break;\n case \"line\":\n this.drawLine(ctx, byteTimeData);\n break;\n case \"curve\":\n this.drawCurve(ctx, byteTimeData);\n break;\n case \"pixel\":\n this.drawPixel(ctx, frequencyData);\n break;\n case \"wave\":\n this.drawWave(ctx, frequencyData);\n break;\n case \"spikes\":\n this.drawSpikes(ctx, frequencyData);\n break;\n case \"roundBars\":\n this.drawRoundBars(ctx, frequencyData);\n break;\n }\n\n ctx.restore();\n },\n });\n\n get durationMs() {\n if (!this.targetElement) return 0;\n return this.targetElement.durationMs;\n }\n\n protected updated(changedProperties: PropertyValueMap<this>): void {\n super.updated(changedProperties);\n\n // Trigger a redraw if any property changes\n if (changedProperties.size > 0) {\n // Request a new frame\n this.frameTask.run();\n }\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"ef-waveform\": EFWaveform & Element;\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { LitElement } from "lit";
|
|
2
|
+
|
|
3
|
+
//#region src/elements/ElementPositionInfo.d.ts
|
|
4
|
+
type Constructor<T = {}> = new (...args: any[]) => T;
|
|
5
|
+
/**
|
|
6
|
+
* Element position information interface.
|
|
7
|
+
* Provides computed position, bounds, and transform information for elements.
|
|
8
|
+
*/
|
|
9
|
+
interface ElementPositionInfo {
|
|
10
|
+
/**
|
|
11
|
+
* The bounding rectangle of the element in screen coordinates.
|
|
12
|
+
*/
|
|
13
|
+
bounds: DOMRect;
|
|
14
|
+
/**
|
|
15
|
+
* The computed transform string (e.g., "translate(10px, 20px) rotate(45deg)").
|
|
16
|
+
*/
|
|
17
|
+
transform: string;
|
|
18
|
+
/**
|
|
19
|
+
* The rotation angle in degrees, extracted from the transform.
|
|
20
|
+
*/
|
|
21
|
+
rotation: number;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Mixin that adds getPositionInfo() method to LitElement.
|
|
25
|
+
* Elements can use this to expose their position information.
|
|
26
|
+
*/
|
|
27
|
+
declare function PositionInfoMixin<T extends Constructor<LitElement>>(superClass: T): T;
|
|
28
|
+
/**
|
|
29
|
+
* Helper function to get position info from any HTMLElement.
|
|
30
|
+
* Reads computed styles and bounding rect to determine position.
|
|
31
|
+
*/
|
|
32
|
+
declare function getPositionInfoFromElement(element: HTMLElement | null): ElementPositionInfo | null;
|
|
33
|
+
//#endregion
|
|
34
|
+
export { ElementPositionInfo, PositionInfoMixin, getPositionInfoFromElement };
|
|
35
|
+
//# sourceMappingURL=ElementPositionInfo.d.ts.map
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
//#region src/elements/ElementPositionInfo.ts
|
|
2
|
+
/**
|
|
3
|
+
* Mixin that adds getPositionInfo() method to LitElement.
|
|
4
|
+
* Elements can use this to expose their position information.
|
|
5
|
+
*/
|
|
6
|
+
function PositionInfoMixin(superClass) {
|
|
7
|
+
class PositionInfoElement extends superClass {
|
|
8
|
+
/**
|
|
9
|
+
* Get position information for this element.
|
|
10
|
+
* Returns computed bounds, transform, and rotation.
|
|
11
|
+
*
|
|
12
|
+
* @public
|
|
13
|
+
*/
|
|
14
|
+
getPositionInfo() {
|
|
15
|
+
return getPositionInfoFromElement(this);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return PositionInfoElement;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Helper function to get position info from any HTMLElement.
|
|
22
|
+
* Reads computed styles and bounding rect to determine position.
|
|
23
|
+
*/
|
|
24
|
+
function getPositionInfoFromElement(element) {
|
|
25
|
+
if (!element) return null;
|
|
26
|
+
const rect = element.getBoundingClientRect();
|
|
27
|
+
const transform = window.getComputedStyle(element).transform;
|
|
28
|
+
let rotation = 0;
|
|
29
|
+
if (transform && transform !== "none") {
|
|
30
|
+
const matrixMatch = transform.match(/matrix\(([^)]+)\)/);
|
|
31
|
+
if (matrixMatch && matrixMatch[1]) {
|
|
32
|
+
const values = matrixMatch[1].split(",").map((v) => parseFloat(v.trim()));
|
|
33
|
+
if (values.length >= 4) {
|
|
34
|
+
const a = values[0];
|
|
35
|
+
const b = values[1];
|
|
36
|
+
rotation = Math.atan2(b, a) * (180 / Math.PI);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
bounds: rect,
|
|
42
|
+
transform: transform || "none",
|
|
43
|
+
rotation
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
//#endregion
|
|
48
|
+
export { PositionInfoMixin, getPositionInfoFromElement };
|
|
49
|
+
//# sourceMappingURL=ElementPositionInfo.js.map
|