@videojs/spf 10.0.0-beta.10 → 10.0.0-beta.12

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 (236) hide show
  1. package/README.md +16 -40
  2. package/dist/default/core/abr/bandwidth-estimator.js +95 -0
  3. package/dist/default/core/abr/bandwidth-estimator.js.map +1 -0
  4. package/dist/default/core/abr/ewma.js +71 -0
  5. package/dist/default/core/abr/ewma.js.map +1 -0
  6. package/dist/default/core/abr/quality-selection.js +53 -0
  7. package/dist/default/core/abr/quality-selection.js.map +1 -0
  8. package/dist/default/core/buffer/back-buffer.js +46 -0
  9. package/dist/default/core/buffer/back-buffer.js.map +1 -0
  10. package/dist/default/core/buffer/forward-buffer.js +73 -0
  11. package/dist/default/core/buffer/forward-buffer.js.map +1 -0
  12. package/dist/default/core/features/calculate-presentation-duration.js +53 -0
  13. package/dist/default/core/features/calculate-presentation-duration.js.map +1 -0
  14. package/dist/default/core/features/quality-switching.js +69 -0
  15. package/dist/default/core/features/quality-switching.js.map +1 -0
  16. package/dist/default/core/features/resolve-presentation.js +77 -0
  17. package/dist/default/core/features/resolve-presentation.js.map +1 -0
  18. package/dist/default/core/features/resolve-track.js +66 -0
  19. package/dist/default/core/features/resolve-track.js.map +1 -0
  20. package/dist/default/core/features/select-tracks.js +130 -0
  21. package/dist/default/core/features/select-tracks.js.map +1 -0
  22. package/dist/default/core/features/sync-preload-attribute.js +26 -0
  23. package/dist/default/core/features/sync-preload-attribute.js.map +1 -0
  24. package/dist/default/core/hls/parse-attributes.js +134 -0
  25. package/dist/default/core/hls/parse-attributes.js.map +1 -0
  26. package/dist/default/core/hls/parse-media-playlist.js +82 -0
  27. package/dist/default/core/hls/parse-media-playlist.js.map +1 -0
  28. package/dist/default/core/hls/parse-multivariant.js +214 -0
  29. package/dist/default/core/hls/parse-multivariant.js.map +1 -0
  30. package/dist/default/core/hls/resolve-url.js +11 -0
  31. package/dist/default/core/hls/resolve-url.js.map +1 -0
  32. package/dist/default/core/signals/effect.js +41 -0
  33. package/dist/default/core/signals/effect.js.map +1 -0
  34. package/dist/default/core/signals/primitives.js +32 -0
  35. package/dist/default/core/signals/primitives.js.map +1 -0
  36. package/dist/default/core/task.js +122 -0
  37. package/dist/default/core/task.js.map +1 -0
  38. package/dist/default/core/types/index.js +15 -0
  39. package/dist/default/core/types/index.js.map +1 -0
  40. package/dist/default/core/utils/generate-id.js +21 -0
  41. package/dist/default/core/utils/generate-id.js.map +1 -0
  42. package/dist/default/core/utils/track-selection.js +39 -0
  43. package/dist/default/core/utils/track-selection.js.map +1 -0
  44. package/dist/default/dom/features/end-of-stream.js +145 -0
  45. package/dist/default/dom/features/end-of-stream.js.map +1 -0
  46. package/dist/default/dom/features/load-segments.js +192 -0
  47. package/dist/default/dom/features/load-segments.js.map +1 -0
  48. package/dist/default/dom/features/load-text-track-cues.js +161 -0
  49. package/dist/default/dom/features/load-text-track-cues.js.map +1 -0
  50. package/dist/default/dom/features/segment-loader-actor.js +196 -0
  51. package/dist/default/dom/features/segment-loader-actor.js.map +1 -0
  52. package/dist/default/dom/features/setup-mediasource.js +48 -0
  53. package/dist/default/dom/features/setup-mediasource.js.map +1 -0
  54. package/dist/default/dom/features/setup-sourcebuffer.js +78 -0
  55. package/dist/default/dom/features/setup-sourcebuffer.js.map +1 -0
  56. package/dist/default/dom/features/setup-text-tracks.js +67 -0
  57. package/dist/default/dom/features/setup-text-tracks.js.map +1 -0
  58. package/dist/default/dom/features/sync-selected-text-track-from-dom.js +54 -0
  59. package/dist/default/dom/features/sync-selected-text-track-from-dom.js.map +1 -0
  60. package/dist/default/dom/features/sync-text-track-modes.js +33 -0
  61. package/dist/default/dom/features/sync-text-track-modes.js.map +1 -0
  62. package/dist/default/dom/features/track-current-time.js +49 -0
  63. package/dist/default/dom/features/track-current-time.js.map +1 -0
  64. package/dist/default/dom/features/track-playback-initiated.js +53 -0
  65. package/dist/default/dom/features/track-playback-initiated.js.map +1 -0
  66. package/dist/default/dom/features/track-playback-rate.js +32 -0
  67. package/dist/default/dom/features/track-playback-rate.js.map +1 -0
  68. package/dist/default/dom/features/update-duration.js +78 -0
  69. package/dist/default/dom/features/update-duration.js.map +1 -0
  70. package/dist/default/dom/media/append-segment.js +60 -0
  71. package/dist/default/dom/media/append-segment.js.map +1 -0
  72. package/dist/default/dom/media/buffer-flusher.js +55 -0
  73. package/dist/default/dom/media/buffer-flusher.js.map +1 -0
  74. package/dist/default/dom/media/mediasource-setup.js +146 -0
  75. package/dist/default/dom/media/mediasource-setup.js.map +1 -0
  76. package/dist/default/dom/media/source-buffer-actor.js +176 -0
  77. package/dist/default/dom/media/source-buffer-actor.js.map +1 -0
  78. package/dist/default/dom/network/chunked-stream-iterable.js +49 -0
  79. package/dist/default/dom/network/chunked-stream-iterable.js.map +1 -0
  80. package/dist/default/dom/network/fetch.js +55 -0
  81. package/dist/default/dom/network/fetch.js.map +1 -0
  82. package/dist/default/dom/playback-engine/adapter.js +95 -0
  83. package/dist/default/dom/playback-engine/adapter.js.map +1 -0
  84. package/dist/default/dom/playback-engine/engine.js +151 -0
  85. package/dist/default/dom/playback-engine/engine.js.map +1 -0
  86. package/dist/default/dom/playback-engine.js +4 -0
  87. package/dist/default/dom/text/parse-vtt-segment.js +56 -0
  88. package/dist/default/dom/text/parse-vtt-segment.js.map +1 -0
  89. package/dist/default/dom.js +10 -0
  90. package/dist/{src-DRsS_H80.js → default/index.js} +3 -3
  91. package/dist/default/index.js.map +1 -0
  92. package/dist/dev/core/abr/bandwidth-estimator.d.ts +35 -0
  93. package/dist/dev/core/abr/bandwidth-estimator.d.ts.map +1 -0
  94. package/dist/dev/core/abr/bandwidth-estimator.js +95 -0
  95. package/dist/dev/core/abr/bandwidth-estimator.js.map +1 -0
  96. package/dist/dev/core/abr/ewma.js +71 -0
  97. package/dist/dev/core/abr/ewma.js.map +1 -0
  98. package/dist/dev/core/abr/quality-selection.js +53 -0
  99. package/dist/dev/core/abr/quality-selection.js.map +1 -0
  100. package/dist/dev/core/actor.d.ts +18 -0
  101. package/dist/dev/core/actor.d.ts.map +1 -0
  102. package/dist/dev/core/buffer/back-buffer.js +46 -0
  103. package/dist/dev/core/buffer/back-buffer.js.map +1 -0
  104. package/dist/dev/core/buffer/forward-buffer.js +73 -0
  105. package/dist/dev/core/buffer/forward-buffer.js.map +1 -0
  106. package/dist/dev/core/features/calculate-presentation-duration.js +53 -0
  107. package/dist/dev/core/features/calculate-presentation-duration.js.map +1 -0
  108. package/dist/dev/core/features/quality-switching.js +69 -0
  109. package/dist/dev/core/features/quality-switching.js.map +1 -0
  110. package/dist/dev/core/features/resolve-presentation.js +77 -0
  111. package/dist/dev/core/features/resolve-presentation.js.map +1 -0
  112. package/dist/dev/core/features/resolve-track.js +66 -0
  113. package/dist/dev/core/features/resolve-track.js.map +1 -0
  114. package/dist/dev/core/features/select-tracks.js +130 -0
  115. package/dist/dev/core/features/select-tracks.js.map +1 -0
  116. package/dist/dev/core/features/sync-preload-attribute.js +26 -0
  117. package/dist/dev/core/features/sync-preload-attribute.js.map +1 -0
  118. package/dist/dev/core/hls/parse-attributes.js +134 -0
  119. package/dist/dev/core/hls/parse-attributes.js.map +1 -0
  120. package/dist/dev/core/hls/parse-media-playlist.js +82 -0
  121. package/dist/dev/core/hls/parse-media-playlist.js.map +1 -0
  122. package/dist/dev/core/hls/parse-multivariant.js +214 -0
  123. package/dist/dev/core/hls/parse-multivariant.js.map +1 -0
  124. package/dist/dev/core/hls/resolve-url.js +11 -0
  125. package/dist/dev/core/hls/resolve-url.js.map +1 -0
  126. package/dist/dev/core/signals/effect.d.ts +15 -0
  127. package/dist/dev/core/signals/effect.d.ts.map +1 -0
  128. package/dist/dev/core/signals/effect.js +41 -0
  129. package/dist/dev/core/signals/effect.js.map +1 -0
  130. package/dist/dev/core/signals/primitives.d.ts +10 -0
  131. package/dist/dev/core/signals/primitives.d.ts.map +1 -0
  132. package/dist/dev/core/signals/primitives.js +32 -0
  133. package/dist/dev/core/signals/primitives.js.map +1 -0
  134. package/dist/dev/core/task.js +122 -0
  135. package/dist/dev/core/task.js.map +1 -0
  136. package/dist/dev/core/types/index.d.ts +173 -0
  137. package/dist/dev/core/types/index.d.ts.map +1 -0
  138. package/dist/dev/core/types/index.js +15 -0
  139. package/dist/dev/core/types/index.js.map +1 -0
  140. package/dist/dev/core/utils/generate-id.js +21 -0
  141. package/dist/dev/core/utils/generate-id.js.map +1 -0
  142. package/dist/dev/core/utils/track-selection.d.ts +16 -0
  143. package/dist/dev/core/utils/track-selection.d.ts.map +1 -0
  144. package/dist/dev/core/utils/track-selection.js +39 -0
  145. package/dist/dev/core/utils/track-selection.js.map +1 -0
  146. package/dist/dev/dom/features/end-of-stream.js +145 -0
  147. package/dist/dev/dom/features/end-of-stream.js.map +1 -0
  148. package/dist/dev/dom/features/load-segments.d.ts +72 -0
  149. package/dist/dev/dom/features/load-segments.d.ts.map +1 -0
  150. package/dist/dev/dom/features/load-segments.js +192 -0
  151. package/dist/dev/dom/features/load-segments.js.map +1 -0
  152. package/dist/dev/dom/features/load-text-track-cues.d.ts +61 -0
  153. package/dist/dev/dom/features/load-text-track-cues.d.ts.map +1 -0
  154. package/dist/dev/dom/features/load-text-track-cues.js +161 -0
  155. package/dist/dev/dom/features/load-text-track-cues.js.map +1 -0
  156. package/dist/dev/dom/features/segment-loader-actor.d.ts +1 -0
  157. package/dist/dev/dom/features/segment-loader-actor.js +196 -0
  158. package/dist/dev/dom/features/segment-loader-actor.js.map +1 -0
  159. package/dist/dev/dom/features/setup-mediasource.js +48 -0
  160. package/dist/dev/dom/features/setup-mediasource.js.map +1 -0
  161. package/dist/dev/dom/features/setup-sourcebuffer.d.ts +9 -0
  162. package/dist/dev/dom/features/setup-sourcebuffer.d.ts.map +1 -0
  163. package/dist/dev/dom/features/setup-sourcebuffer.js +78 -0
  164. package/dist/dev/dom/features/setup-sourcebuffer.js.map +1 -0
  165. package/dist/dev/dom/features/setup-text-tracks.js +67 -0
  166. package/dist/dev/dom/features/setup-text-tracks.js.map +1 -0
  167. package/dist/dev/dom/features/sync-selected-text-track-from-dom.js +54 -0
  168. package/dist/dev/dom/features/sync-selected-text-track-from-dom.js.map +1 -0
  169. package/dist/dev/dom/features/sync-text-track-modes.js +33 -0
  170. package/dist/dev/dom/features/sync-text-track-modes.js.map +1 -0
  171. package/dist/dev/dom/features/track-current-time.d.ts +40 -0
  172. package/dist/dev/dom/features/track-current-time.d.ts.map +1 -0
  173. package/dist/dev/dom/features/track-current-time.js +49 -0
  174. package/dist/dev/dom/features/track-current-time.js.map +1 -0
  175. package/dist/dev/dom/features/track-playback-initiated.d.ts +44 -0
  176. package/dist/dev/dom/features/track-playback-initiated.d.ts.map +1 -0
  177. package/dist/dev/dom/features/track-playback-initiated.js +53 -0
  178. package/dist/dev/dom/features/track-playback-initiated.js.map +1 -0
  179. package/dist/dev/dom/features/track-playback-rate.d.ts +34 -0
  180. package/dist/dev/dom/features/track-playback-rate.d.ts.map +1 -0
  181. package/dist/dev/dom/features/track-playback-rate.js +32 -0
  182. package/dist/dev/dom/features/track-playback-rate.js.map +1 -0
  183. package/dist/dev/dom/features/update-duration.js +78 -0
  184. package/dist/dev/dom/features/update-duration.js.map +1 -0
  185. package/dist/dev/dom/media/append-segment.d.ts +24 -0
  186. package/dist/dev/dom/media/append-segment.d.ts.map +1 -0
  187. package/dist/dev/dom/media/append-segment.js +60 -0
  188. package/dist/dev/dom/media/append-segment.js.map +1 -0
  189. package/dist/dev/dom/media/buffer-flusher.d.ts +24 -0
  190. package/dist/dev/dom/media/buffer-flusher.d.ts.map +1 -0
  191. package/dist/dev/dom/media/buffer-flusher.js +55 -0
  192. package/dist/dev/dom/media/buffer-flusher.js.map +1 -0
  193. package/dist/dev/dom/media/mediasource-setup.js +146 -0
  194. package/dist/dev/dom/media/mediasource-setup.js.map +1 -0
  195. package/dist/dev/dom/media/source-buffer-actor.d.ts +56 -0
  196. package/dist/dev/dom/media/source-buffer-actor.d.ts.map +1 -0
  197. package/dist/dev/dom/media/source-buffer-actor.js +176 -0
  198. package/dist/dev/dom/media/source-buffer-actor.js.map +1 -0
  199. package/dist/dev/dom/network/chunked-stream-iterable.js +49 -0
  200. package/dist/dev/dom/network/chunked-stream-iterable.js.map +1 -0
  201. package/dist/dev/dom/network/fetch.js +55 -0
  202. package/dist/dev/dom/network/fetch.js.map +1 -0
  203. package/dist/dev/dom/playback-engine/adapter.d.ts +41 -0
  204. package/dist/dev/dom/playback-engine/adapter.d.ts.map +1 -0
  205. package/dist/dev/dom/playback-engine/adapter.js +95 -0
  206. package/dist/dev/dom/playback-engine/adapter.js.map +1 -0
  207. package/dist/dev/dom/playback-engine/engine.d.ts +123 -0
  208. package/dist/dev/dom/playback-engine/engine.d.ts.map +1 -0
  209. package/dist/dev/dom/playback-engine/engine.js +151 -0
  210. package/dist/dev/dom/playback-engine/engine.js.map +1 -0
  211. package/dist/dev/dom/playback-engine.d.ts +4 -0
  212. package/dist/dev/dom/playback-engine.js +4 -0
  213. package/dist/dev/dom/text/parse-vtt-segment.d.ts +12 -0
  214. package/dist/dev/dom/text/parse-vtt-segment.d.ts.map +1 -0
  215. package/dist/dev/dom/text/parse-vtt-segment.js +56 -0
  216. package/dist/dev/dom/text/parse-vtt-segment.js.map +1 -0
  217. package/dist/dev/dom.d.ts +10 -0
  218. package/dist/dev/dom.js +10 -0
  219. package/dist/dev/index.d.ts.map +1 -0
  220. package/dist/dev/index.js +14 -0
  221. package/dist/dev/index.js.map +1 -0
  222. package/package.json +24 -23
  223. package/dist/adapter-25AtMN8s.d.ts +0 -719
  224. package/dist/adapter-25AtMN8s.d.ts.map +0 -1
  225. package/dist/adapter-CflgYzjF.js +0 -3556
  226. package/dist/adapter-CflgYzjF.js.map +0 -1
  227. package/dist/dom/index.d.ts +0 -221
  228. package/dist/dom/index.d.ts.map +0 -1
  229. package/dist/dom/index.js +0 -38
  230. package/dist/dom/index.js.map +0 -1
  231. package/dist/dom/playback-engine/index.d.ts +0 -2
  232. package/dist/dom/playback-engine/index.js +0 -4
  233. package/dist/index.d.ts.map +0 -1
  234. package/dist/index.js +0 -3
  235. package/dist/src-DRsS_H80.js.map +0 -1
  236. /package/dist/{index.d.ts → dev/index.d.ts} +0 -0
@@ -0,0 +1,49 @@
1
+ import { effect } from "../../core/signals/effect.js";
2
+ import { update } from "../../core/signals/primitives.js";
3
+ import { listen } from "@videojs/utils/dom";
4
+ //#region src/dom/features/track-current-time.ts
5
+ /**
6
+ * Track current playback position from the media element.
7
+ *
8
+ * Mirrors `mediaElement.currentTime` into reactive state on:
9
+ * - `timeupdate` — fires during playback (~4 Hz)
10
+ * - `seeking` — fires when a seek begins; per spec, `currentTime` is
11
+ * already at the new position when this event dispatches, so buffer
12
+ * management can react immediately rather than waiting for `timeupdate`,
13
+ * which does not fire while paused.
14
+ *
15
+ * Also syncs immediately when a media element becomes available.
16
+ *
17
+ * @example
18
+ * const cleanup = trackCurrentTime({ state, owners });
19
+ */
20
+ function trackCurrentTime({ state, owners }) {
21
+ let lastMediaElement;
22
+ let removeListeners = null;
23
+ const cleanupEffect = effect(() => {
24
+ const { mediaElement } = owners.get();
25
+ if (mediaElement === lastMediaElement) return;
26
+ removeListeners?.();
27
+ removeListeners = null;
28
+ lastMediaElement = mediaElement;
29
+ if (!mediaElement) return;
30
+ const sync = () => {
31
+ update(state, { currentTime: mediaElement.currentTime });
32
+ };
33
+ sync();
34
+ const removeTimeupdate = listen(mediaElement, "timeupdate", sync);
35
+ const removeSeeking = listen(mediaElement, "seeking", sync);
36
+ removeListeners = () => {
37
+ removeTimeupdate();
38
+ removeSeeking();
39
+ };
40
+ });
41
+ return () => {
42
+ removeListeners?.();
43
+ cleanupEffect();
44
+ };
45
+ }
46
+ //#endregion
47
+ export { trackCurrentTime };
48
+
49
+ //# sourceMappingURL=track-current-time.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"track-current-time.js","names":[],"sources":["../../../../src/dom/features/track-current-time.ts"],"sourcesContent":["import { listen } from '@videojs/utils/dom';\nimport { effect } from '../../core/signals/effect';\nimport { type Signal, update } from '../../core/signals/primitives';\n\n/**\n * State shape for current time tracking.\n */\nexport interface CurrentTimeState {\n currentTime?: number;\n}\n\n/**\n * Owners shape for current time tracking.\n */\nexport interface CurrentTimeOwners {\n mediaElement?: HTMLMediaElement | undefined;\n}\n\n/**\n * Check if we can track current time.\n *\n * Requires:\n * - mediaElement exists in owners\n */\nexport function canTrackCurrentTime(owners: CurrentTimeOwners): boolean {\n return !!owners.mediaElement;\n}\n\n/**\n * Track current playback position from the media element.\n *\n * Mirrors `mediaElement.currentTime` into reactive state on:\n * - `timeupdate` — fires during playback (~4 Hz)\n * - `seeking` — fires when a seek begins; per spec, `currentTime` is\n * already at the new position when this event dispatches, so buffer\n * management can react immediately rather than waiting for `timeupdate`,\n * which does not fire while paused.\n *\n * Also syncs immediately when a media element becomes available.\n *\n * @example\n * const cleanup = trackCurrentTime({ state, owners });\n */\nexport function trackCurrentTime<S extends CurrentTimeState, O extends CurrentTimeOwners>({\n state,\n owners,\n}: {\n state: Signal<S>;\n owners: Signal<O>;\n}): () => void {\n let lastMediaElement: HTMLMediaElement | undefined;\n let removeListeners: (() => void) | null = null;\n\n const cleanupEffect = effect(() => {\n const { mediaElement } = owners.get();\n\n if (mediaElement === lastMediaElement) return;\n\n removeListeners?.();\n removeListeners = null;\n lastMediaElement = mediaElement;\n\n if (!mediaElement) return;\n\n const sync = () => {\n const patch: Partial<CurrentTimeState> = { currentTime: mediaElement.currentTime };\n update(state, patch);\n };\n\n // Sync immediately so consumers don't wait for the first event\n sync();\n const removeTimeupdate = listen(mediaElement, 'timeupdate', sync);\n const removeSeeking = listen(mediaElement, 'seeking', sync);\n removeListeners = () => {\n removeTimeupdate();\n removeSeeking();\n };\n });\n\n return () => {\n removeListeners?.();\n cleanupEffect();\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA2CA,SAAgB,iBAA0E,EACxF,OACA,UAIa;CACb,IAAI;CACJ,IAAI,kBAAuC;CAE3C,MAAM,gBAAgB,aAAa;EACjC,MAAM,EAAE,iBAAiB,OAAO,KAAK;AAErC,MAAI,iBAAiB,iBAAkB;AAEvC,qBAAmB;AACnB,oBAAkB;AAClB,qBAAmB;AAEnB,MAAI,CAAC,aAAc;EAEnB,MAAM,aAAa;AAEjB,UAAO,OADkC,EAAE,aAAa,aAAa,aAAa,CAC9D;;AAItB,QAAM;EACN,MAAM,mBAAmB,OAAO,cAAc,cAAc,KAAK;EACjE,MAAM,gBAAgB,OAAO,cAAc,WAAW,KAAK;AAC3D,0BAAwB;AACtB,qBAAkB;AAClB,kBAAe;;GAEjB;AAEF,cAAa;AACX,qBAAmB;AACnB,iBAAe"}
@@ -0,0 +1,44 @@
1
+ import { Signal } from "../../core/signals/primitives.js";
2
+
3
+ //#region src/dom/features/track-playback-initiated.d.ts
4
+ /**
5
+ * State shape for playback initiation tracking.
6
+ */
7
+ interface PlaybackInitiatedState {
8
+ /** True once play has been requested for the current presentation URL. */
9
+ playbackInitiated?: boolean;
10
+ /** Current presentation — URL is used to detect source changes. */
11
+ presentation?: {
12
+ url?: string;
13
+ };
14
+ }
15
+ /**
16
+ * Owners shape for playback initiation tracking.
17
+ */
18
+ interface PlaybackInitiatedOwners {
19
+ mediaElement?: HTMLMediaElement | undefined;
20
+ }
21
+ /**
22
+ * Track whether playback has been initiated for the current presentation URL.
23
+ *
24
+ * Uses a local intermediate signal written by two effect streams:
25
+ * - false stream: resets on URL change
26
+ * - true stream: sets on play event
27
+ *
28
+ * A third merge effect reads the local signal and writes to state, reading
29
+ * `state.get()` at merge time so the spread uses the up-to-date value after
30
+ * the async forward bridge has run.
31
+ *
32
+ * @example
33
+ * const cleanup = trackPlaybackInitiated({ state, owners, events });
34
+ */
35
+ declare function trackPlaybackInitiated<S extends PlaybackInitiatedState, O extends PlaybackInitiatedOwners>({
36
+ state,
37
+ owners
38
+ }: {
39
+ state: Signal<S>;
40
+ owners: Signal<O>;
41
+ }): () => void;
42
+ //#endregion
43
+ export { PlaybackInitiatedOwners, PlaybackInitiatedState, trackPlaybackInitiated };
44
+ //# sourceMappingURL=track-playback-initiated.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"track-playback-initiated.d.ts","names":[],"sources":["../../../../src/dom/features/track-playback-initiated.ts"],"mappings":";;;;;AAOA;UAAiB,sBAAA;;EAEf,iBAAA;EAAA;EAEA,YAAA;IAAiB,GAAA;EAAA;AAAA;AAMnB;;;AAAA,UAAiB,uBAAA;EACf,YAAA,GAAe,gBAAA;AAAA;;;;;;;;;;;;;;;iBAiBD,sBAAA,WAAiC,sBAAA,YAAkC,uBAAA,CAAA,CAAA;EACjF,KAAA;EACA;AAAA;EAEA,KAAA,EAAO,MAAA,CAAO,CAAA;EACd,MAAA,EAAQ,MAAA,CAAO,CAAA;AAAA"}
@@ -0,0 +1,53 @@
1
+ import { effect } from "../../core/signals/effect.js";
2
+ import { computed, signal, update } from "../../core/signals/primitives.js";
3
+ import { listen } from "@videojs/utils/dom";
4
+ //#region src/dom/features/track-playback-initiated.ts
5
+ /**
6
+ * Track whether playback has been initiated for the current presentation URL.
7
+ *
8
+ * Uses a local intermediate signal written by two effect streams:
9
+ * - false stream: resets on URL change
10
+ * - true stream: sets on play event
11
+ *
12
+ * A third merge effect reads the local signal and writes to state, reading
13
+ * `state.get()` at merge time so the spread uses the up-to-date value after
14
+ * the async forward bridge has run.
15
+ *
16
+ * @example
17
+ * const cleanup = trackPlaybackInitiated({ state, owners, events });
18
+ */
19
+ function trackPlaybackInitiated({ state, owners }) {
20
+ const presentationUrl = computed(() => state.get().presentation?.url);
21
+ const mediaElement = computed(() => owners.get().mediaElement);
22
+ const playbackInitiated = signal(void 0);
23
+ let lastPresentationUrl;
24
+ let lastMediaElement;
25
+ const cleanupResetEffect = effect(() => {
26
+ const url = presentationUrl.get();
27
+ const el = mediaElement.get();
28
+ if (url !== lastPresentationUrl && lastPresentationUrl !== void 0 || el !== lastMediaElement && lastMediaElement !== void 0) playbackInitiated.set(false);
29
+ lastPresentationUrl = url;
30
+ lastMediaElement = el;
31
+ });
32
+ const cleanupPlayEffect = effect(() => {
33
+ const el = mediaElement.get();
34
+ if (!el) return;
35
+ return listen(el, "play", () => {
36
+ playbackInitiated.set(true);
37
+ });
38
+ });
39
+ const cleanupMergeEffect = effect(() => {
40
+ const pi = playbackInitiated.get();
41
+ if (pi === void 0) return;
42
+ if (state.get().playbackInitiated !== pi) update(state, { playbackInitiated: pi });
43
+ });
44
+ return () => {
45
+ cleanupResetEffect();
46
+ cleanupPlayEffect();
47
+ cleanupMergeEffect();
48
+ };
49
+ }
50
+ //#endregion
51
+ export { trackPlaybackInitiated };
52
+
53
+ //# sourceMappingURL=track-playback-initiated.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"track-playback-initiated.js","names":[],"sources":["../../../../src/dom/features/track-playback-initiated.ts"],"sourcesContent":["import { listen } from '@videojs/utils/dom';\nimport { effect } from '../../core/signals/effect';\nimport { computed, type Signal, signal, update } from '../../core/signals/primitives';\n\n/**\n * State shape for playback initiation tracking.\n */\nexport interface PlaybackInitiatedState {\n /** True once play has been requested for the current presentation URL. */\n playbackInitiated?: boolean;\n /** Current presentation — URL is used to detect source changes. */\n presentation?: { url?: string };\n}\n\n/**\n * Owners shape for playback initiation tracking.\n */\nexport interface PlaybackInitiatedOwners {\n mediaElement?: HTMLMediaElement | undefined;\n}\n\n/**\n * Track whether playback has been initiated for the current presentation URL.\n *\n * Uses a local intermediate signal written by two effect streams:\n * - false stream: resets on URL change\n * - true stream: sets on play event\n *\n * A third merge effect reads the local signal and writes to state, reading\n * `state.get()` at merge time so the spread uses the up-to-date value after\n * the async forward bridge has run.\n *\n * @example\n * const cleanup = trackPlaybackInitiated({ state, owners, events });\n */\nexport function trackPlaybackInitiated<S extends PlaybackInitiatedState, O extends PlaybackInitiatedOwners>({\n state,\n owners,\n}: {\n state: Signal<S>;\n owners: Signal<O>;\n}): () => void {\n const presentationUrl = computed(() => state.get().presentation?.url);\n const mediaElement = computed(() => owners.get().mediaElement);\n\n // Local signal: written by the URL effect (false) and the play listener (true).\n // undefined = not yet initialized (suppresses the merge effect on startup).\n const playbackInitiated = signal<boolean | undefined>(undefined);\n\n let lastPresentationUrl: string | undefined;\n let lastMediaElement: HTMLMediaElement | undefined;\n\n // False stream: reset on URL change or element swap.\n const cleanupResetEffect = effect(() => {\n const url = presentationUrl.get();\n const el = mediaElement.get();\n\n const urlChanged = url !== lastPresentationUrl;\n const elChanged = el !== lastMediaElement;\n\n if ((urlChanged && lastPresentationUrl !== undefined) || (elChanged && lastMediaElement !== undefined)) {\n playbackInitiated.set(false);\n }\n\n lastPresentationUrl = url;\n lastMediaElement = el;\n });\n\n // True stream: set on play event. Cleanup return removes the listener on element swap.\n const cleanupPlayEffect = effect(() => {\n const el = mediaElement.get();\n if (!el) return;\n return listen(el, 'play', () => {\n playbackInitiated.set(true);\n });\n });\n\n // Merge effect: bridge local signal → state.\n // Reads state.get() at merge time (after the async forward bridge has flushed)\n // so the spread captures the current value rather than a stale event-time snapshot.\n // The guard makes the write idempotent.\n const cleanupMergeEffect = effect(() => {\n const pi = playbackInitiated.get();\n if (pi === undefined) return;\n const current = state.get();\n if (current.playbackInitiated !== pi) {\n const patch: Partial<PlaybackInitiatedState> = { playbackInitiated: pi };\n update(state, patch);\n }\n });\n\n return () => {\n cleanupResetEffect();\n cleanupPlayEffect();\n cleanupMergeEffect();\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAmCA,SAAgB,uBAA4F,EAC1G,OACA,UAIa;CACb,MAAM,kBAAkB,eAAe,MAAM,KAAK,CAAC,cAAc,IAAI;CACrE,MAAM,eAAe,eAAe,OAAO,KAAK,CAAC,aAAa;CAI9D,MAAM,oBAAoB,OAA4B,KAAA,EAAU;CAEhE,IAAI;CACJ,IAAI;CAGJ,MAAM,qBAAqB,aAAa;EACtC,MAAM,MAAM,gBAAgB,KAAK;EACjC,MAAM,KAAK,aAAa,KAAK;AAK7B,MAHmB,QAAQ,uBAGR,wBAAwB,KAAA,KAFzB,OAAO,oBAE8C,qBAAqB,KAAA,EAC1F,mBAAkB,IAAI,MAAM;AAG9B,wBAAsB;AACtB,qBAAmB;GACnB;CAGF,MAAM,oBAAoB,aAAa;EACrC,MAAM,KAAK,aAAa,KAAK;AAC7B,MAAI,CAAC,GAAI;AACT,SAAO,OAAO,IAAI,cAAc;AAC9B,qBAAkB,IAAI,KAAK;IAC3B;GACF;CAMF,MAAM,qBAAqB,aAAa;EACtC,MAAM,KAAK,kBAAkB,KAAK;AAClC,MAAI,OAAO,KAAA,EAAW;AAEtB,MADgB,MAAM,KAAK,CACf,sBAAsB,GAEhC,QAAO,OADwC,EAAE,mBAAmB,IAAI,CACpD;GAEtB;AAEF,cAAa;AACX,sBAAoB;AACpB,qBAAmB;AACnB,sBAAoB"}
@@ -0,0 +1,34 @@
1
+ import { Signal } from "../../core/signals/primitives.js";
2
+
3
+ //#region src/dom/features/track-playback-rate.d.ts
4
+ /**
5
+ * State shape for playback rate tracking.
6
+ */
7
+ interface PlaybackRateState {
8
+ playbackRate?: number;
9
+ }
10
+ /**
11
+ * Owners shape for playback rate tracking.
12
+ */
13
+ interface PlaybackRateOwners {
14
+ mediaElement?: HTMLMediaElement | undefined;
15
+ }
16
+ /**
17
+ * Track playback rate from the media element.
18
+ *
19
+ * Mirrors `mediaElement.playbackRate` into reactive state on each `ratechange`
20
+ * event. Also syncs immediately when a media element becomes available.
21
+ *
22
+ * @example
23
+ * const cleanup = trackPlaybackRate({ state, owners });
24
+ */
25
+ declare function trackPlaybackRate<O extends PlaybackRateOwners>({
26
+ state,
27
+ owners
28
+ }: {
29
+ state: Signal<PlaybackRateState>;
30
+ owners: Signal<O>;
31
+ }): () => void;
32
+ //#endregion
33
+ export { PlaybackRateOwners, PlaybackRateState, trackPlaybackRate };
34
+ //# sourceMappingURL=track-playback-rate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"track-playback-rate.d.ts","names":[],"sources":["../../../../src/dom/features/track-playback-rate.ts"],"mappings":";;;;;AAOA;UAAiB,iBAAA;EACf,YAAA;AAAA;;AAMF;;UAAiB,kBAAA;EACf,YAAA,GAAe,gBAAA;AAAA;AAYjB;;;;;;;;;AAAA,iBAAgB,iBAAA,WAA4B,kBAAA,CAAA,CAAA;EAC1C,KAAA;EACA;AAAA;EAEA,KAAA,EAAO,MAAA,CAAO,iBAAA;EACd,MAAA,EAAQ,MAAA,CAAO,CAAA;AAAA"}
@@ -0,0 +1,32 @@
1
+ import { effect } from "../../core/signals/effect.js";
2
+ import { computed, update } from "../../core/signals/primitives.js";
3
+ import { listen } from "@videojs/utils/dom";
4
+ //#region src/dom/features/track-playback-rate.ts
5
+ /**
6
+ * Track playback rate from the media element.
7
+ *
8
+ * Mirrors `mediaElement.playbackRate` into reactive state on each `ratechange`
9
+ * event. Also syncs immediately when a media element becomes available.
10
+ *
11
+ * @example
12
+ * const cleanup = trackPlaybackRate({ state, owners });
13
+ */
14
+ function trackPlaybackRate({ state, owners }) {
15
+ const mediaElementSignal = computed(() => owners.get().mediaElement);
16
+ const canTrackPlaybackRate = computed(() => !!mediaElementSignal.get());
17
+ const cleanupEffect = effect(() => {
18
+ if (!canTrackPlaybackRate.get()) return;
19
+ const mediaElement = mediaElementSignal.get();
20
+ update(state, { playbackRate: mediaElement.playbackRate });
21
+ return listen(mediaElement, "ratechange", () => {
22
+ update(state, { playbackRate: mediaElement.playbackRate });
23
+ });
24
+ });
25
+ return () => {
26
+ cleanupEffect();
27
+ };
28
+ }
29
+ //#endregion
30
+ export { trackPlaybackRate };
31
+
32
+ //# sourceMappingURL=track-playback-rate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"track-playback-rate.js","names":[],"sources":["../../../../src/dom/features/track-playback-rate.ts"],"sourcesContent":["import { listen } from '@videojs/utils/dom';\nimport { effect } from '../../core/signals/effect';\nimport { computed, type Signal, update } from '../../core/signals/primitives';\n\n/**\n * State shape for playback rate tracking.\n */\nexport interface PlaybackRateState {\n playbackRate?: number;\n}\n\n/**\n * Owners shape for playback rate tracking.\n */\nexport interface PlaybackRateOwners {\n mediaElement?: HTMLMediaElement | undefined;\n}\n\n/**\n * Track playback rate from the media element.\n *\n * Mirrors `mediaElement.playbackRate` into reactive state on each `ratechange`\n * event. Also syncs immediately when a media element becomes available.\n *\n * @example\n * const cleanup = trackPlaybackRate({ state, owners });\n */\nexport function trackPlaybackRate<O extends PlaybackRateOwners>({\n state,\n owners,\n}: {\n state: Signal<PlaybackRateState>;\n owners: Signal<O>;\n}): () => void {\n const mediaElementSignal = computed(() => owners.get().mediaElement);\n const canTrackPlaybackRate = computed(() => !!mediaElementSignal.get());\n\n const cleanupEffect = effect(() => {\n if (!canTrackPlaybackRate.get()) return;\n const mediaElement = mediaElementSignal.get() as HTMLMediaElement;\n\n update(state, { playbackRate: mediaElement.playbackRate });\n\n return listen(mediaElement, 'ratechange', () => {\n update(state, { playbackRate: mediaElement.playbackRate });\n });\n });\n\n return () => {\n cleanupEffect();\n };\n}\n"],"mappings":";;;;;;;;;;;;;AA2BA,SAAgB,kBAAgD,EAC9D,OACA,UAIa;CACb,MAAM,qBAAqB,eAAe,OAAO,KAAK,CAAC,aAAa;CACpE,MAAM,uBAAuB,eAAe,CAAC,CAAC,mBAAmB,KAAK,CAAC;CAEvE,MAAM,gBAAgB,aAAa;AACjC,MAAI,CAAC,qBAAqB,KAAK,CAAE;EACjC,MAAM,eAAe,mBAAmB,KAAK;AAE7C,SAAO,OAAO,EAAE,cAAc,aAAa,cAAc,CAAC;AAE1D,SAAO,OAAO,cAAc,oBAAoB;AAC9C,UAAO,OAAO,EAAE,cAAc,aAAa,cAAc,CAAC;IAC1D;GACF;AAEF,cAAa;AACX,iBAAe"}
@@ -0,0 +1,78 @@
1
+ import { effect } from "../../core/signals/effect.js";
2
+ import { hasPresentationDuration } from "../../core/types/index.js";
3
+ //#region src/dom/features/update-duration.ts
4
+ /**
5
+ * Check if we can update MediaSource duration (have required data).
6
+ */
7
+ function canUpdateDuration(state, owners) {
8
+ return !!(owners.mediaSource && state.presentation && hasPresentationDuration(state.presentation));
9
+ }
10
+ /**
11
+ * Get the maximum buffered end time across all SourceBuffers.
12
+ */
13
+ function getMaxBufferedEnd(owners) {
14
+ let maxEnd = 0;
15
+ const buffers = [owners.videoSourceBuffer, owners.audioSourceBuffer].filter((buf) => buf !== void 0);
16
+ for (const buffer of buffers) {
17
+ const { buffered } = buffer;
18
+ if (buffered.length > 0) {
19
+ const end = buffered.end(buffered.length - 1);
20
+ if (end > maxEnd) maxEnd = end;
21
+ }
22
+ }
23
+ return maxEnd;
24
+ }
25
+ /**
26
+ * Check if we should update MediaSource duration (conditions met).
27
+ */
28
+ function shouldUpdateDuration(state, owners) {
29
+ if (!canUpdateDuration(state, owners)) return false;
30
+ const { mediaSource } = owners;
31
+ const { presentation } = state;
32
+ if ((owners.mediaSourceReadyState?.get() ?? owners.mediaSource?.readyState) !== "open") return false;
33
+ const duration = presentation.duration;
34
+ if (!Number.isFinite(duration) || Number.isNaN(duration) || duration <= 0) return false;
35
+ return Number.isNaN(mediaSource.duration);
36
+ }
37
+ /**
38
+ * Wait for all currently-updating SourceBuffers to finish.
39
+ *
40
+ * The MSE spec forbids setting MediaSource.duration while any attached
41
+ * SourceBuffer has updating === true. This defers until all are idle.
42
+ */
43
+ function waitForSourceBuffersReady(owners) {
44
+ const updating = [owners.videoSourceBuffer, owners.audioSourceBuffer].filter((buf) => buf?.updating === true);
45
+ if (updating.length === 0) return Promise.resolve();
46
+ return Promise.all(updating.map((buf) => new Promise((resolve) => buf.addEventListener("updateend", () => resolve(), { once: true })))).then(() => void 0);
47
+ }
48
+ /**
49
+ * Update MediaSource duration when presentation duration becomes available.
50
+ */
51
+ function updateDuration({ state, owners }) {
52
+ let destroyed = false;
53
+ let running = false;
54
+ const cleanupEffect = effect(() => {
55
+ const currentState = state.get();
56
+ const currentOwners = owners.get();
57
+ if (!shouldUpdateDuration(currentState, currentOwners) || running) return;
58
+ const { mediaSource } = currentOwners;
59
+ running = true;
60
+ waitForSourceBuffersReady(currentOwners).then(() => {
61
+ if (destroyed || mediaSource.readyState !== "open") return;
62
+ let duration = currentState.presentation.duration;
63
+ const maxBufferedEnd = getMaxBufferedEnd(currentOwners);
64
+ if (maxBufferedEnd > duration) duration = maxBufferedEnd;
65
+ mediaSource.duration = duration;
66
+ }).finally(() => {
67
+ running = false;
68
+ });
69
+ });
70
+ return () => {
71
+ destroyed = true;
72
+ cleanupEffect();
73
+ };
74
+ }
75
+ //#endregion
76
+ export { updateDuration };
77
+
78
+ //# sourceMappingURL=update-duration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-duration.js","names":[],"sources":["../../../../src/dom/features/update-duration.ts"],"sourcesContent":["import { effect } from '../../core/signals/effect';\nimport type { ReadonlySignal, Signal } from '../../core/signals/primitives';\nimport type { Presentation } from '../../core/types';\nimport { hasPresentationDuration } from '../../core/types';\n\nexport interface DurationUpdateState {\n presentation?: Presentation;\n}\n\nexport interface DurationUpdateOwners {\n mediaSource?: MediaSource;\n /** Reactive mirror of `mediaSource.readyState` — updated via DOM events. */\n mediaSourceReadyState?: ReadonlySignal<MediaSource['readyState']>;\n videoSourceBuffer?: SourceBuffer;\n audioSourceBuffer?: SourceBuffer;\n}\n\n/**\n * Check if we can update MediaSource duration (have required data).\n */\nexport function canUpdateDuration(state: DurationUpdateState, owners: DurationUpdateOwners): boolean {\n return !!(owners.mediaSource && state.presentation && hasPresentationDuration(state.presentation));\n}\n\n/**\n * Get the maximum buffered end time across all SourceBuffers.\n */\nexport function getMaxBufferedEnd(owners: DurationUpdateOwners): number {\n let maxEnd = 0;\n\n const buffers = [owners.videoSourceBuffer, owners.audioSourceBuffer].filter(\n (buf): buf is SourceBuffer => buf !== undefined\n );\n\n for (const buffer of buffers) {\n const { buffered } = buffer;\n if (buffered.length > 0) {\n const end = buffered.end(buffered.length - 1);\n if (end > maxEnd) {\n maxEnd = end;\n }\n }\n }\n\n return maxEnd;\n}\n\n/**\n * Check if we should update MediaSource duration (conditions met).\n */\nexport function shouldUpdateDuration(state: DurationUpdateState, owners: DurationUpdateOwners): boolean {\n if (!canUpdateDuration(state, owners)) return false;\n\n const { mediaSource } = owners;\n const { presentation } = state;\n\n // MediaSource must be open — use reactive readyState signal when available\n // so the effect re-evaluates when readyState changes from 'closed' to 'open'.\n const readyState = owners.mediaSourceReadyState?.get() ?? owners.mediaSource?.readyState;\n if (readyState !== 'open') return false;\n\n const duration = presentation!.duration!;\n\n // Validate duration: finite, positive, not NaN\n if (!Number.isFinite(duration) || Number.isNaN(duration) || duration <= 0) return false;\n\n // Only set duration on initial MediaSource setup, when it hasn't been set yet.\n // A freshly opened MediaSource has duration === NaN. Once set (either by this\n // task or by endOfStreamTask from buffered.end()), we leave it alone — attempting\n // to re-sync a slight drift between mediaSource.duration and presentation.duration\n // races with concurrent appendBuffer() calls from loadSegmentsTask.\n return Number.isNaN(mediaSource!.duration);\n}\n\n/**\n * Wait for all currently-updating SourceBuffers to finish.\n *\n * The MSE spec forbids setting MediaSource.duration while any attached\n * SourceBuffer has updating === true. This defers until all are idle.\n */\nfunction waitForSourceBuffersReady(owners: DurationUpdateOwners): Promise<void> {\n const updating = [owners.videoSourceBuffer, owners.audioSourceBuffer].filter(\n (buf): buf is SourceBuffer => buf?.updating === true\n );\n\n if (updating.length === 0) return Promise.resolve();\n\n return Promise.all(\n updating.map(\n (buf) => new Promise<void>((resolve) => buf.addEventListener('updateend', () => resolve(), { once: true }))\n )\n ).then(() => undefined);\n}\n\n/**\n * Update MediaSource duration when presentation duration becomes available.\n */\nexport function updateDuration<S extends DurationUpdateState, O extends DurationUpdateOwners>({\n state,\n owners,\n}: {\n state: Signal<S>;\n owners: Signal<O>;\n}): () => void {\n let destroyed = false;\n let running = false;\n\n const cleanupEffect = effect(() => {\n const currentState = state.get();\n const currentOwners = owners.get();\n\n if (!shouldUpdateDuration(currentState, currentOwners) || running) return;\n\n const { mediaSource } = currentOwners;\n running = true;\n\n // MSE spec: duration cannot be set while any SourceBuffer is updating.\n // Capture the snapshot; the async helper does not read signals.\n waitForSourceBuffersReady(currentOwners)\n .then(() => {\n // Re-check after async wait: destroyed, or readyState changed (e.g. endOfStream\n // already called endOfStream(), transitioning 'open' → 'ended').\n if (destroyed || mediaSource!.readyState !== 'open') return;\n\n let duration = currentState.presentation!.duration!;\n\n // MSE spec: duration cannot be less than any buffered range.\n // If buffered ranges exceed calculated duration, extend to match.\n const maxBufferedEnd = getMaxBufferedEnd(currentOwners);\n if (maxBufferedEnd > duration) {\n duration = maxBufferedEnd;\n }\n\n mediaSource!.duration = duration;\n })\n .finally(() => {\n running = false;\n });\n });\n\n return () => {\n destroyed = true;\n cleanupEffect();\n };\n}\n"],"mappings":";;;;;;AAoBA,SAAgB,kBAAkB,OAA4B,QAAuC;AACnG,QAAO,CAAC,EAAE,OAAO,eAAe,MAAM,gBAAgB,wBAAwB,MAAM,aAAa;;;;;AAMnG,SAAgB,kBAAkB,QAAsC;CACtE,IAAI,SAAS;CAEb,MAAM,UAAU,CAAC,OAAO,mBAAmB,OAAO,kBAAkB,CAAC,QAClE,QAA6B,QAAQ,KAAA,EACvC;AAED,MAAK,MAAM,UAAU,SAAS;EAC5B,MAAM,EAAE,aAAa;AACrB,MAAI,SAAS,SAAS,GAAG;GACvB,MAAM,MAAM,SAAS,IAAI,SAAS,SAAS,EAAE;AAC7C,OAAI,MAAM,OACR,UAAS;;;AAKf,QAAO;;;;;AAMT,SAAgB,qBAAqB,OAA4B,QAAuC;AACtG,KAAI,CAAC,kBAAkB,OAAO,OAAO,CAAE,QAAO;CAE9C,MAAM,EAAE,gBAAgB;CACxB,MAAM,EAAE,iBAAiB;AAKzB,MADmB,OAAO,uBAAuB,KAAK,IAAI,OAAO,aAAa,gBAC3D,OAAQ,QAAO;CAElC,MAAM,WAAW,aAAc;AAG/B,KAAI,CAAC,OAAO,SAAS,SAAS,IAAI,OAAO,MAAM,SAAS,IAAI,YAAY,EAAG,QAAO;AAOlF,QAAO,OAAO,MAAM,YAAa,SAAS;;;;;;;;AAS5C,SAAS,0BAA0B,QAA6C;CAC9E,MAAM,WAAW,CAAC,OAAO,mBAAmB,OAAO,kBAAkB,CAAC,QACnE,QAA6B,KAAK,aAAa,KACjD;AAED,KAAI,SAAS,WAAW,EAAG,QAAO,QAAQ,SAAS;AAEnD,QAAO,QAAQ,IACb,SAAS,KACN,QAAQ,IAAI,SAAe,YAAY,IAAI,iBAAiB,mBAAmB,SAAS,EAAE,EAAE,MAAM,MAAM,CAAC,CAAC,CAC5G,CACF,CAAC,WAAW,KAAA,EAAU;;;;;AAMzB,SAAgB,eAA8E,EAC5F,OACA,UAIa;CACb,IAAI,YAAY;CAChB,IAAI,UAAU;CAEd,MAAM,gBAAgB,aAAa;EACjC,MAAM,eAAe,MAAM,KAAK;EAChC,MAAM,gBAAgB,OAAO,KAAK;AAElC,MAAI,CAAC,qBAAqB,cAAc,cAAc,IAAI,QAAS;EAEnE,MAAM,EAAE,gBAAgB;AACxB,YAAU;AAIV,4BAA0B,cAAc,CACrC,WAAW;AAGV,OAAI,aAAa,YAAa,eAAe,OAAQ;GAErD,IAAI,WAAW,aAAa,aAAc;GAI1C,MAAM,iBAAiB,kBAAkB,cAAc;AACvD,OAAI,iBAAiB,SACnB,YAAW;AAGb,eAAa,WAAW;IACxB,CACD,cAAc;AACb,aAAU;IACV;GACJ;AAEF,cAAa;AACX,cAAY;AACZ,iBAAe"}
@@ -0,0 +1,24 @@
1
+ //#region src/dom/media/append-segment.d.ts
2
+ /**
3
+ * Segment appender helper.
4
+ *
5
+ * Appends media data (ArrayBuffer or AsyncIterable<Uint8Array> stream) to a
6
+ * SourceBuffer, waiting for `updateend` between calls so the browser can
7
+ * process each append before the next one arrives.
8
+ */
9
+ /** Data accepted by appendSegment — a full buffer or an async chunk stream. */
10
+ type AppendData = ArrayBuffer | AsyncIterable<Uint8Array>;
11
+ /**
12
+ * Append media data to a SourceBuffer.
13
+ *
14
+ * Accepts either a full ArrayBuffer (single append) or an AsyncIterable of
15
+ * Uint8Array chunks (one append per chunk, in order). Waits for `updateend`
16
+ * between each call so appends are serialized correctly.
17
+ *
18
+ * Errors from the SourceBuffer (`error` event) or from the iterable are
19
+ * propagated as rejections.
20
+ */
21
+ declare function appendSegment(sourceBuffer: SourceBuffer, data: AppendData, signal?: AbortSignal): Promise<void>;
22
+ //#endregion
23
+ export { AppendData, appendSegment };
24
+ //# sourceMappingURL=append-segment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"append-segment.d.ts","names":[],"sources":["../../../../src/dom/media/append-segment.ts"],"mappings":";;AASA;;;;;;;KAAY,UAAA,GAAa,WAAA,GAAc,aAAA,CAAc,UAAA;;;;;;AAYrD;;;;;iBAAsB,aAAA,CAAc,YAAA,EAAc,YAAA,EAAc,IAAA,EAAM,UAAA,EAAY,MAAA,GAAS,WAAA,GAAc,OAAA"}
@@ -0,0 +1,60 @@
1
+ //#region src/dom/media/append-segment.ts
2
+ /**
3
+ * Append media data to a SourceBuffer.
4
+ *
5
+ * Accepts either a full ArrayBuffer (single append) or an AsyncIterable of
6
+ * Uint8Array chunks (one append per chunk, in order). Waits for `updateend`
7
+ * between each call so appends are serialized correctly.
8
+ *
9
+ * Errors from the SourceBuffer (`error` event) or from the iterable are
10
+ * propagated as rejections.
11
+ */
12
+ async function appendSegment(sourceBuffer, data, signal) {
13
+ if (data instanceof ArrayBuffer) await appendChunk(sourceBuffer, data);
14
+ else try {
15
+ for await (const chunk of data) {
16
+ if (signal?.aborted) throw signal.reason ?? new DOMException("Aborted", "AbortError");
17
+ await appendChunk(sourceBuffer, chunk);
18
+ }
19
+ } catch (e) {
20
+ if (e instanceof DOMException && e.name === "AbortError" && !sourceBuffer.updating) try {
21
+ sourceBuffer.abort();
22
+ } catch {}
23
+ throw e;
24
+ }
25
+ }
26
+ async function appendChunk(sourceBuffer, data) {
27
+ if (sourceBuffer.updating) await new Promise((resolve) => {
28
+ const onUpdateEnd = () => {
29
+ sourceBuffer.removeEventListener("updateend", onUpdateEnd);
30
+ resolve();
31
+ };
32
+ sourceBuffer.addEventListener("updateend", onUpdateEnd);
33
+ });
34
+ return new Promise((resolve, reject) => {
35
+ const onUpdateEnd = () => {
36
+ cleanup();
37
+ resolve();
38
+ };
39
+ const onError = (event) => {
40
+ cleanup();
41
+ reject(/* @__PURE__ */ new Error(`SourceBuffer append error: ${event.type}`));
42
+ };
43
+ const cleanup = () => {
44
+ sourceBuffer.removeEventListener("updateend", onUpdateEnd);
45
+ sourceBuffer.removeEventListener("error", onError);
46
+ };
47
+ sourceBuffer.addEventListener("updateend", onUpdateEnd);
48
+ sourceBuffer.addEventListener("error", onError);
49
+ try {
50
+ sourceBuffer.appendBuffer(data);
51
+ } catch (error) {
52
+ cleanup();
53
+ reject(error);
54
+ }
55
+ });
56
+ }
57
+ //#endregion
58
+ export { appendSegment };
59
+
60
+ //# sourceMappingURL=append-segment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"append-segment.js","names":[],"sources":["../../../../src/dom/media/append-segment.ts"],"sourcesContent":["/**\n * Segment appender helper.\n *\n * Appends media data (ArrayBuffer or AsyncIterable<Uint8Array> stream) to a\n * SourceBuffer, waiting for `updateend` between calls so the browser can\n * process each append before the next one arrives.\n */\n\n/** Data accepted by appendSegment — a full buffer or an async chunk stream. */\nexport type AppendData = ArrayBuffer | AsyncIterable<Uint8Array>;\n\n/**\n * Append media data to a SourceBuffer.\n *\n * Accepts either a full ArrayBuffer (single append) or an AsyncIterable of\n * Uint8Array chunks (one append per chunk, in order). Waits for `updateend`\n * between each call so appends are serialized correctly.\n *\n * Errors from the SourceBuffer (`error` event) or from the iterable are\n * propagated as rejections.\n */\nexport async function appendSegment(sourceBuffer: SourceBuffer, data: AppendData, signal?: AbortSignal): Promise<void> {\n if (data instanceof ArrayBuffer) {\n await appendChunk(sourceBuffer, data);\n } else {\n try {\n for await (const chunk of data) {\n // Check between chunks so an abort can stop streaming before the next\n // appendBuffer call. The current chunk (if any) has already landed in the\n // SourceBuffer; the partial: true flag in the actor model reflects this.\n if (signal?.aborted) throw signal.reason ?? new DOMException('Aborted', 'AbortError');\n await appendChunk(sourceBuffer, chunk);\n }\n } catch (e) {\n // Reset the MSE segment parser on any abort to discard partial fMP4 box\n // data from the SourceBuffer's internal byte buffer. Two paths reach here:\n //\n // 1. Explicit abort check above (signal.aborted between chunks) — the\n // last appended chunk may have left the parser mid-fragment.\n //\n // 2. The fetch was cancelled (signal fired), causing the underlying\n // ReadableStream to error and the for-await loop to throw directly\n // without ever passing through the signal check above. Same cleanup\n // is needed: partial box data must be cleared before the next append.\n //\n // Without this, the next appendBuffer call sees stale partial data and\n // Chrome throws CHUNK_DEMUXER_ERROR_APPEND_FAILED.\n if (e instanceof DOMException && e.name === 'AbortError' && !sourceBuffer.updating) {\n try {\n sourceBuffer.abort();\n } catch {\n // Thrown if the MediaSource is not \"open\" (e.g. during teardown).\n }\n }\n throw e;\n }\n }\n}\n\nasync function appendChunk(sourceBuffer: SourceBuffer, data: ArrayBuffer | Uint8Array<ArrayBufferLike>): Promise<void> {\n if (sourceBuffer.updating) {\n await new Promise<void>((resolve) => {\n const onUpdateEnd = () => {\n sourceBuffer.removeEventListener('updateend', onUpdateEnd);\n resolve();\n };\n sourceBuffer.addEventListener('updateend', onUpdateEnd);\n });\n }\n\n return new Promise<void>((resolve, reject) => {\n const onUpdateEnd = () => {\n cleanup();\n resolve();\n };\n\n const onError = (event: Event) => {\n cleanup();\n reject(new Error(`SourceBuffer append error: ${event.type}`));\n };\n\n const cleanup = () => {\n sourceBuffer.removeEventListener('updateend', onUpdateEnd);\n sourceBuffer.removeEventListener('error', onError);\n };\n\n sourceBuffer.addEventListener('updateend', onUpdateEnd);\n sourceBuffer.addEventListener('error', onError);\n\n try {\n sourceBuffer.appendBuffer(data as ArrayBuffer);\n } catch (error) {\n cleanup();\n reject(error);\n }\n });\n}\n"],"mappings":";;;;;;;;;;;AAqBA,eAAsB,cAAc,cAA4B,MAAkB,QAAqC;AACrH,KAAI,gBAAgB,YAClB,OAAM,YAAY,cAAc,KAAK;KAErC,KAAI;AACF,aAAW,MAAM,SAAS,MAAM;AAI9B,OAAI,QAAQ,QAAS,OAAM,OAAO,UAAU,IAAI,aAAa,WAAW,aAAa;AACrF,SAAM,YAAY,cAAc,MAAM;;UAEjC,GAAG;AAcV,MAAI,aAAa,gBAAgB,EAAE,SAAS,gBAAgB,CAAC,aAAa,SACxE,KAAI;AACF,gBAAa,OAAO;UACd;AAIV,QAAM;;;AAKZ,eAAe,YAAY,cAA4B,MAAgE;AACrH,KAAI,aAAa,SACf,OAAM,IAAI,SAAe,YAAY;EACnC,MAAM,oBAAoB;AACxB,gBAAa,oBAAoB,aAAa,YAAY;AAC1D,YAAS;;AAEX,eAAa,iBAAiB,aAAa,YAAY;GACvD;AAGJ,QAAO,IAAI,SAAe,SAAS,WAAW;EAC5C,MAAM,oBAAoB;AACxB,YAAS;AACT,YAAS;;EAGX,MAAM,WAAW,UAAiB;AAChC,YAAS;AACT,0BAAO,IAAI,MAAM,8BAA8B,MAAM,OAAO,CAAC;;EAG/D,MAAM,gBAAgB;AACpB,gBAAa,oBAAoB,aAAa,YAAY;AAC1D,gBAAa,oBAAoB,SAAS,QAAQ;;AAGpD,eAAa,iBAAiB,aAAa,YAAY;AACvD,eAAa,iBAAiB,SAAS,QAAQ;AAE/C,MAAI;AACF,gBAAa,aAAa,KAAoB;WACvC,OAAO;AACd,YAAS;AACT,UAAO,MAAM;;GAEf"}
@@ -0,0 +1,24 @@
1
+ //#region src/dom/media/buffer-flusher.d.ts
2
+ /**
3
+ * Buffer flusher helper (P12)
4
+ *
5
+ * Removes a time range from a SourceBuffer to manage memory.
6
+ */
7
+ /**
8
+ * Remove a time range from a SourceBuffer.
9
+ *
10
+ * Waits for the SourceBuffer to be ready (not updating), then removes
11
+ * the specified range. Returns a promise that resolves when removal completes.
12
+ *
13
+ * @param sourceBuffer - The SourceBuffer to remove data from
14
+ * @param start - Start of the time range to remove (seconds)
15
+ * @param end - End of the time range to remove (seconds)
16
+ * @returns Promise that resolves when removal completes
17
+ *
18
+ * @example
19
+ * await flushBuffer(videoSourceBuffer, 0, 30);
20
+ */
21
+ declare function flushBuffer(sourceBuffer: SourceBuffer, start: number, end: number): Promise<void>;
22
+ //#endregion
23
+ export { flushBuffer };
24
+ //# sourceMappingURL=buffer-flusher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buffer-flusher.d.ts","names":[],"sources":["../../../../src/dom/media/buffer-flusher.ts"],"mappings":";;AAoBA;;;;;;;;;;;;;;;;;;iBAAsB,WAAA,CAAY,YAAA,EAAc,YAAA,EAAc,KAAA,UAAe,GAAA,WAAc,OAAA"}
@@ -0,0 +1,55 @@
1
+ //#region src/dom/media/buffer-flusher.ts
2
+ /**
3
+ * Buffer flusher helper (P12)
4
+ *
5
+ * Removes a time range from a SourceBuffer to manage memory.
6
+ */
7
+ /**
8
+ * Remove a time range from a SourceBuffer.
9
+ *
10
+ * Waits for the SourceBuffer to be ready (not updating), then removes
11
+ * the specified range. Returns a promise that resolves when removal completes.
12
+ *
13
+ * @param sourceBuffer - The SourceBuffer to remove data from
14
+ * @param start - Start of the time range to remove (seconds)
15
+ * @param end - End of the time range to remove (seconds)
16
+ * @returns Promise that resolves when removal completes
17
+ *
18
+ * @example
19
+ * await flushBuffer(videoSourceBuffer, 0, 30);
20
+ */
21
+ async function flushBuffer(sourceBuffer, start, end) {
22
+ if (sourceBuffer.updating) await new Promise((resolve) => {
23
+ const onUpdateEnd = () => {
24
+ sourceBuffer.removeEventListener("updateend", onUpdateEnd);
25
+ resolve();
26
+ };
27
+ sourceBuffer.addEventListener("updateend", onUpdateEnd);
28
+ });
29
+ return new Promise((resolve, reject) => {
30
+ const onUpdateEnd = () => {
31
+ cleanup();
32
+ resolve();
33
+ };
34
+ const onError = (event) => {
35
+ cleanup();
36
+ reject(/* @__PURE__ */ new Error(`SourceBuffer remove error: ${event.type}`));
37
+ };
38
+ const cleanup = () => {
39
+ sourceBuffer.removeEventListener("updateend", onUpdateEnd);
40
+ sourceBuffer.removeEventListener("error", onError);
41
+ };
42
+ sourceBuffer.addEventListener("updateend", onUpdateEnd);
43
+ sourceBuffer.addEventListener("error", onError);
44
+ try {
45
+ sourceBuffer.remove(start, end);
46
+ } catch (error) {
47
+ cleanup();
48
+ reject(error);
49
+ }
50
+ });
51
+ }
52
+ //#endregion
53
+ export { flushBuffer };
54
+
55
+ //# sourceMappingURL=buffer-flusher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buffer-flusher.js","names":[],"sources":["../../../../src/dom/media/buffer-flusher.ts"],"sourcesContent":["/**\n * Buffer flusher helper (P12)\n *\n * Removes a time range from a SourceBuffer to manage memory.\n */\n\n/**\n * Remove a time range from a SourceBuffer.\n *\n * Waits for the SourceBuffer to be ready (not updating), then removes\n * the specified range. Returns a promise that resolves when removal completes.\n *\n * @param sourceBuffer - The SourceBuffer to remove data from\n * @param start - Start of the time range to remove (seconds)\n * @param end - End of the time range to remove (seconds)\n * @returns Promise that resolves when removal completes\n *\n * @example\n * await flushBuffer(videoSourceBuffer, 0, 30);\n */\nexport async function flushBuffer(sourceBuffer: SourceBuffer, start: number, end: number): Promise<void> {\n // Wait for SourceBuffer to be ready (not currently updating)\n if (sourceBuffer.updating) {\n await new Promise<void>((resolve) => {\n const onUpdateEnd = () => {\n sourceBuffer.removeEventListener('updateend', onUpdateEnd);\n resolve();\n };\n sourceBuffer.addEventListener('updateend', onUpdateEnd);\n });\n }\n\n return new Promise<void>((resolve, reject) => {\n const onUpdateEnd = () => {\n cleanup();\n resolve();\n };\n\n const onError = (event: Event) => {\n cleanup();\n reject(new Error(`SourceBuffer remove error: ${event.type}`));\n };\n\n const cleanup = () => {\n sourceBuffer.removeEventListener('updateend', onUpdateEnd);\n sourceBuffer.removeEventListener('error', onError);\n };\n\n sourceBuffer.addEventListener('updateend', onUpdateEnd);\n sourceBuffer.addEventListener('error', onError);\n\n try {\n sourceBuffer.remove(start, end);\n } catch (error) {\n cleanup();\n reject(error);\n }\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAoBA,eAAsB,YAAY,cAA4B,OAAe,KAA4B;AAEvG,KAAI,aAAa,SACf,OAAM,IAAI,SAAe,YAAY;EACnC,MAAM,oBAAoB;AACxB,gBAAa,oBAAoB,aAAa,YAAY;AAC1D,YAAS;;AAEX,eAAa,iBAAiB,aAAa,YAAY;GACvD;AAGJ,QAAO,IAAI,SAAe,SAAS,WAAW;EAC5C,MAAM,oBAAoB;AACxB,YAAS;AACT,YAAS;;EAGX,MAAM,WAAW,UAAiB;AAChC,YAAS;AACT,0BAAO,IAAI,MAAM,8BAA8B,MAAM,OAAO,CAAC;;EAG/D,MAAM,gBAAgB;AACpB,gBAAa,oBAAoB,aAAa,YAAY;AAC1D,gBAAa,oBAAoB,SAAS,QAAQ;;AAGpD,eAAa,iBAAiB,aAAa,YAAY;AACvD,eAAa,iBAAiB,SAAS,QAAQ;AAE/C,MAAI;AACF,gBAAa,OAAO,OAAO,IAAI;WACxB,OAAO;AACd,YAAS;AACT,UAAO,MAAM;;GAEf"}