@editframe/elements 0.33.0-beta → 0.34.5-beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (251) hide show
  1. package/dist/EF_FRAMEGEN.js +5 -3
  2. package/dist/EF_FRAMEGEN.js.map +1 -1
  3. package/dist/_virtual/{_@oxc-project_runtime@0.94.0 → _@oxc-project_runtime@0.95.0}/helpers/decorate.js +1 -1
  4. package/dist/canvas/EFCanvas.d.ts +7 -4
  5. package/dist/canvas/EFCanvas.js +1 -1
  6. package/dist/canvas/EFCanvasItem.d.ts +4 -4
  7. package/dist/canvas/EFCanvasItem.js +1 -1
  8. package/dist/canvas/overlays/SelectionOverlay.d.ts +95 -0
  9. package/dist/canvas/overlays/SelectionOverlay.js +1 -1
  10. package/dist/canvas/selection/SelectionController.js +7 -11
  11. package/dist/canvas/selection/SelectionController.js.map +1 -1
  12. package/dist/elements/EFAudio.d.ts +25 -7
  13. package/dist/elements/EFAudio.js +31 -61
  14. package/dist/elements/EFAudio.js.map +1 -1
  15. package/dist/elements/EFCaptions.d.ts +65 -52
  16. package/dist/elements/EFCaptions.js +186 -400
  17. package/dist/elements/EFCaptions.js.map +1 -1
  18. package/dist/elements/EFImage.d.ts +34 -6
  19. package/dist/elements/EFImage.js +114 -79
  20. package/dist/elements/EFImage.js.map +1 -1
  21. package/dist/elements/EFMedia/AssetIdMediaEngine.js +17 -17
  22. package/dist/elements/EFMedia/AssetIdMediaEngine.js.map +1 -1
  23. package/dist/elements/EFMedia/AssetMediaEngine.js +41 -25
  24. package/dist/elements/EFMedia/AssetMediaEngine.js.map +1 -1
  25. package/dist/elements/EFMedia/BaseMediaEngine.js +4 -4
  26. package/dist/elements/EFMedia/BaseMediaEngine.js.map +1 -1
  27. package/dist/elements/EFMedia/BufferedSeekingInput.js +1 -1
  28. package/dist/elements/EFMedia/BufferedSeekingInput.js.map +1 -1
  29. package/dist/elements/EFMedia/JitMediaEngine.js +31 -17
  30. package/dist/elements/EFMedia/JitMediaEngine.js.map +1 -1
  31. package/dist/elements/EFMedia/shared/AudioSpanUtils.js +3 -3
  32. package/dist/elements/EFMedia/shared/AudioSpanUtils.js.map +1 -1
  33. package/dist/elements/EFMedia/videoTasks/ScrubInputCache.js +17 -9
  34. package/dist/elements/EFMedia/videoTasks/ScrubInputCache.js.map +1 -1
  35. package/dist/elements/EFMedia.d.ts +66 -20
  36. package/dist/elements/EFMedia.js +412 -30
  37. package/dist/elements/EFMedia.js.map +1 -1
  38. package/dist/elements/EFPanZoom.d.ts +4 -4
  39. package/dist/elements/EFPanZoom.js +1 -1
  40. package/dist/elements/EFSourceMixin.js +43 -15
  41. package/dist/elements/EFSourceMixin.js.map +1 -1
  42. package/dist/elements/EFSurface.d.ts +23 -10
  43. package/dist/elements/EFSurface.js +64 -22
  44. package/dist/elements/EFSurface.js.map +1 -1
  45. package/dist/elements/EFTemporal.d.ts +8 -2
  46. package/dist/elements/EFTemporal.js +42 -31
  47. package/dist/elements/EFTemporal.js.map +1 -1
  48. package/dist/elements/EFText.d.ts +5 -4
  49. package/dist/elements/EFText.js +11 -2
  50. package/dist/elements/EFText.js.map +1 -1
  51. package/dist/elements/EFTextSegment.d.ts +4 -4
  52. package/dist/elements/EFTextSegment.js +1 -1
  53. package/dist/elements/EFThumbnailStrip.d.ts +4 -4
  54. package/dist/elements/EFThumbnailStrip.js +1 -1
  55. package/dist/elements/EFTimegroup.d.ts +22 -8
  56. package/dist/elements/EFTimegroup.js +203 -115
  57. package/dist/elements/EFTimegroup.js.map +1 -1
  58. package/dist/elements/EFVideo.d.ts +57 -20
  59. package/dist/elements/EFVideo.js +324 -72
  60. package/dist/elements/EFVideo.js.map +1 -1
  61. package/dist/elements/EFWaveform.d.ts +33 -7
  62. package/dist/elements/EFWaveform.js +103 -59
  63. package/dist/elements/EFWaveform.js.map +1 -1
  64. package/dist/elements/renderTemporalAudio.js +14 -3
  65. package/dist/elements/renderTemporalAudio.js.map +1 -1
  66. package/dist/getRenderInfo.d.ts +2 -2
  67. package/dist/gui/ContextMixin.js +1 -1
  68. package/dist/gui/Controllable.d.ts +2 -0
  69. package/dist/gui/EFActiveRootTemporal.d.ts +4 -4
  70. package/dist/gui/EFActiveRootTemporal.js +1 -1
  71. package/dist/gui/EFConfiguration.d.ts +4 -4
  72. package/dist/gui/EFConfiguration.js +1 -1
  73. package/dist/gui/EFControls.d.ts +2 -2
  74. package/dist/gui/EFControls.js +1 -1
  75. package/dist/gui/EFDial.d.ts +4 -4
  76. package/dist/gui/EFDial.js +1 -1
  77. package/dist/gui/EFFilmstrip.d.ts +3 -2
  78. package/dist/gui/EFFilmstrip.js +1 -1
  79. package/dist/gui/EFFitScale.js +1 -1
  80. package/dist/gui/EFFocusOverlay.d.ts +4 -4
  81. package/dist/gui/EFFocusOverlay.js +1 -1
  82. package/dist/gui/EFOverlayItem.d.ts +4 -4
  83. package/dist/gui/EFOverlayItem.js +1 -1
  84. package/dist/gui/EFOverlayLayer.d.ts +4 -4
  85. package/dist/gui/EFOverlayLayer.js +1 -1
  86. package/dist/gui/EFPause.d.ts +4 -4
  87. package/dist/gui/EFPause.js +1 -1
  88. package/dist/gui/EFPlay.d.ts +4 -4
  89. package/dist/gui/EFPlay.js +1 -1
  90. package/dist/gui/EFPreview.d.ts +4 -4
  91. package/dist/gui/EFPreview.js +1 -1
  92. package/dist/gui/EFResizableBox.d.ts +4 -4
  93. package/dist/gui/EFResizableBox.js +1 -1
  94. package/dist/gui/EFScrubber.d.ts +4 -4
  95. package/dist/gui/EFScrubber.js +1 -1
  96. package/dist/gui/EFTimeDisplay.d.ts +4 -4
  97. package/dist/gui/EFTimeDisplay.js +1 -1
  98. package/dist/gui/EFTimelineRuler.d.ts +4 -4
  99. package/dist/gui/EFTimelineRuler.js +1 -1
  100. package/dist/gui/EFToggleLoop.d.ts +4 -4
  101. package/dist/gui/EFToggleLoop.js +1 -1
  102. package/dist/gui/EFTogglePlay.d.ts +4 -4
  103. package/dist/gui/EFTogglePlay.js +1 -1
  104. package/dist/gui/EFTransformHandles.d.ts +4 -4
  105. package/dist/gui/EFTransformHandles.js +1 -1
  106. package/dist/gui/EFWorkbench.d.ts +5 -4
  107. package/dist/gui/EFWorkbench.js +1 -1
  108. package/dist/gui/PlaybackController.d.ts +10 -2
  109. package/dist/gui/PlaybackController.js +52 -30
  110. package/dist/gui/PlaybackController.js.map +1 -1
  111. package/dist/gui/TWMixin.js +1 -1
  112. package/dist/gui/TWMixin.js.map +1 -1
  113. package/dist/gui/TargetOrContextMixin.js +1 -1
  114. package/dist/gui/hierarchy/EFHierarchy.d.ts +4 -4
  115. package/dist/gui/hierarchy/EFHierarchy.js +1 -1
  116. package/dist/gui/hierarchy/EFHierarchyItem.d.ts +3 -3
  117. package/dist/gui/hierarchy/EFHierarchyItem.js +1 -1
  118. package/dist/gui/timeline/EFTimeline.d.ts +6 -2
  119. package/dist/gui/timeline/EFTimeline.js +1 -1
  120. package/dist/gui/timeline/EFTimelineRow.d.ts +57 -0
  121. package/dist/gui/timeline/EFTimelineRow.js +1 -1
  122. package/dist/gui/timeline/TrimHandles.d.ts +4 -4
  123. package/dist/gui/timeline/TrimHandles.js +1 -1
  124. package/dist/gui/timeline/tracks/AudioTrack.d.ts +2 -0
  125. package/dist/gui/timeline/tracks/AudioTrack.js +1 -1
  126. package/dist/gui/timeline/tracks/CaptionsTrack.d.ts +58 -0
  127. package/dist/gui/timeline/tracks/CaptionsTrack.js +1 -1
  128. package/dist/gui/timeline/tracks/HTMLTrack.d.ts +13 -0
  129. package/dist/gui/timeline/tracks/HTMLTrack.js +1 -1
  130. package/dist/gui/timeline/tracks/ImageTrack.d.ts +14 -0
  131. package/dist/gui/timeline/tracks/ImageTrack.js +1 -1
  132. package/dist/gui/timeline/tracks/TextTrack.d.ts +26 -0
  133. package/dist/gui/timeline/tracks/TextTrack.js +1 -1
  134. package/dist/gui/timeline/tracks/TimegroupTrack.d.ts +47 -0
  135. package/dist/gui/timeline/tracks/TimegroupTrack.js +4 -12
  136. package/dist/gui/timeline/tracks/TimegroupTrack.js.map +1 -1
  137. package/dist/gui/timeline/tracks/TrackItem.d.ts +81 -0
  138. package/dist/gui/timeline/tracks/TrackItem.js +1 -1
  139. package/dist/gui/timeline/tracks/VideoTrack.d.ts +25 -0
  140. package/dist/gui/timeline/tracks/VideoTrack.js +1 -1
  141. package/dist/gui/timeline/tracks/WaveformTrack.d.ts +14 -0
  142. package/dist/gui/timeline/tracks/WaveformTrack.js +1 -1
  143. package/dist/gui/timeline/tracks/ensureTrackItemInit.d.ts +1 -0
  144. package/dist/gui/timeline/tracks/preloadTracks.d.ts +9 -0
  145. package/dist/gui/tree/EFTree.d.ts +5 -4
  146. package/dist/gui/tree/EFTree.js +1 -1
  147. package/dist/gui/tree/EFTreeItem.d.ts +4 -4
  148. package/dist/gui/tree/EFTreeItem.js +1 -1
  149. package/dist/index.d.ts +4 -1
  150. package/dist/preview/AdaptiveResolutionTracker.js +6 -14
  151. package/dist/preview/AdaptiveResolutionTracker.js.map +1 -1
  152. package/dist/preview/FrameController.d.ts +123 -0
  153. package/dist/preview/FrameController.js +216 -0
  154. package/dist/preview/FrameController.js.map +1 -0
  155. package/dist/preview/RenderContext.d.ts +1 -0
  156. package/dist/preview/RenderContext.js +193 -0
  157. package/dist/preview/RenderContext.js.map +1 -0
  158. package/dist/preview/encoding/canvasEncoder.js +166 -0
  159. package/dist/preview/encoding/canvasEncoder.js.map +1 -0
  160. package/dist/preview/encoding/mainThreadEncoder.js +39 -0
  161. package/dist/preview/encoding/mainThreadEncoder.js.map +1 -0
  162. package/dist/preview/encoding/types.d.ts +1 -0
  163. package/dist/preview/encoding/workerEncoder.js +58 -0
  164. package/dist/preview/encoding/workerEncoder.js.map +1 -0
  165. package/dist/preview/logger.js +41 -0
  166. package/dist/preview/logger.js.map +1 -0
  167. package/dist/preview/previewTypes.js +11 -10
  168. package/dist/preview/previewTypes.js.map +1 -1
  169. package/dist/preview/renderTimegroupPreview.js +259 -236
  170. package/dist/preview/renderTimegroupPreview.js.map +1 -1
  171. package/dist/preview/renderTimegroupToCanvas.d.ts +5 -0
  172. package/dist/preview/renderTimegroupToCanvas.js +99 -489
  173. package/dist/preview/renderTimegroupToCanvas.js.map +1 -1
  174. package/dist/preview/renderTimegroupToVideo.d.ts +1 -0
  175. package/dist/preview/renderTimegroupToVideo.js +80 -22
  176. package/dist/preview/renderTimegroupToVideo.js.map +1 -1
  177. package/dist/preview/renderers.js.map +1 -1
  178. package/dist/preview/rendering/inlineImages.js +56 -0
  179. package/dist/preview/rendering/inlineImages.js.map +1 -0
  180. package/dist/preview/rendering/renderToImage.d.ts +1 -0
  181. package/dist/preview/rendering/renderToImage.js +120 -0
  182. package/dist/preview/rendering/renderToImage.js.map +1 -0
  183. package/dist/preview/rendering/renderToImageForeignObject.js +135 -0
  184. package/dist/preview/rendering/renderToImageForeignObject.js.map +1 -0
  185. package/dist/preview/rendering/renderToImageNative.d.ts +1 -0
  186. package/dist/preview/rendering/renderToImageNative.js +129 -0
  187. package/dist/preview/rendering/renderToImageNative.js.map +1 -0
  188. package/dist/preview/rendering/svgSerializer.js +43 -0
  189. package/dist/preview/rendering/svgSerializer.js.map +1 -0
  190. package/dist/preview/rendering/types.d.ts +2 -0
  191. package/dist/preview/statsTrackingStrategy.js +3 -1
  192. package/dist/preview/statsTrackingStrategy.js.map +1 -1
  193. package/dist/preview/workers/WorkerPool.js +8 -57
  194. package/dist/preview/workers/WorkerPool.js.map +1 -1
  195. package/dist/render/EFRenderAPI.d.ts +35 -0
  196. package/dist/render/EFRenderAPI.js +1 -0
  197. package/dist/render/EFRenderAPI.js.map +1 -1
  198. package/dist/sandbox/PlaybackControls.d.ts +1 -0
  199. package/dist/sandbox/ScenarioRunner.d.ts +1 -0
  200. package/dist/sandbox/defineSandbox.d.ts +1 -0
  201. package/dist/sandbox/index.d.ts +3 -0
  202. package/dist/style.css +3 -0
  203. package/dist/transcoding/types/index.d.ts +6 -3
  204. package/package.json +2 -3
  205. package/test/EFVideo.framegen.browsertest.ts +8 -1
  206. package/test/profilingPlugin.ts +1 -3
  207. package/test/setup.ts +23 -1
  208. package/dist/EF_INTERACTIVE.js +0 -7
  209. package/dist/EF_INTERACTIVE.js.map +0 -1
  210. package/dist/elements/EFMedia/BufferedSeekingInput.d.ts +0 -50
  211. package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.d.ts +0 -12
  212. package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.js +0 -104
  213. package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.js.map +0 -1
  214. package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.js +0 -168
  215. package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.js.map +0 -1
  216. package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.js +0 -46
  217. package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.js.map +0 -1
  218. package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.js +0 -49
  219. package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.js.map +0 -1
  220. package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.js +0 -30
  221. package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.js.map +0 -1
  222. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.js +0 -49
  223. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.js.map +0 -1
  224. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.js +0 -47
  225. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.js.map +0 -1
  226. package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.js +0 -140
  227. package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.js.map +0 -1
  228. package/dist/elements/EFMedia/shared/BufferUtils.d.ts +0 -13
  229. package/dist/elements/EFMedia/shared/BufferUtils.js +0 -86
  230. package/dist/elements/EFMedia/shared/BufferUtils.js.map +0 -1
  231. package/dist/elements/EFMedia/shared/MediaTaskUtils.d.ts +0 -17
  232. package/dist/elements/EFMedia/tasks/makeMediaEngineTask.js +0 -90
  233. package/dist/elements/EFMedia/tasks/makeMediaEngineTask.js.map +0 -1
  234. package/dist/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.js +0 -80
  235. package/dist/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.js.map +0 -1
  236. package/dist/elements/EFMedia/videoTasks/makeScrubVideoInitSegmentFetchTask.js +0 -49
  237. package/dist/elements/EFMedia/videoTasks/makeScrubVideoInitSegmentFetchTask.js.map +0 -1
  238. package/dist/elements/EFMedia/videoTasks/makeScrubVideoInputTask.js +0 -58
  239. package/dist/elements/EFMedia/videoTasks/makeScrubVideoInputTask.js.map +0 -1
  240. package/dist/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.js +0 -71
  241. package/dist/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.js.map +0 -1
  242. package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.js +0 -52
  243. package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.js.map +0 -1
  244. package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.js +0 -50
  245. package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.js.map +0 -1
  246. package/dist/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.js +0 -109
  247. package/dist/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.js.map +0 -1
  248. package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.d.ts +0 -12
  249. package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.js +0 -97
  250. package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.js.map +0 -1
  251. package/dist/elements/SampleBuffer.d.ts +0 -19
@@ -1,58 +0,0 @@
1
- import { BufferedSeekingInput } from "../BufferedSeekingInput.js";
2
- import { EFMedia } from "../../EFMedia.js";
3
- import { Task } from "@lit/task";
4
-
5
- //#region src/elements/EFMedia/videoTasks/makeScrubVideoInputTask.ts
6
- const makeScrubVideoInputTask = (host) => {
7
- let task;
8
- task = new Task(host, {
9
- args: () => [host.scrubVideoInitSegmentFetchTask.value, host.scrubVideoSegmentFetchTask.value],
10
- onError: (error) => {
11
- task.taskComplete.catch(() => {});
12
- 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;
13
- if (error instanceof Error && error.message !== "Scrub init segment or segment is not available" && !error.message.includes("Failed to fetch") && !error.message.includes("File not found") && !error.message.includes("is not valid JSON")) console.error("scrubVideoInputTask error", error);
14
- },
15
- onComplete: (_value) => {},
16
- task: async (_, { signal }) => {
17
- if (host.mediaEngineTask.error) return;
18
- let initSegment;
19
- try {
20
- initSegment = await host.scrubVideoInitSegmentFetchTask.taskComplete;
21
- } catch (error) {
22
- if (error instanceof DOMException && error.name === "AbortError") throw error;
23
- return;
24
- }
25
- signal?.throwIfAborted();
26
- let segment;
27
- try {
28
- segment = await host.scrubVideoSegmentFetchTask.taskComplete;
29
- } catch (error) {
30
- if (error instanceof DOMException && error.name === "AbortError") throw error;
31
- return;
32
- }
33
- signal?.throwIfAborted();
34
- if (!initSegment || !segment) return;
35
- let mediaEngine;
36
- try {
37
- mediaEngine = await host.mediaEngineTask.taskComplete;
38
- } catch (error) {
39
- if (error instanceof Error && error.message === "No valid media source") return;
40
- throw error;
41
- }
42
- signal?.throwIfAborted();
43
- const startTimeOffsetMs = mediaEngine.getScrubVideoRendition()?.startTimeOffsetMs;
44
- const arrayBuffer = await new Blob([initSegment, segment]).arrayBuffer();
45
- signal?.throwIfAborted();
46
- return new BufferedSeekingInput(arrayBuffer, {
47
- videoBufferSize: EFMedia.VIDEO_SAMPLE_BUFFER_SIZE,
48
- audioBufferSize: EFMedia.AUDIO_SAMPLE_BUFFER_SIZE,
49
- startTimeOffsetMs
50
- });
51
- }
52
- });
53
- return task;
54
- };
55
-
56
- //#endregion
57
- export { makeScrubVideoInputTask };
58
- //# sourceMappingURL=makeScrubVideoInputTask.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"makeScrubVideoInputTask.js","names":["task: InputTask","initSegment: ArrayBuffer | undefined","segment: ArrayBuffer | undefined"],"sources":["../../../../src/elements/EFMedia/videoTasks/makeScrubVideoInputTask.ts"],"sourcesContent":["import { Task } from \"@lit/task\";\n\nimport { EFMedia } from \"../../EFMedia\";\nimport type { EFVideo } from \"../../EFVideo\";\nimport { BufferedSeekingInput } from \"../BufferedSeekingInput\";\nimport type { InputTask } from \"../shared/MediaTaskUtils\";\n\nexport const makeScrubVideoInputTask = (host: EFVideo): InputTask => {\n // Capture task reference for use in onError\n let task: InputTask;\n\n task = new Task<\n readonly [ArrayBuffer | undefined, ArrayBuffer | undefined],\n BufferedSeekingInput | undefined\n >(host, {\n args: () =>\n [\n host.scrubVideoInitSegmentFetchTask.value,\n host.scrubVideoSegmentFetchTask.value,\n ] 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 task.taskComplete.catch(() => {});\n \n // Don't log AbortErrors - these are intentional cancellations when element is disconnected\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 - missing scrub segments, fetch failures, and file not found are handled gracefully\n if (\n error instanceof Error &&\n error.message !== \"Scrub init segment or segment is not available\" &&\n !error.message.includes(\"Failed to fetch\") &&\n !error.message.includes(\"File not found\") &&\n !error.message.includes(\"is not valid JSON\")\n ) {\n console.error(\"scrubVideoInputTask error\", error);\n }\n },\n onComplete: (_value) => {},\n task: async (_, { signal }) => {\n // Check if media engine task has errored (no valid source) before attempting to use it\n if (host.mediaEngineTask.error) {\n return undefined;\n }\n \n // Await init segment with proper error handling for AbortErrors\n let initSegment: ArrayBuffer | undefined;\n try {\n initSegment = await host.scrubVideoInitSegmentFetchTask.taskComplete;\n } catch (error) {\n // If aborted, propagate the abort\n if (error instanceof DOMException && error.name === \"AbortError\") {\n throw error;\n }\n // Other errors mean init segment failed - return undefined\n return undefined;\n }\n \n // Check for abort after awaiting init segment\n signal?.throwIfAborted();\n\n // Await segment with proper error handling for AbortErrors\n let segment: ArrayBuffer | undefined;\n try {\n segment = await host.scrubVideoSegmentFetchTask.taskComplete;\n } catch (error) {\n // If aborted, propagate the abort\n if (error instanceof DOMException && error.name === \"AbortError\") {\n throw error;\n }\n // Other errors mean segment failed - return undefined\n return undefined;\n }\n \n // Check for abort after awaiting segment\n signal?.throwIfAborted();\n\n if (!initSegment || !segment) {\n // Scrub segments not available - scrub is optional, return undefined gracefully\n return undefined;\n }\n\n // Get startTimeOffsetMs from the scrub rendition if available\n let mediaEngine;\n try {\n mediaEngine = await host.mediaEngineTask.taskComplete;\n } catch (error) {\n // If media engine task failed (no valid source), return undefined silently\n if (error instanceof Error && error.message === \"No valid media source\") {\n return undefined;\n }\n // Re-throw unexpected errors\n throw error;\n }\n \n // Check for abort after awaiting media engine\n signal?.throwIfAborted();\n\n const scrubRendition = mediaEngine.getScrubVideoRendition();\n const startTimeOffsetMs = scrubRendition?.startTimeOffsetMs;\n\n const arrayBuffer = await new Blob([initSegment, segment]).arrayBuffer();\n \n // Check for abort after expensive arrayBuffer operation\n signal?.throwIfAborted();\n\n const input = new BufferedSeekingInput(arrayBuffer, {\n videoBufferSize: EFMedia.VIDEO_SAMPLE_BUFFER_SIZE,\n audioBufferSize: EFMedia.AUDIO_SAMPLE_BUFFER_SIZE,\n startTimeOffsetMs,\n });\n return input;\n },\n });\n\n return task;\n};\n"],"mappings":";;;;;AAOA,MAAa,2BAA2B,SAA6B;CAEnE,IAAIA;AAEJ,QAAO,IAAI,KAGT,MAAM;EACN,YACE,CACE,KAAK,+BAA+B,OACpC,KAAK,2BAA2B,MACjC;EACH,UAAU,UAAU;AAGlB,QAAK,aAAa,YAAY,GAAG;AAWjC,OAPE,iBAAiB,gBAAgB,MAAM,SAAS,gBAChD,iBAAiB,UACf,MAAM,SAAS,gBACf,MAAM,QAAQ,SAAS,oBAAoB,IAC3C,MAAM,QAAQ,SAAS,6BAA6B,EAItD;AAIF,OACE,iBAAiB,SACjB,MAAM,YAAY,oDAClB,CAAC,MAAM,QAAQ,SAAS,kBAAkB,IAC1C,CAAC,MAAM,QAAQ,SAAS,iBAAiB,IACzC,CAAC,MAAM,QAAQ,SAAS,oBAAoB,CAE5C,SAAQ,MAAM,6BAA6B,MAAM;;EAGrD,aAAa,WAAW;EACxB,MAAM,OAAO,GAAG,EAAE,aAAa;AAE7B,OAAI,KAAK,gBAAgB,MACvB;GAIF,IAAIC;AACJ,OAAI;AACF,kBAAc,MAAM,KAAK,+BAA+B;YACjD,OAAO;AAEd,QAAI,iBAAiB,gBAAgB,MAAM,SAAS,aAClD,OAAM;AAGR;;AAIF,WAAQ,gBAAgB;GAGxB,IAAIC;AACJ,OAAI;AACF,cAAU,MAAM,KAAK,2BAA2B;YACzC,OAAO;AAEd,QAAI,iBAAiB,gBAAgB,MAAM,SAAS,aAClD,OAAM;AAGR;;AAIF,WAAQ,gBAAgB;AAExB,OAAI,CAAC,eAAe,CAAC,QAEnB;GAIF,IAAI;AACJ,OAAI;AACF,kBAAc,MAAM,KAAK,gBAAgB;YAClC,OAAO;AAEd,QAAI,iBAAiB,SAAS,MAAM,YAAY,wBAC9C;AAGF,UAAM;;AAIR,WAAQ,gBAAgB;GAGxB,MAAM,oBADiB,YAAY,wBAAwB,EACjB;GAE1C,MAAM,cAAc,MAAM,IAAI,KAAK,CAAC,aAAa,QAAQ,CAAC,CAAC,aAAa;AAGxE,WAAQ,gBAAgB;AAOxB,UALc,IAAI,qBAAqB,aAAa;IAClD,iBAAiB,QAAQ;IACzB,iBAAiB,QAAQ;IACzB;IACD,CAAC;;EAGL,CAAC;AAEF,QAAO"}
@@ -1,71 +0,0 @@
1
- import { ScrubInputCache } from "./ScrubInputCache.js";
2
- import { Task } from "@lit/task";
3
-
4
- //#region src/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.ts
5
- const scrubInputCache = new ScrubInputCache();
6
- const makeScrubVideoSeekTask = (host) => {
7
- let task;
8
- task = new Task(host, {
9
- args: () => [host.desiredSeekTimeMs],
10
- onError: (error) => {
11
- task.taskComplete.catch(() => {});
12
- 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;
13
- if (error instanceof Error && (error.message === "No valid media source" || error.message.includes("File not found") || error.message.includes("is not valid JSON"))) return;
14
- console.error("scrubVideoSeekTask error", error);
15
- },
16
- onComplete: (_value) => {},
17
- task: async ([desiredSeekTimeMs], { signal }) => {
18
- signal?.throwIfAborted();
19
- const mediaEngine = host.mediaEngineTask.value;
20
- if (!mediaEngine) return;
21
- const scrubRendition = mediaEngine.getScrubVideoRendition();
22
- if (!scrubRendition) return;
23
- const scrubRenditionWithSrc = {
24
- ...scrubRendition,
25
- src: mediaEngine.src
26
- };
27
- const segmentId = mediaEngine.computeSegmentId(desiredSeekTimeMs, scrubRenditionWithSrc);
28
- if (segmentId === void 0) return;
29
- if (!mediaEngine.isSegmentCached(segmentId, scrubRenditionWithSrc)) return;
30
- signal?.throwIfAborted();
31
- try {
32
- const scrubInput = await scrubInputCache.getOrCreateInput(segmentId, async () => {
33
- let initSegment;
34
- let mediaSegment;
35
- try {
36
- [initSegment, mediaSegment] = await Promise.all([mediaEngine.fetchInitSegment(scrubRenditionWithSrc, signal), mediaEngine.fetchMediaSegment(segmentId, scrubRenditionWithSrc, signal)]);
37
- } catch (error) {
38
- if (error instanceof DOMException && error.name === "AbortError") throw error;
39
- if (error instanceof Error && (error.message.includes("401") || error.message.includes("UNAUTHORIZED") || error.message.includes("Failed to fetch") || error.message.includes("File not found") || error.message.includes("Media segment not found") || error.message.includes("Init segment not found") || error.message.includes("Track not found"))) return;
40
- throw error;
41
- }
42
- if (!initSegment || !mediaSegment) return;
43
- signal?.throwIfAborted();
44
- const { BufferedSeekingInput } = await import("../BufferedSeekingInput.js");
45
- const { EFMedia } = await import("../../EFMedia.js");
46
- return new BufferedSeekingInput(await new Blob([initSegment, mediaSegment]).arrayBuffer(), {
47
- videoBufferSize: EFMedia.VIDEO_SAMPLE_BUFFER_SIZE,
48
- audioBufferSize: EFMedia.AUDIO_SAMPLE_BUFFER_SIZE,
49
- startTimeOffsetMs: scrubRendition.startTimeOffsetMs
50
- });
51
- });
52
- if (!scrubInput) return;
53
- signal?.throwIfAborted();
54
- const videoTrack = await scrubInput.getFirstVideoTrack();
55
- if (!videoTrack) return;
56
- signal?.throwIfAborted();
57
- return await scrubInput.seek(videoTrack.id, desiredSeekTimeMs);
58
- } catch (error) {
59
- if (signal?.aborted || error instanceof DOMException && error.name === "AbortError") return;
60
- if (error instanceof RangeError && error.message.includes("Sample not found")) return;
61
- console.warn("Failed to get scrub video sample:", error);
62
- return;
63
- }
64
- }
65
- });
66
- return task;
67
- };
68
-
69
- //#endregion
70
- export { makeScrubVideoSeekTask };
71
- //# sourceMappingURL=makeScrubVideoSeekTask.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"makeScrubVideoSeekTask.js","names":["task: ScrubVideoSeekTask","initSegment: ArrayBuffer | undefined","mediaSegment: ArrayBuffer | undefined"],"sources":["../../../../src/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.ts"],"sourcesContent":["import { Task } from \"@lit/task\";\n\nimport type { VideoSample } from \"mediabunny\";\n\nimport type { EFVideo } from \"../../EFVideo\";\nimport { ScrubInputCache } from \"./ScrubInputCache\";\n\ntype ScrubVideoSeekTask = Task<readonly [number], VideoSample | undefined>;\n\n// Shared cache instance across all scrub seek tasks\nconst scrubInputCache = new ScrubInputCache();\n\nexport const makeScrubVideoSeekTask = (host: EFVideo): ScrubVideoSeekTask => {\n // Capture task reference for use in onError\n let task: ScrubVideoSeekTask;\n\n task = new Task(host, {\n args: () => [host.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 task.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 // Don't log errors when there's no valid media source or file not found - these are expected\n if (error instanceof Error && (\n error.message === \"No valid media source\" ||\n error.message.includes(\"File not found\") ||\n error.message.includes(\"is not valid JSON\")\n )) {\n return;\n }\n console.error(\"scrubVideoSeekTask error\", error);\n },\n onComplete: (_value) => {},\n task: async ([desiredSeekTimeMs], { signal }) => {\n signal?.throwIfAborted();\n\n const mediaEngine = host.mediaEngineTask.value;\n if (!mediaEngine) {\n return undefined;\n }\n\n // Get scrub rendition using the proper interface method\n const scrubRendition = mediaEngine.getScrubVideoRendition();\n\n if (!scrubRendition) {\n return undefined; // No scrub rendition\n }\n\n const scrubRenditionWithSrc = {\n ...scrubRendition,\n src: mediaEngine.src,\n };\n\n // Compute which scrub segment (30s) contains the desired seek time\n const segmentId = mediaEngine.computeSegmentId(\n desiredSeekTimeMs,\n scrubRenditionWithSrc,\n );\n if (segmentId === undefined) {\n return undefined;\n }\n\n // Check if this scrub segment is cached (preloaded by scrub buffer task)\n const isCached = mediaEngine.isSegmentCached(\n segmentId,\n scrubRenditionWithSrc,\n );\n if (!isCached) {\n // Scrub content not preloaded - fail fast, let main video handle it\n // Scrub's job is instant feedback from cached content only\n return undefined;\n }\n\n signal?.throwIfAborted();\n\n try {\n // Get or create BufferedSeekingInput for this scrub segment (30s)\n // This efficiently reuses the same input for seeks within the same 30s range\n const scrubInput = await scrubInputCache.getOrCreateInput(\n segmentId,\n async () => {\n // Try to fetch segments, but return undefined if they fail with expected errors\n let initSegment: ArrayBuffer | undefined;\n let mediaSegment: ArrayBuffer | undefined;\n \n try {\n [initSegment, mediaSegment] = await Promise.all([\n mediaEngine.fetchInitSegment(scrubRenditionWithSrc, signal),\n mediaEngine.fetchMediaSegment(segmentId, scrubRenditionWithSrc, signal),\n ]);\n } catch (error) {\n // If aborted, re-throw to propagate cancellation\n if (error instanceof DOMException && error.name === \"AbortError\") {\n throw error;\n }\n // If fetch fails with expected errors (401, missing segments, etc.), return undefined\n if (\n error instanceof Error &&\n (error.message.includes(\"401\") ||\n error.message.includes(\"UNAUTHORIZED\") ||\n error.message.includes(\"Failed to fetch\") ||\n error.message.includes(\"File not found\") ||\n error.message.includes(\"Media segment not found\") ||\n error.message.includes(\"Init segment not found\") ||\n error.message.includes(\"Track not found\"))\n ) {\n return undefined;\n }\n // Re-throw unexpected errors\n throw error;\n }\n\n if (!initSegment || !mediaSegment) {\n return undefined;\n }\n signal?.throwIfAborted();\n\n const { BufferedSeekingInput } =\n await import(\"../BufferedSeekingInput.js\");\n const { EFMedia } = await import(\"../../EFMedia.js\");\n\n return new BufferedSeekingInput(\n await new Blob([initSegment, mediaSegment]).arrayBuffer(),\n {\n videoBufferSize: EFMedia.VIDEO_SAMPLE_BUFFER_SIZE,\n audioBufferSize: EFMedia.AUDIO_SAMPLE_BUFFER_SIZE,\n startTimeOffsetMs: scrubRendition.startTimeOffsetMs,\n },\n );\n },\n );\n\n if (!scrubInput) {\n return undefined;\n }\n\n signal?.throwIfAborted();\n\n // Get video track and seek to precise time within the 30s scrub segment\n const videoTrack = await scrubInput.getFirstVideoTrack();\n if (!videoTrack) {\n return undefined;\n }\n\n signal?.throwIfAborted();\n\n const sample = (await scrubInput.seek(\n videoTrack.id,\n desiredSeekTimeMs,\n )) as unknown as VideoSample | undefined;\n\n return sample;\n } catch (error) {\n // If aborted, return undefined silently\n if (signal?.aborted || (error instanceof DOMException && error.name === \"AbortError\")) {\n return undefined;\n }\n // Don't warn for RangeError about sample not found - this is expected when seeking\n // outside the segment range (e.g., seeking beyond video duration or outside loaded segment)\n if (error instanceof RangeError && error.message.includes(\"Sample not found\")) {\n return undefined;\n }\n console.warn(\"Failed to get scrub video sample:\", error);\n return undefined;\n }\n },\n });\n\n return task;\n};\n"],"mappings":";;;;AAUA,MAAM,kBAAkB,IAAI,iBAAiB;AAE7C,MAAa,0BAA0B,SAAsC;CAE3E,IAAIA;AAEJ,QAAO,IAAI,KAAK,MAAM;EACpB,YAAY,CAAC,KAAK,kBAAkB;EACpC,UAAU,UAAU;AAGlB,QAAK,aAAa,YAAY,GAAG;AAWjC,OAPE,iBAAiB,gBAAgB,MAAM,SAAS,gBAChD,iBAAiB,UACf,MAAM,SAAS,gBACf,MAAM,QAAQ,SAAS,oBAAoB,IAC3C,MAAM,QAAQ,SAAS,6BAA6B,EAItD;AAIF,OAAI,iBAAiB,UACnB,MAAM,YAAY,2BAClB,MAAM,QAAQ,SAAS,iBAAiB,IACxC,MAAM,QAAQ,SAAS,oBAAoB,EAE3C;AAEF,WAAQ,MAAM,4BAA4B,MAAM;;EAElD,aAAa,WAAW;EACxB,MAAM,OAAO,CAAC,oBAAoB,EAAE,aAAa;AAC/C,WAAQ,gBAAgB;GAExB,MAAM,cAAc,KAAK,gBAAgB;AACzC,OAAI,CAAC,YACH;GAIF,MAAM,iBAAiB,YAAY,wBAAwB;AAE3D,OAAI,CAAC,eACH;GAGF,MAAM,wBAAwB;IAC5B,GAAG;IACH,KAAK,YAAY;IAClB;GAGD,MAAM,YAAY,YAAY,iBAC5B,mBACA,sBACD;AACD,OAAI,cAAc,OAChB;AAQF,OAAI,CAJa,YAAY,gBAC3B,WACA,sBACD,CAIC;AAGF,WAAQ,gBAAgB;AAExB,OAAI;IAGF,MAAM,aAAa,MAAM,gBAAgB,iBACvC,WACA,YAAY;KAEV,IAAIC;KACJ,IAAIC;AAEJ,SAAI;AACF,OAAC,aAAa,gBAAgB,MAAM,QAAQ,IAAI,CAC9C,YAAY,iBAAiB,uBAAuB,OAAO,EAC3D,YAAY,kBAAkB,WAAW,uBAAuB,OAAO,CACxE,CAAC;cACK,OAAO;AAEd,UAAI,iBAAiB,gBAAgB,MAAM,SAAS,aAClD,OAAM;AAGR,UACE,iBAAiB,UAChB,MAAM,QAAQ,SAAS,MAAM,IAC5B,MAAM,QAAQ,SAAS,eAAe,IACtC,MAAM,QAAQ,SAAS,kBAAkB,IACzC,MAAM,QAAQ,SAAS,iBAAiB,IACxC,MAAM,QAAQ,SAAS,0BAA0B,IACjD,MAAM,QAAQ,SAAS,yBAAyB,IAChD,MAAM,QAAQ,SAAS,kBAAkB,EAE3C;AAGF,YAAM;;AAGR,SAAI,CAAC,eAAe,CAAC,aACnB;AAEF,aAAQ,gBAAgB;KAExB,MAAM,EAAE,yBACN,MAAM,OAAO;KACf,MAAM,EAAE,YAAY,MAAM,OAAO;AAEjC,YAAO,IAAI,qBACT,MAAM,IAAI,KAAK,CAAC,aAAa,aAAa,CAAC,CAAC,aAAa,EACzD;MACE,iBAAiB,QAAQ;MACzB,iBAAiB,QAAQ;MACzB,mBAAmB,eAAe;MACnC,CACF;MAEJ;AAED,QAAI,CAAC,WACH;AAGF,YAAQ,gBAAgB;IAGxB,MAAM,aAAa,MAAM,WAAW,oBAAoB;AACxD,QAAI,CAAC,WACH;AAGF,YAAQ,gBAAgB;AAOxB,WALgB,MAAM,WAAW,KAC/B,WAAW,IACX,kBACD;YAGM,OAAO;AAEd,QAAI,QAAQ,WAAY,iBAAiB,gBAAgB,MAAM,SAAS,aACtE;AAIF,QAAI,iBAAiB,cAAc,MAAM,QAAQ,SAAS,mBAAmB,CAC3E;AAEF,YAAQ,KAAK,qCAAqC,MAAM;AACxD;;;EAGL,CAAC;AAEF,QAAO"}
@@ -1,52 +0,0 @@
1
- import { AssetMediaEngine } from "../AssetMediaEngine.js";
2
- import { getLatestMediaEngine } from "../tasks/makeMediaEngineTask.js";
3
- import { Task } from "@lit/task";
4
-
5
- //#region src/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.ts
6
- const makeScrubVideoSegmentFetchTask = (host) => {
7
- let task;
8
- task = new Task(host, {
9
- args: () => [host.mediaEngineTask.value, host.scrubVideoSegmentIdTask.value],
10
- onError: (error) => {
11
- task.taskComplete.catch(() => {});
12
- 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;
13
- if (error instanceof Error && error.message !== "Scrub segment ID is not available for video" && error.message !== "No scrub rendition available") console.error("scrubVideoSegmentFetchTask error", error);
14
- },
15
- onComplete: (_value) => {},
16
- task: async ([mediaEngineValue], { signal }) => {
17
- if (host.mediaEngineTask.error || !mediaEngineValue) return;
18
- let mediaEngine;
19
- try {
20
- mediaEngine = await getLatestMediaEngine(host, signal);
21
- } catch (error) {
22
- if (error instanceof Error && error.message === "No valid media source") return;
23
- throw error;
24
- }
25
- if (!mediaEngine) return;
26
- const segmentId = await host.scrubVideoSegmentIdTask.taskComplete;
27
- signal?.throwIfAborted();
28
- if (segmentId === void 0) return;
29
- const scrubRendition = mediaEngine.getScrubVideoRendition();
30
- if (!scrubRendition) return;
31
- if (mediaEngine instanceof AssetMediaEngine && scrubRendition.trackId !== -1) {
32
- const trackData = mediaEngine.data?.[scrubRendition.trackId];
33
- if (!trackData?.segments || segmentId >= trackData.segments.length) return;
34
- }
35
- try {
36
- return await mediaEngine.fetchMediaSegment(segmentId, {
37
- ...scrubRendition,
38
- src: mediaEngine.src
39
- }, signal);
40
- } catch (error) {
41
- if (error instanceof DOMException && error.name === "AbortError") throw error;
42
- if (error instanceof Error && (error.message.includes("Media segment not found") || error.message.includes("Track not found") || error.message.includes("Failed to fetch") || error.message.includes("File not found"))) return;
43
- throw error;
44
- }
45
- }
46
- });
47
- return task;
48
- };
49
-
50
- //#endregion
51
- export { makeScrubVideoSegmentFetchTask };
52
- //# sourceMappingURL=makeScrubVideoSegmentFetchTask.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"makeScrubVideoSegmentFetchTask.js","names":["task: ScrubVideoSegmentFetchTask"],"sources":["../../../../src/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.ts"],"sourcesContent":["import { Task } from \"@lit/task\";\nimport type { MediaEngine } from \"../../../transcoding/types\";\nimport type { EFVideo } from \"../../EFVideo\";\nimport { AssetMediaEngine } from \"../AssetMediaEngine\";\nimport { getLatestMediaEngine } from \"../tasks/makeMediaEngineTask\";\n\ntype ScrubVideoSegmentFetchTask = Task<\n readonly [MediaEngine | undefined, number | undefined],\n ArrayBuffer\n>;\n\nexport const makeScrubVideoSegmentFetchTask = (\n host: EFVideo,\n): ScrubVideoSegmentFetchTask => {\n // Capture task reference for use in onError\n let task: ScrubVideoSegmentFetchTask;\n\n task = new Task(host, {\n args: () =>\n [host.mediaEngineTask.value, host.scrubVideoSegmentIdTask.value] 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 task.taskComplete.catch(() => {});\n \n // Don't log AbortErrors - these are expected when tasks are cancelled\n if (\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 return;\n }\n // Only log unexpected errors - missing scrub rendition/segment is handled gracefully above\n if (\n error instanceof Error &&\n error.message !== \"Scrub segment ID is not available for video\" &&\n error.message !== \"No scrub rendition available\"\n ) {\n console.error(\"scrubVideoSegmentFetchTask error\", error);\n }\n },\n onComplete: (_value) => {},\n task: async ([mediaEngineValue], { signal }) => {\n // Check if media engine task has errored (no valid source) before attempting to use it\n if (host.mediaEngineTask.error || !mediaEngineValue) {\n return undefined as any;\n }\n \n let mediaEngine;\n try {\n mediaEngine = await getLatestMediaEngine(host, signal);\n } catch (error) {\n // If media engine task failed (no valid source), return undefined silently\n if (error instanceof Error && error.message === \"No valid media source\") {\n return undefined as any;\n }\n // Re-throw unexpected errors\n throw error;\n }\n \n // Return undefined if no valid media engine (no valid source)\n if (!mediaEngine) {\n return undefined as any;\n }\n const segmentId = await host.scrubVideoSegmentIdTask.taskComplete;\n \n // Check for abort after awaiting segment ID\n signal?.throwIfAborted();\n \n if (segmentId === undefined) {\n // Scrub segment ID not available - scrub is optional, return undefined\n return undefined as any; // Task expects ArrayBuffer, but undefined indicates unavailable\n }\n\n // Get scrub rendition using the proper interface method\n const scrubRendition = mediaEngine.getScrubVideoRendition();\n\n if (!scrubRendition) {\n // No scrub rendition available - this is fine, scrub is optional\n // Return undefined instead of throwing to avoid error noise\n return undefined as any; // Task expects ArrayBuffer, but undefined indicates unavailable\n }\n\n // Check if the segment exists in AssetMediaEngine data before fetching\n // Scrub track uses trackId -1, so check for that\n if (mediaEngine instanceof AssetMediaEngine && scrubRendition.trackId !== -1) {\n // @ts-expect-error - data is protected but we need to check segment existence\n const trackData = (mediaEngine as any).data?.[scrubRendition.trackId];\n if (!trackData?.segments || segmentId >= trackData.segments.length) {\n // Segment doesn't exist in the data - don't fetch\n return undefined as any;\n }\n }\n\n // Try to fetch the segment, but return undefined if it fails\n try {\n return await mediaEngine.fetchMediaSegment(\n segmentId,\n {\n ...scrubRendition,\n src: mediaEngine.src, // Ensure src is set\n },\n signal,\n );\n } catch (error) {\n // If aborted, re-throw to propagate cancellation\n if (error instanceof DOMException && error.name === \"AbortError\") {\n throw error;\n }\n // If segment doesn't exist or fetch fails, return undefined gracefully\n if (\n error instanceof Error &&\n (error.message.includes(\"Media segment not found\") ||\n error.message.includes(\"Track not found\") ||\n error.message.includes(\"Failed to fetch\") ||\n error.message.includes(\"File not found\"))\n ) {\n return undefined as any;\n }\n // Re-throw unexpected errors\n throw error;\n }\n },\n });\n\n return task;\n};\n"],"mappings":";;;;;AAWA,MAAa,kCACX,SAC+B;CAE/B,IAAIA;AAEJ,QAAO,IAAI,KAAK,MAAM;EACpB,YACE,CAAC,KAAK,gBAAgB,OAAO,KAAK,wBAAwB,MAAM;EAClE,UAAU,UAAU;AAGlB,QAAK,aAAa,YAAY,GAAG;AAGjC,OACG,iBAAiB,gBAAgB,MAAM,SAAS,gBAChD,iBAAiB,UAChB,MAAM,SAAS,gBACf,MAAM,SAAS,SAAS,oBAAoB,IAC5C,MAAM,SAAS,SAAS,6BAA6B,EAGvD;AAGF,OACE,iBAAiB,SACjB,MAAM,YAAY,iDAClB,MAAM,YAAY,+BAElB,SAAQ,MAAM,oCAAoC,MAAM;;EAG5D,aAAa,WAAW;EACxB,MAAM,OAAO,CAAC,mBAAmB,EAAE,aAAa;AAE9C,OAAI,KAAK,gBAAgB,SAAS,CAAC,iBACjC;GAGF,IAAI;AACJ,OAAI;AACF,kBAAc,MAAM,qBAAqB,MAAM,OAAO;YAC/C,OAAO;AAEd,QAAI,iBAAiB,SAAS,MAAM,YAAY,wBAC9C;AAGF,UAAM;;AAIR,OAAI,CAAC,YACH;GAEF,MAAM,YAAY,MAAM,KAAK,wBAAwB;AAGrD,WAAQ,gBAAgB;AAExB,OAAI,cAAc,OAEhB;GAIF,MAAM,iBAAiB,YAAY,wBAAwB;AAE3D,OAAI,CAAC,eAGH;AAKF,OAAI,uBAAuB,oBAAoB,eAAe,YAAY,IAAI;IAE5E,MAAM,YAAa,YAAoB,OAAO,eAAe;AAC7D,QAAI,CAAC,WAAW,YAAY,aAAa,UAAU,SAAS,OAE1D;;AAKJ,OAAI;AACF,WAAO,MAAM,YAAY,kBACvB,WACA;KACE,GAAG;KACH,KAAK,YAAY;KAClB,EACD,OACD;YACM,OAAO;AAEd,QAAI,iBAAiB,gBAAgB,MAAM,SAAS,aAClD,OAAM;AAGR,QACE,iBAAiB,UAChB,MAAM,QAAQ,SAAS,0BAA0B,IAChD,MAAM,QAAQ,SAAS,kBAAkB,IACzC,MAAM,QAAQ,SAAS,kBAAkB,IACzC,MAAM,QAAQ,SAAS,iBAAiB,EAE1C;AAGF,UAAM;;;EAGX,CAAC;AAEF,QAAO"}
@@ -1,50 +0,0 @@
1
- import { AssetMediaEngine } from "../AssetMediaEngine.js";
2
- import { getLatestMediaEngine } from "../tasks/makeMediaEngineTask.js";
3
- import { Task } from "@lit/task";
4
-
5
- //#region src/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.ts
6
- const makeScrubVideoSegmentIdTask = (host) => {
7
- let task;
8
- task = new Task(host, {
9
- args: () => [host.mediaEngineTask.value, host.desiredSeekTimeMs],
10
- onError: (error) => {
11
- task.taskComplete.catch(() => {});
12
- 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;
13
- if (error instanceof Error && (error.message === "No valid media source" || error.message.includes("File not found") || error.message.includes("is not valid JSON"))) return;
14
- console.error("scrubVideoSegmentIdTask error", error);
15
- },
16
- onComplete: (_value) => {},
17
- task: async ([mediaEngineValue, targetSeekTimeMs], { signal }) => {
18
- if (host.mediaEngineTask.error || !mediaEngineValue) return;
19
- let mediaEngine;
20
- try {
21
- mediaEngine = await getLatestMediaEngine(host, signal);
22
- } catch (error) {
23
- if (error instanceof Error && error.message === "No valid media source") return;
24
- throw error;
25
- }
26
- if (!mediaEngine) return;
27
- signal?.throwIfAborted();
28
- const scrubRendition = mediaEngine.getScrubVideoRendition();
29
- if (!scrubRendition) return;
30
- if (mediaEngine instanceof AssetMediaEngine && scrubRendition.trackId !== -1) {
31
- const trackData = mediaEngine.data?.[scrubRendition.trackId];
32
- if (!trackData || !trackData.segments || trackData.segments.length === 0) return;
33
- }
34
- try {
35
- return mediaEngine.computeSegmentId(targetSeekTimeMs, {
36
- ...scrubRendition,
37
- src: mediaEngine.src
38
- });
39
- } catch (error) {
40
- if (error instanceof Error && (error.message.includes("Track not found") || error.message.includes("Track ID is required"))) return;
41
- throw error;
42
- }
43
- }
44
- });
45
- return task;
46
- };
47
-
48
- //#endregion
49
- export { makeScrubVideoSegmentIdTask };
50
- //# sourceMappingURL=makeScrubVideoSegmentIdTask.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"makeScrubVideoSegmentIdTask.js","names":["task: ScrubVideoSegmentIdTask"],"sources":["../../../../src/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.ts"],"sourcesContent":["import { Task } from \"@lit/task\";\nimport type { MediaEngine } from \"../../../transcoding/types\";\nimport type { EFVideo } from \"../../EFVideo\";\nimport { AssetMediaEngine } from \"../AssetMediaEngine\";\nimport { getLatestMediaEngine } from \"../tasks/makeMediaEngineTask\";\n\ntype ScrubVideoSegmentIdTask = Task<readonly [MediaEngine | undefined, number], number | undefined>;\n\nexport const makeScrubVideoSegmentIdTask = (\n host: EFVideo,\n): ScrubVideoSegmentIdTask => {\n // Capture task reference for use in onError\n let task: ScrubVideoSegmentIdTask;\n\n task = new Task(host, {\n args: () => [host.mediaEngineTask.value, host.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 task.taskComplete.catch(() => {});\n \n // Don't log AbortErrors - check this FIRST before instanceof Error\n // because DOMException might not pass instanceof Error in all cases\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 // Don't log errors when there's no valid media source or file not found - these are expected\n if (error instanceof Error && (\n error.message === \"No valid media source\" ||\n error.message.includes(\"File not found\") ||\n error.message.includes(\"is not valid JSON\")\n )) {\n return;\n }\n console.error(\"scrubVideoSegmentIdTask error\", error);\n },\n onComplete: (_value) => {},\n task: async ([mediaEngineValue, targetSeekTimeMs], { signal }) => {\n // Check if media engine task has errored (no valid source) before attempting to use it\n if (host.mediaEngineTask.error || !mediaEngineValue) {\n return undefined;\n }\n \n let mediaEngine;\n try {\n mediaEngine = await getLatestMediaEngine(host, signal);\n } catch (error) {\n // If media engine task failed (no valid source), return undefined silently\n if (error instanceof Error && error.message === \"No valid media source\") {\n return undefined;\n }\n // Re-throw unexpected errors\n throw error;\n }\n \n // Return undefined if no valid media engine (no valid source)\n if (!mediaEngine) {\n return undefined;\n }\n signal?.throwIfAborted(); // Abort if a new seek started\n\n // Get scrub rendition using the proper interface method\n const scrubRendition = mediaEngine.getScrubVideoRendition();\n\n if (!scrubRendition) {\n return undefined; // No scrub rendition available\n }\n\n // Check if the track exists in AssetMediaEngine data before computing segment ID\n // Scrub track uses trackId -1, which is handled specially, so skip check for that\n if (mediaEngine instanceof AssetMediaEngine && scrubRendition.trackId !== -1) {\n // @ts-expect-error - data is protected but we need to check track existence\n const trackData = (mediaEngine as any).data?.[scrubRendition.trackId];\n if (!trackData || !trackData.segments || trackData.segments.length === 0) {\n // Track doesn't exist or has no segments - don't compute segment ID\n return undefined;\n }\n }\n\n try {\n return mediaEngine.computeSegmentId(targetSeekTimeMs, {\n ...scrubRendition,\n src: mediaEngine.src, // Ensure src is set\n });\n } catch (error) {\n // If track doesn't exist or segment computation fails, return undefined gracefully\n if (\n error instanceof Error &&\n (error.message.includes(\"Track not found\") ||\n error.message.includes(\"Track ID is required\"))\n ) {\n return undefined;\n }\n // Re-throw unexpected errors\n throw error;\n }\n },\n });\n\n return task;\n};\n"],"mappings":";;;;;AAQA,MAAa,+BACX,SAC4B;CAE5B,IAAIA;AAEJ,QAAO,IAAI,KAAK,MAAM;EACpB,YAAY,CAAC,KAAK,gBAAgB,OAAO,KAAK,kBAAkB;EAChE,UAAU,UAAU;AAGlB,QAAK,aAAa,YAAY,GAAG;AAYjC,OAPG,iBAAiB,gBAAgB,MAAM,SAAS,gBAChD,iBAAiB,UAChB,MAAM,SAAS,gBACf,MAAM,SAAS,SAAS,oBAAoB,IAC5C,MAAM,SAAS,SAAS,6BAA6B,EAIvD;AAIF,OAAI,iBAAiB,UACnB,MAAM,YAAY,2BAClB,MAAM,QAAQ,SAAS,iBAAiB,IACxC,MAAM,QAAQ,SAAS,oBAAoB,EAE3C;AAEF,WAAQ,MAAM,iCAAiC,MAAM;;EAEvD,aAAa,WAAW;EACxB,MAAM,OAAO,CAAC,kBAAkB,mBAAmB,EAAE,aAAa;AAEhE,OAAI,KAAK,gBAAgB,SAAS,CAAC,iBACjC;GAGF,IAAI;AACJ,OAAI;AACF,kBAAc,MAAM,qBAAqB,MAAM,OAAO;YAC/C,OAAO;AAEd,QAAI,iBAAiB,SAAS,MAAM,YAAY,wBAC9C;AAGF,UAAM;;AAIR,OAAI,CAAC,YACH;AAEF,WAAQ,gBAAgB;GAGxB,MAAM,iBAAiB,YAAY,wBAAwB;AAE3D,OAAI,CAAC,eACH;AAKF,OAAI,uBAAuB,oBAAoB,eAAe,YAAY,IAAI;IAE5E,MAAM,YAAa,YAAoB,OAAO,eAAe;AAC7D,QAAI,CAAC,aAAa,CAAC,UAAU,YAAY,UAAU,SAAS,WAAW,EAErE;;AAIJ,OAAI;AACF,WAAO,YAAY,iBAAiB,kBAAkB;KACpD,GAAG;KACH,KAAK,YAAY;KAClB,CAAC;YACK,OAAO;AAEd,QACE,iBAAiB,UAChB,MAAM,QAAQ,SAAS,kBAAkB,IACxC,MAAM,QAAQ,SAAS,uBAAuB,EAEhD;AAGF,UAAM;;;EAGX,CAAC;AAEF,QAAO"}
@@ -1,109 +0,0 @@
1
- import { withSpan } from "../../../otel/tracingHelpers.js";
2
- import { getLatestMediaEngine } from "../tasks/makeMediaEngineTask.js";
3
- import { BufferedSeekingInput } from "../BufferedSeekingInput.js";
4
- import { EFMedia } from "../../EFMedia.js";
5
- import { ScrubInputCache } from "./ScrubInputCache.js";
6
- import { MainVideoInputCache } from "./MainVideoInputCache.js";
7
- import { Task } from "@lit/task";
8
-
9
- //#region src/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.ts
10
- new ScrubInputCache();
11
- const mainVideoInputCache = new MainVideoInputCache();
12
- const makeUnifiedVideoSeekTask = (host) => {
13
- return new Task(host, {
14
- autoRun: false,
15
- args: () => [host.desiredSeekTimeMs],
16
- onError: (error) => {
17
- 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;
18
- if (error instanceof Error && error.message !== "Video rendition unavailable after checking videoRendition exists" && !error.message.includes("No valid media source") && !error.message.includes("Sample not found for time")) console.error("unifiedVideoSeekTask error", error);
19
- },
20
- onComplete: (_value) => {},
21
- task: async ([desiredSeekTimeMs], { signal }) => {
22
- const mediaEngine = await getLatestMediaEngine(host, signal);
23
- if (!mediaEngine) return void 0;
24
- signal?.throwIfAborted();
25
- const mainRendition = mediaEngine.videoRendition;
26
- if (mainRendition) {
27
- const mainSegmentId = mediaEngine.computeSegmentId(desiredSeekTimeMs, mainRendition);
28
- if (mainSegmentId !== void 0 && mediaEngine.isSegmentCached(mainSegmentId, mainRendition)) {
29
- const result$1 = await getMainVideoSample(host, mediaEngine, desiredSeekTimeMs, signal);
30
- signal?.throwIfAborted();
31
- return result$1;
32
- }
33
- }
34
- const result = await getMainVideoSample(host, mediaEngine, desiredSeekTimeMs, signal);
35
- signal?.throwIfAborted();
36
- return result;
37
- }
38
- });
39
- };
40
- /**
41
- * Get main video sample (slower path with fetching)
42
- */
43
- async function getMainVideoSample(_host, mediaEngine, desiredSeekTimeMs, signal) {
44
- return withSpan("video.getMainVideoSample", {
45
- desiredSeekTimeMs,
46
- src: mediaEngine.src || "unknown"
47
- }, void 0, async (span) => {
48
- try {
49
- const videoRendition = mediaEngine.getVideoRendition();
50
- if (!videoRendition) {
51
- span.setAttribute("result", "no-video-rendition");
52
- return;
53
- }
54
- const segmentId = mediaEngine.computeSegmentId(desiredSeekTimeMs, videoRendition);
55
- if (segmentId === void 0) {
56
- span.setAttribute("result", "no-segment-id");
57
- return;
58
- }
59
- span.setAttribute("segmentId", segmentId);
60
- const mainInput = await mainVideoInputCache.getOrCreateInput(mediaEngine.src, segmentId, videoRendition.id, async () => {
61
- let initSegment;
62
- let mediaSegment;
63
- try {
64
- [initSegment, mediaSegment] = await Promise.all([mediaEngine.fetchInitSegment(videoRendition, signal), mediaEngine.fetchMediaSegment(segmentId, videoRendition, signal)]);
65
- } catch (error) {
66
- if (error instanceof DOMException && error.name === "AbortError") throw error;
67
- if (error instanceof Error && (error.message.includes("401") || error.message.includes("UNAUTHORIZED") || error.message.includes("Failed to fetch") || error.message.includes("File not found") || error.message.includes("Media segment not found") || error.message.includes("Init segment not found") || error.message.includes("Track not found"))) return;
68
- throw error;
69
- }
70
- if (!initSegment || !mediaSegment) return;
71
- signal?.throwIfAborted();
72
- signal?.throwIfAborted();
73
- const combinedBlob = new Blob([initSegment, mediaSegment]);
74
- signal?.throwIfAborted();
75
- const arrayBuffer = await combinedBlob.arrayBuffer();
76
- signal?.throwIfAborted();
77
- return new BufferedSeekingInput(arrayBuffer, {
78
- videoBufferSize: EFMedia.VIDEO_SAMPLE_BUFFER_SIZE,
79
- audioBufferSize: EFMedia.AUDIO_SAMPLE_BUFFER_SIZE,
80
- startTimeOffsetMs: videoRendition.startTimeOffsetMs
81
- });
82
- });
83
- if (!mainInput) {
84
- span.setAttribute("result", "no-segments");
85
- return;
86
- }
87
- signal?.throwIfAborted();
88
- const videoTrack = await mainInput.getFirstVideoTrack();
89
- if (!videoTrack) {
90
- span.setAttribute("result", "no-video-track");
91
- return;
92
- }
93
- signal?.throwIfAborted();
94
- const sample = await mainInput.seek(videoTrack.id, desiredSeekTimeMs);
95
- span.setAttribute("result", sample ? "success" : "no-sample");
96
- return sample;
97
- } catch (error) {
98
- if (signal?.aborted) {
99
- span.setAttribute("result", "aborted");
100
- return;
101
- }
102
- throw error;
103
- }
104
- });
105
- }
106
-
107
- //#endregion
108
- export { makeUnifiedVideoSeekTask };
109
- //# sourceMappingURL=makeUnifiedVideoSeekTask.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"makeUnifiedVideoSeekTask.js","names":["result","initSegment: ArrayBuffer | undefined","mediaSegment: ArrayBuffer | undefined"],"sources":["../../../../src/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.ts"],"sourcesContent":["import { Task } from \"@lit/task\";\nimport type { VideoSample } from \"mediabunny\";\nimport { withSpan } from \"../../../otel/tracingHelpers.js\";\nimport type { VideoRendition } from \"../../../transcoding/types\";\nimport { EFMedia } from \"../../EFMedia.js\";\nimport type { EFVideo } from \"../../EFVideo\";\nimport { BufferedSeekingInput } from \"../BufferedSeekingInput.js\";\nimport { getLatestMediaEngine } from \"../tasks/makeMediaEngineTask\";\nimport { MainVideoInputCache } from \"./MainVideoInputCache\";\nimport { ScrubInputCache } from \"./ScrubInputCache\";\n\ntype UnifiedVideoSeekTask = Task<readonly [number], VideoSample | undefined>;\n\n// Shared cache for scrub inputs\nconst scrubInputCache = new ScrubInputCache();\n\n// Shared cache for main video inputs\nconst mainVideoInputCache = new MainVideoInputCache();\n\nexport const makeUnifiedVideoSeekTask = (\n host: EFVideo,\n): UnifiedVideoSeekTask => {\n return new Task(host, {\n autoRun: false,\n args: () => [host.desiredSeekTimeMs] as const,\n onError: (error) => {\n // Don't log AbortErrors - these are expected when tasks are cancelled\n if (\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 return;\n }\n // Only log unexpected errors - expected conditions handled gracefully\n if (\n error instanceof Error &&\n error.message !== \"Video rendition unavailable after checking videoRendition exists\" &&\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(\"unifiedVideoSeekTask error\", error);\n }\n },\n onComplete: (_value) => {},\n task: async ([desiredSeekTimeMs], { signal }) => {\n const mediaEngine = await getLatestMediaEngine(host, signal);\n if (!mediaEngine) return undefined;\n signal?.throwIfAborted();\n\n // FIRST: Check if main quality content is already cached\n const mainRendition = mediaEngine.videoRendition;\n if (mainRendition) {\n const mainSegmentId = mediaEngine.computeSegmentId(\n desiredSeekTimeMs,\n mainRendition,\n );\n if (\n mainSegmentId !== undefined &&\n mediaEngine.isSegmentCached(mainSegmentId, mainRendition)\n ) {\n const result = await getMainVideoSample(\n host,\n mediaEngine,\n desiredSeekTimeMs,\n signal,\n );\n\n signal?.throwIfAborted();\n\n return result;\n }\n }\n\n // SECOND: Scrub track disabled for thumbnail generation\n // Testing showed scrub track is actually SLOWER than main video for this use case\n // because the entire 42MB file needs to be parsed vs efficient segment-based seeking.\n // The scrub track is designed for interactive scrubbing preview, not batch thumbnail gen.\n\n // THIRD: Fetch main video path\n const result = await getMainVideoSample(\n host,\n mediaEngine,\n desiredSeekTimeMs,\n signal,\n );\n\n signal?.throwIfAborted();\n\n return result;\n },\n });\n};\n\n/**\n * Try to get scrub sample from cache (instant if available)\n */\nasync function tryGetScrubSample(\n mediaEngine: any,\n desiredSeekTimeMs: number,\n signal: AbortSignal,\n): Promise<VideoSample | undefined> {\n return withSpan(\n \"video.tryGetScrubSample\",\n {\n desiredSeekTimeMs,\n src: mediaEngine.src || \"unknown\",\n },\n undefined,\n async (span) => {\n try {\n // Get scrub rendition\n let scrubRendition: VideoRendition | undefined;\n\n // Check if media engine has a getScrubVideoRendition method (AssetMediaEngine, etc.)\n if (typeof mediaEngine.getScrubVideoRendition === \"function\") {\n scrubRendition = mediaEngine.getScrubVideoRendition();\n } else if (\"data\" in mediaEngine && mediaEngine.data?.videoRenditions) {\n // Fallback to data structure for other engines\n scrubRendition = mediaEngine.data.videoRenditions.find(\n (r: any) => r.id === \"scrub\",\n );\n }\n\n if (!scrubRendition) {\n span.setAttribute(\"result\", \"no-scrub-rendition\");\n return undefined;\n }\n\n const scrubRenditionWithSrc = {\n ...scrubRendition,\n src: mediaEngine.src,\n };\n\n // Check if scrub segment is cached\n const segmentId = mediaEngine.computeSegmentId(\n desiredSeekTimeMs,\n scrubRenditionWithSrc,\n );\n if (segmentId === undefined) {\n span.setAttribute(\"result\", \"no-segment-id\");\n return undefined;\n }\n\n const isCached = mediaEngine.isSegmentCached(\n segmentId,\n scrubRenditionWithSrc,\n );\n span.setAttribute(\"isCached\", isCached);\n if (!isCached) {\n span.setAttribute(\"result\", \"not-cached\");\n return undefined; // Not cached - let main video handle it\n }\n\n // For single-file scrub tracks (AssetMediaEngine trackId -1), use URL-based caching\n // This ensures all segments share the same BufferedSeekingInput instance\n // Use JIT format URL for consistency\n let scrubUrl: string | undefined;\n if (scrubRendition.trackId === -1) {\n // Check if mediaEngine has urlGenerator (AssetMediaEngine)\n if (mediaEngine.urlGenerator && typeof mediaEngine.urlGenerator.baseUrl === \"function\") {\n // Get baseUrl, fallback to current origin if empty\n let baseUrl = mediaEngine.urlGenerator.baseUrl();\n if (!baseUrl && typeof window !== \"undefined\") {\n baseUrl = window.location.origin;\n }\n // Get source URL\n const sourceUrl = mediaEngine.src.startsWith(\"http://\") || mediaEngine.src.startsWith(\"https://\")\n ? mediaEngine.src\n : `${baseUrl}/${mediaEngine.src.startsWith(\"/\") ? mediaEngine.src.slice(1) : mediaEngine.src}`;\n scrubUrl = `${baseUrl}/api/v1/transcode/scrub/init.m4s?url=${encodeURIComponent(sourceUrl)}`;\n } else {\n // Fallback if no urlGenerator (shouldn't happen, but for safety)\n // Use production API format for local files\n let normalizedSrc = mediaEngine.src.startsWith(\"/\")\n ? mediaEngine.src.slice(1)\n : mediaEngine.src;\n normalizedSrc = normalizedSrc.replace(/^\\/+/, \"\");\n // Use the local isobmff API format\n scrubUrl = `/api/v1/isobmff_files/local/track?src=${encodeURIComponent(normalizedSrc)}&trackId=-1`;\n }\n }\n\n // Get cached scrub input and seek within it\n const scrubInput = await scrubInputCache.getOrCreateInput(\n segmentId,\n async () => {\n // Try to fetch segments, but return undefined if they fail with expected errors\n let initSegment: ArrayBuffer | undefined;\n let mediaSegment: ArrayBuffer | undefined;\n \n try {\n [initSegment, mediaSegment] = await Promise.all([\n mediaEngine.fetchInitSegment(scrubRenditionWithSrc, signal),\n mediaEngine.fetchMediaSegment(segmentId, scrubRenditionWithSrc, signal),\n ]);\n } catch (error) {\n // If aborted, re-throw to propagate cancellation\n if (error instanceof DOMException && error.name === \"AbortError\") {\n throw error;\n }\n // If fetch fails with expected errors (401, missing segments, etc.), return undefined\n if (\n error instanceof Error &&\n (error.message.includes(\"401\") ||\n error.message.includes(\"UNAUTHORIZED\") ||\n error.message.includes(\"Failed to fetch\") ||\n error.message.includes(\"File not found\") ||\n error.message.includes(\"Media segment not found\") ||\n error.message.includes(\"Init segment not found\") ||\n error.message.includes(\"Track not found\"))\n ) {\n return undefined;\n }\n // Re-throw unexpected errors\n throw error;\n }\n\n if (!initSegment || !mediaSegment) {\n return undefined;\n }\n signal?.throwIfAborted();\n\n const { BufferedSeekingInput } =\n await import(\"../BufferedSeekingInput.js\");\n const { EFMedia } = await import(\"../../EFMedia.js\");\n\n // Create combined blob - this is expensive, check abort before/after\n signal?.throwIfAborted();\n const combinedBlob = new Blob([initSegment, mediaSegment]);\n signal?.throwIfAborted();\n \n const arrayBuffer = await combinedBlob.arrayBuffer();\n signal?.throwIfAborted();\n\n return new BufferedSeekingInput(\n arrayBuffer,\n {\n videoBufferSize: EFMedia.VIDEO_SAMPLE_BUFFER_SIZE,\n audioBufferSize: EFMedia.AUDIO_SAMPLE_BUFFER_SIZE,\n startTimeOffsetMs: scrubRendition.startTimeOffsetMs,\n },\n );\n },\n scrubUrl,\n );\n\n if (!scrubInput) {\n span.setAttribute(\"result\", \"no-scrub-input\");\n return undefined;\n }\n\n signal?.throwIfAborted();\n\n const videoTrack = await scrubInput.getFirstVideoTrack();\n if (!videoTrack) {\n span.setAttribute(\"result\", \"no-video-track\");\n return undefined;\n }\n\n signal?.throwIfAborted();\n\n const sample = (await scrubInput.seek(\n videoTrack.id,\n desiredSeekTimeMs,\n )) as unknown as VideoSample | undefined;\n\n span.setAttribute(\"result\", sample ? \"success\" : \"no-sample\");\n return sample;\n } catch (_error) {\n if (signal?.aborted) {\n span.setAttribute(\"result\", \"aborted\");\n return undefined;\n }\n span.setAttribute(\"result\", \"error\");\n return undefined; // Scrub failed - let main video handle it\n }\n },\n );\n}\n\n/**\n * Get main video sample (slower path with fetching)\n */\nasync function getMainVideoSample(\n _host: EFVideo,\n mediaEngine: any,\n desiredSeekTimeMs: number,\n signal: AbortSignal,\n): Promise<VideoSample | undefined> {\n return withSpan(\n \"video.getMainVideoSample\",\n {\n desiredSeekTimeMs,\n src: mediaEngine.src || \"unknown\",\n },\n undefined,\n async (span) => {\n try {\n // Use existing main video task chain\n const videoRendition = mediaEngine.getVideoRendition();\n if (!videoRendition) {\n // Video rendition not available - return undefined gracefully instead of throwing\n span.setAttribute(\"result\", \"no-video-rendition\");\n return undefined;\n }\n\n const segmentId = mediaEngine.computeSegmentId(\n desiredSeekTimeMs,\n videoRendition,\n );\n if (segmentId === undefined) {\n span.setAttribute(\"result\", \"no-segment-id\");\n return undefined;\n }\n\n span.setAttribute(\"segmentId\", segmentId);\n\n // Get cached main video input or create new one\n const mainInput = await mainVideoInputCache.getOrCreateInput(\n mediaEngine.src,\n segmentId,\n videoRendition.id,\n async () => {\n // Fetch main video segment (will be cached at mediaEngine level)\n // Try to fetch segments, but return undefined if they fail with expected errors\n let initSegment: ArrayBuffer | undefined;\n let mediaSegment: ArrayBuffer | undefined;\n \n try {\n [initSegment, mediaSegment] = await Promise.all([\n mediaEngine.fetchInitSegment(videoRendition, signal),\n mediaEngine.fetchMediaSegment(segmentId, videoRendition, signal),\n ]);\n } catch (error) {\n // If aborted, re-throw to propagate cancellation\n if (error instanceof DOMException && error.name === \"AbortError\") {\n throw error;\n }\n // If fetch fails with expected errors (401, missing segments, etc.), return undefined\n if (\n error instanceof Error &&\n (error.message.includes(\"401\") ||\n error.message.includes(\"UNAUTHORIZED\") ||\n error.message.includes(\"Failed to fetch\") ||\n error.message.includes(\"File not found\") ||\n error.message.includes(\"Media segment not found\") ||\n error.message.includes(\"Init segment not found\") ||\n error.message.includes(\"Track not found\"))\n ) {\n return undefined;\n }\n // Re-throw unexpected errors\n throw error;\n }\n\n if (!initSegment || !mediaSegment) {\n return undefined;\n }\n signal?.throwIfAborted();\n\n // Create combined blob - this is expensive, check abort before/after\n signal?.throwIfAborted();\n const combinedBlob = new Blob([initSegment, mediaSegment]);\n signal?.throwIfAborted();\n \n const arrayBuffer = await combinedBlob.arrayBuffer();\n signal?.throwIfAborted();\n\n return new BufferedSeekingInput(\n arrayBuffer,\n {\n videoBufferSize: EFMedia.VIDEO_SAMPLE_BUFFER_SIZE,\n audioBufferSize: EFMedia.AUDIO_SAMPLE_BUFFER_SIZE,\n startTimeOffsetMs: videoRendition.startTimeOffsetMs,\n },\n );\n },\n );\n\n if (!mainInput) {\n span.setAttribute(\"result\", \"no-segments\");\n return undefined;\n }\n\n signal?.throwIfAborted();\n\n const videoTrack = await mainInput.getFirstVideoTrack();\n if (!videoTrack) {\n span.setAttribute(\"result\", \"no-video-track\");\n return undefined;\n }\n\n signal?.throwIfAborted();\n\n const sample = (await mainInput.seek(\n videoTrack.id,\n desiredSeekTimeMs,\n )) as unknown as VideoSample | undefined;\n\n span.setAttribute(\"result\", sample ? \"success\" : \"no-sample\");\n return sample;\n } catch (error) {\n if (signal?.aborted) {\n span.setAttribute(\"result\", \"aborted\");\n return undefined;\n }\n throw error;\n }\n },\n );\n}\n\n/**\n * Start background upgrade to main quality (non-blocking)\n */\nasync function startMainQualityUpgrade(\n host: EFVideo,\n mediaEngine: any,\n targetSeekTimeMs: number,\n signal: AbortSignal,\n): Promise<void> {\n // Small delay to let scrub content display first\n await new Promise((resolve) => setTimeout(resolve, 50));\n if (signal?.aborted || host.desiredSeekTimeMs !== targetSeekTimeMs) return;\n\n // Get main quality sample and upgrade display\n const mainSample = await getMainVideoSample(\n host,\n mediaEngine,\n targetSeekTimeMs,\n signal,\n );\n if (\n mainSample &&\n !signal?.aborted &&\n host.desiredSeekTimeMs === targetSeekTimeMs\n ) {\n const videoFrame = mainSample.toVideoFrame();\n try {\n host.displayFrame(videoFrame, targetSeekTimeMs);\n } finally {\n videoFrame.close();\n }\n }\n}\n"],"mappings":"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"}
@@ -1,12 +0,0 @@
1
- import { MediaBufferState } from "../shared/BufferUtils.js";
2
- import { Task } from "@lit/task";
3
-
4
- //#region src/elements/EFMedia/videoTasks/makeVideoBufferTask.d.ts
5
-
6
- /**
7
- * State of the video buffer - uses the generic interface
8
- */
9
- interface VideoBufferState extends MediaBufferState {}
10
- //#endregion
11
- export { VideoBufferState };
12
- //# sourceMappingURL=makeVideoBufferTask.d.ts.map
@@ -1,97 +0,0 @@
1
- import { EF_INTERACTIVE } from "../../../EF_INTERACTIVE.js";
2
- import { EF_RENDERING } from "../../../EF_RENDERING.js";
3
- import { AssetMediaEngine } from "../AssetMediaEngine.js";
4
- import { manageMediaBuffer } from "../shared/BufferUtils.js";
5
- import { getLatestMediaEngine } from "../tasks/makeMediaEngineTask.js";
6
- import { Task } from "@lit/task";
7
-
8
- //#region src/elements/EFMedia/videoTasks/makeVideoBufferTask.ts
9
- const makeVideoBufferTask = (host) => {
10
- let currentState = {
11
- currentSeekTimeMs: 0,
12
- requestedSegments: /* @__PURE__ */ new Set(),
13
- activeRequests: /* @__PURE__ */ new Set(),
14
- requestQueue: []
15
- };
16
- let task;
17
- task = new Task(host, {
18
- autoRun: EF_INTERACTIVE,
19
- args: () => [host.desiredSeekTimeMs],
20
- onError: (error) => {
21
- task.taskComplete.catch(() => {});
22
- 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;
23
- if (error instanceof Error && (error.message === "No valid media source" || error.message.includes("File not found") || error.message.includes("is not valid JSON") || error.message.includes("401") || error.message.includes("UNAUTHORIZED") || error.message.includes("Failed to fetch"))) return;
24
- console.error("videoBufferTask error", error);
25
- },
26
- onComplete: (value) => {
27
- currentState = value;
28
- },
29
- task: async ([seekTimeMs], { signal }) => {
30
- if (EF_RENDERING()) return currentState;
31
- if (host.mediaEngineTask.error) return currentState;
32
- let mediaEngine;
33
- try {
34
- mediaEngine = await getLatestMediaEngine(host, signal);
35
- } catch (error) {
36
- if (error instanceof Error && error.message === "No valid media source") return currentState;
37
- throw error;
38
- }
39
- if (!mediaEngine) return currentState;
40
- const engineConfig = mediaEngine.getBufferConfig();
41
- const currentConfig = {
42
- bufferDurationMs: engineConfig.videoBufferDurationMs,
43
- maxParallelFetches: engineConfig.maxVideoBufferFetches,
44
- enableBuffering: host.enableVideoBuffering,
45
- bufferThresholdMs: engineConfig.bufferThresholdMs
46
- };
47
- const timelineContext = host.rootTimegroup?.currentTimeMs !== void 0 ? {
48
- elementStartMs: host.startTimeMs,
49
- elementEndMs: host.endTimeMs,
50
- playheadMs: host.rootTimegroup.currentTimeMs
51
- } : void 0;
52
- return manageMediaBuffer(seekTimeMs, currentConfig, currentState, host.intrinsicDurationMs || 1e4, signal, {
53
- computeSegmentId: async (timeMs, rendition) => {
54
- const mediaEngine$1 = await getLatestMediaEngine(host, signal);
55
- if (!mediaEngine$1) return void 0;
56
- return mediaEngine$1.computeSegmentId(timeMs, rendition);
57
- },
58
- prefetchSegment: async (segmentId, rendition) => {
59
- try {
60
- const mediaEngine$1 = await getLatestMediaEngine(host, signal);
61
- if (!mediaEngine$1) return;
62
- if (mediaEngine$1 instanceof AssetMediaEngine && rendition.trackId !== -1) {
63
- const trackData = mediaEngine$1.data?.[rendition.trackId];
64
- if (!trackData?.segments || segmentId >= trackData.segments.length) return;
65
- }
66
- await mediaEngine$1.fetchMediaSegment(segmentId, rendition, signal);
67
- } catch (error) {
68
- if (error instanceof Error && (error.message.includes("Media segment not found") || error.message.includes("Track not found") || error.message.includes("Failed to fetch") || error.message.includes("401") || error.message.includes("UNAUTHORIZED") || error.message.includes("File not found"))) return;
69
- throw error;
70
- }
71
- },
72
- isSegmentCached: (segmentId, rendition) => {
73
- const mediaEngine$1 = host.mediaEngineTask.value;
74
- if (!mediaEngine$1) return false;
75
- return mediaEngine$1.isSegmentCached(segmentId, rendition);
76
- },
77
- getRendition: async () => {
78
- try {
79
- const mediaEngine$1 = await getLatestMediaEngine(host, signal);
80
- if (!mediaEngine$1) throw new Error("Video rendition not available");
81
- return mediaEngine$1.getVideoRendition();
82
- } catch (error) {
83
- if (error instanceof Error && error.message === "No valid media source") throw new Error("Video rendition not available");
84
- throw error;
85
- }
86
- },
87
- logError: console.error
88
- }, timelineContext);
89
- }
90
- });
91
- task.taskComplete.catch(() => {});
92
- return task;
93
- };
94
-
95
- //#endregion
96
- export { makeVideoBufferTask };
97
- //# sourceMappingURL=makeVideoBufferTask.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"makeVideoBufferTask.js","names":["currentState: VideoBufferState","task: VideoBufferTask","currentConfig: VideoBufferConfig","mediaEngine"],"sources":["../../../../src/elements/EFMedia/videoTasks/makeVideoBufferTask.ts"],"sourcesContent":["import { Task } from \"@lit/task\";\n\nimport { EF_INTERACTIVE } from \"../../../EF_INTERACTIVE\";\nimport { EF_RENDERING } from \"../../../EF_RENDERING\";\nimport type { VideoRendition } from \"../../../transcoding/types\";\nimport type { EFVideo } from \"../../EFVideo\";\nimport { AssetMediaEngine } from \"../AssetMediaEngine\";\nimport {\n type MediaBufferConfig,\n type MediaBufferState,\n manageMediaBuffer,\n} from \"../shared/BufferUtils\";\nimport { getLatestMediaEngine } from \"../tasks/makeMediaEngineTask\";\n\n/**\n * Configuration for video buffering - extends the generic interface\n */\nexport interface VideoBufferConfig extends MediaBufferConfig {}\n\n/**\n * State of the video buffer - uses the generic interface\n */\nexport interface VideoBufferState extends MediaBufferState {}\n\ntype VideoBufferTask = Task<readonly [number], VideoBufferState>;\nexport const makeVideoBufferTask = (host: EFVideo): VideoBufferTask => {\n let currentState: VideoBufferState = {\n currentSeekTimeMs: 0,\n requestedSegments: new Set(),\n activeRequests: new Set(),\n requestQueue: [],\n };\n\n // Capture task reference for use in onError\n let task: VideoBufferTask;\n\n task = new Task(host, {\n autoRun: EF_INTERACTIVE, // Make lazy - only run when element becomes timeline-active\n args: () => [host.desiredSeekTimeMs] as const,\n onError: (error) => {\n // CRITICAL: Attach .catch() handler to taskComplete in onError.\n // This is called BEFORE reject(), so the handler is attached in time.\n task.taskComplete.catch(() => {});\n \n // Don't log AbortErrors - these are expected when tasks are cancelled\n if (\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 return;\n }\n // Don't log errors when there's no valid media source, file not found, or auth errors - these are expected\n if (error instanceof Error && (\n error.message === \"No valid media source\" ||\n error.message.includes(\"File not found\") ||\n error.message.includes(\"is not valid JSON\") ||\n error.message.includes(\"401\") ||\n error.message.includes(\"UNAUTHORIZED\") ||\n error.message.includes(\"Failed to fetch\")\n )) {\n return;\n }\n console.error(\"videoBufferTask error\", error);\n },\n onComplete: (value) => {\n currentState = value;\n },\n task: async ([seekTimeMs], { signal }) => {\n // Skip buffering entirely in rendering mode\n if (EF_RENDERING()) {\n return currentState; // Return existing state without any buffering activity\n }\n\n // Check if media engine task has errored (no valid source) before attempting to use it\n if (host.mediaEngineTask.error) {\n return currentState;\n }\n\n // Get media engine to potentially override buffer configuration\n let mediaEngine;\n try {\n mediaEngine = await getLatestMediaEngine(host, signal);\n } catch (error) {\n // If media engine task failed (no valid source), return current state silently\n if (error instanceof Error && error.message === \"No valid media source\") {\n return currentState;\n }\n // Re-throw unexpected errors\n throw error;\n }\n\n // Return existing state if no valid media engine (no valid source)\n if (!mediaEngine) {\n return currentState;\n }\n\n // Use media engine's buffer config, falling back to host properties\n const engineConfig = mediaEngine.getBufferConfig();\n const bufferDurationMs = engineConfig.videoBufferDurationMs;\n const maxParallelFetches = engineConfig.maxVideoBufferFetches;\n\n const currentConfig: VideoBufferConfig = {\n bufferDurationMs,\n maxParallelFetches,\n enableBuffering: host.enableVideoBuffering,\n bufferThresholdMs: engineConfig.bufferThresholdMs,\n };\n\n // Timeline context for priority-based buffering\n const timelineContext =\n host.rootTimegroup?.currentTimeMs !== undefined\n ? {\n elementStartMs: host.startTimeMs,\n elementEndMs: host.endTimeMs,\n playheadMs: host.rootTimegroup.currentTimeMs,\n }\n : undefined;\n\n return manageMediaBuffer<VideoRendition>(\n seekTimeMs,\n currentConfig,\n currentState,\n (host as any).intrinsicDurationMs || 10000,\n signal,\n {\n computeSegmentId: async (timeMs, rendition) => {\n // Use media engine's computeSegmentId\n const mediaEngine = await getLatestMediaEngine(host, signal);\n if (!mediaEngine) return undefined;\n return mediaEngine.computeSegmentId(timeMs, rendition);\n },\n prefetchSegment: async (segmentId, rendition) => {\n // Trigger prefetch through BaseMediaEngine - let it handle caching\n try {\n const mediaEngine = await getLatestMediaEngine(host, signal);\n if (!mediaEngine) return;\n \n // Check if the segment exists in AssetMediaEngine data before prefetching\n // Scrub track uses trackId -1, which is handled specially, so skip check for that\n if (mediaEngine instanceof AssetMediaEngine && rendition.trackId !== -1) {\n // @ts-expect-error - data is protected but we need to check segment existence\n const trackData = (mediaEngine as any).data?.[rendition.trackId];\n if (!trackData?.segments || segmentId >= trackData.segments.length) {\n // Segment doesn't exist in the data - don't prefetch\n return;\n }\n }\n \n await mediaEngine.fetchMediaSegment(segmentId, rendition, signal);\n } catch (error) {\n // If segment doesn't exist or fetch fails (401, etc.), skip prefetch silently\n if (\n error instanceof Error &&\n (error.message.includes(\"Media segment not found\") ||\n error.message.includes(\"Track not found\") ||\n error.message.includes(\"Failed to fetch\") ||\n error.message.includes(\"401\") ||\n error.message.includes(\"UNAUTHORIZED\") ||\n error.message.includes(\"File not found\"))\n ) {\n return;\n }\n throw error;\n }\n // Don't return data - just ensure it's cached in BaseMediaEngine\n },\n isSegmentCached: (segmentId, rendition) => {\n // Check if segment is already cached in BaseMediaEngine\n const mediaEngine = host.mediaEngineTask.value;\n if (!mediaEngine) return false;\n\n return mediaEngine.isSegmentCached(segmentId, rendition);\n },\n getRendition: async () => {\n // Get real video rendition from media engine\n try {\n const mediaEngine = await getLatestMediaEngine(host, signal);\n if (!mediaEngine) {\n throw new Error(\"Video rendition not available\");\n }\n return mediaEngine.getVideoRendition();\n } catch (error) {\n // If media engine task failed (no valid source), throw error for getRendition\n if (error instanceof Error && error.message === \"No valid media source\") {\n throw new Error(\"Video rendition not available\");\n }\n throw error;\n }\n },\n logError: console.error,\n },\n timelineContext,\n );\n },\n });\n\n // CRITICAL: Attach .catch() handler IMMEDIATELY to prevent unhandled rejections.\n // This must be done synchronously after task creation, before any updates can trigger run().\n // When hostUpdate() triggers _performTask() -> run(), the rejection needs to already have a handler.\n task.taskComplete.catch(() => {});\n\n return task;\n};\n"],"mappings":"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"}
@@ -1,19 +0,0 @@
1
- import { AudioSample, VideoSample } from "mediabunny";
2
-
3
- //#region src/elements/SampleBuffer.d.ts
4
- type MediaSample = VideoSample | AudioSample;
5
- declare class SampleBuffer {
6
- private buffer;
7
- private bufferSize;
8
- constructor(bufferSize?: number);
9
- push(sample: MediaSample): void;
10
- clear(): void;
11
- peek(): MediaSample | undefined;
12
- find(desiredSeekTimeMs: number): MediaSample | undefined;
13
- get length(): number;
14
- get firstTimestamp(): number;
15
- getContents(): MediaSample[];
16
- }
17
- //#endregion
18
- export { MediaSample, SampleBuffer };
19
- //# sourceMappingURL=SampleBuffer.d.ts.map