@videojs/spf 10.0.0-beta.2 → 10.0.0-beta.21

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 (264) 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/create-machine-actor.js +91 -0
  13. package/dist/default/core/create-machine-actor.js.map +1 -0
  14. package/dist/default/core/create-machine-reactor.js +83 -0
  15. package/dist/default/core/create-machine-reactor.js.map +1 -0
  16. package/dist/default/core/create-transition-actor.js +46 -0
  17. package/dist/default/core/create-transition-actor.js.map +1 -0
  18. package/dist/default/core/features/calculate-presentation-duration.js +53 -0
  19. package/dist/default/core/features/calculate-presentation-duration.js.map +1 -0
  20. package/dist/default/core/features/quality-switching.js +69 -0
  21. package/dist/default/core/features/quality-switching.js.map +1 -0
  22. package/dist/default/core/features/resolve-presentation.js +73 -0
  23. package/dist/default/core/features/resolve-presentation.js.map +1 -0
  24. package/dist/default/core/features/resolve-track.js +66 -0
  25. package/dist/default/core/features/resolve-track.js.map +1 -0
  26. package/dist/default/core/features/select-tracks.js +130 -0
  27. package/dist/default/core/features/select-tracks.js.map +1 -0
  28. package/dist/default/core/features/sync-preload-attribute.js +26 -0
  29. package/dist/default/core/features/sync-preload-attribute.js.map +1 -0
  30. package/dist/default/core/hls/parse-attributes.js +134 -0
  31. package/dist/default/core/hls/parse-attributes.js.map +1 -0
  32. package/dist/default/core/hls/parse-media-playlist.js +82 -0
  33. package/dist/default/core/hls/parse-media-playlist.js.map +1 -0
  34. package/dist/default/core/hls/parse-multivariant.js +214 -0
  35. package/dist/default/core/hls/parse-multivariant.js.map +1 -0
  36. package/dist/default/core/hls/resolve-url.js +11 -0
  37. package/dist/default/core/hls/resolve-url.js.map +1 -0
  38. package/dist/default/core/machine.js +26 -0
  39. package/dist/default/core/machine.js.map +1 -0
  40. package/dist/default/core/signals/effect.js +41 -0
  41. package/dist/default/core/signals/effect.js.map +1 -0
  42. package/dist/default/core/signals/primitives.js +32 -0
  43. package/dist/default/core/signals/primitives.js.map +1 -0
  44. package/dist/default/core/task.js +191 -0
  45. package/dist/default/core/task.js.map +1 -0
  46. package/dist/default/core/types/index.js +15 -0
  47. package/dist/default/core/types/index.js.map +1 -0
  48. package/dist/default/core/utils/generate-id.js +21 -0
  49. package/dist/default/core/utils/generate-id.js.map +1 -0
  50. package/dist/default/core/utils/track-selection.js +39 -0
  51. package/dist/default/core/utils/track-selection.js.map +1 -0
  52. package/dist/default/dom/features/end-of-stream.js +145 -0
  53. package/dist/default/dom/features/end-of-stream.js.map +1 -0
  54. package/dist/default/dom/features/load-segments.js +192 -0
  55. package/dist/default/dom/features/load-segments.js.map +1 -0
  56. package/dist/default/dom/features/load-text-track-cues.js +104 -0
  57. package/dist/default/dom/features/load-text-track-cues.js.map +1 -0
  58. package/dist/default/dom/features/segment-loader-actor.js +243 -0
  59. package/dist/default/dom/features/segment-loader-actor.js.map +1 -0
  60. package/dist/default/dom/features/setup-mediasource.js +48 -0
  61. package/dist/default/dom/features/setup-mediasource.js.map +1 -0
  62. package/dist/default/dom/features/setup-sourcebuffer.js +78 -0
  63. package/dist/default/dom/features/setup-sourcebuffer.js.map +1 -0
  64. package/dist/default/dom/features/sync-text-tracks.js +95 -0
  65. package/dist/default/dom/features/sync-text-tracks.js.map +1 -0
  66. package/dist/default/dom/features/text-track-segment-loader-actor.js +55 -0
  67. package/dist/default/dom/features/text-track-segment-loader-actor.js.map +1 -0
  68. package/dist/default/dom/features/text-tracks-actor.js +42 -0
  69. package/dist/default/dom/features/text-tracks-actor.js.map +1 -0
  70. package/dist/default/dom/features/track-current-time.js +49 -0
  71. package/dist/default/dom/features/track-current-time.js.map +1 -0
  72. package/dist/default/dom/features/track-playback-initiated.js +66 -0
  73. package/dist/default/dom/features/track-playback-initiated.js.map +1 -0
  74. package/dist/default/dom/features/track-playback-rate.js +32 -0
  75. package/dist/default/dom/features/track-playback-rate.js.map +1 -0
  76. package/dist/default/dom/features/update-duration.js +78 -0
  77. package/dist/default/dom/features/update-duration.js.map +1 -0
  78. package/dist/default/dom/media/append-segment.js +60 -0
  79. package/dist/default/dom/media/append-segment.js.map +1 -0
  80. package/dist/default/dom/media/buffer-flusher.js +55 -0
  81. package/dist/default/dom/media/buffer-flusher.js.map +1 -0
  82. package/dist/default/dom/media/mediasource-setup.js +146 -0
  83. package/dist/default/dom/media/mediasource-setup.js.map +1 -0
  84. package/dist/default/dom/media/source-buffer-actor.js +137 -0
  85. package/dist/default/dom/media/source-buffer-actor.js.map +1 -0
  86. package/dist/default/dom/network/chunked-stream-iterable.js +49 -0
  87. package/dist/default/dom/network/chunked-stream-iterable.js.map +1 -0
  88. package/dist/default/dom/network/fetch.js +54 -0
  89. package/dist/default/dom/network/fetch.js.map +1 -0
  90. package/dist/default/dom/playback-engine/adapter.js +100 -0
  91. package/dist/default/dom/playback-engine/adapter.js.map +1 -0
  92. package/dist/default/dom/playback-engine/engine.js +142 -0
  93. package/dist/default/dom/playback-engine/engine.js.map +1 -0
  94. package/dist/default/dom/playback-engine.js +4 -0
  95. package/dist/default/dom/text/parse-vtt-segment.js +56 -0
  96. package/dist/default/dom/text/parse-vtt-segment.js.map +1 -0
  97. package/dist/default/dom.js +10 -0
  98. package/dist/{src-DRsS_H80.js → default/index.js} +3 -3
  99. package/dist/default/index.js.map +1 -0
  100. package/dist/dev/core/abr/bandwidth-estimator.d.ts +35 -0
  101. package/dist/dev/core/abr/bandwidth-estimator.d.ts.map +1 -0
  102. package/dist/dev/core/abr/bandwidth-estimator.js +95 -0
  103. package/dist/dev/core/abr/bandwidth-estimator.js.map +1 -0
  104. package/dist/dev/core/abr/ewma.js +71 -0
  105. package/dist/dev/core/abr/ewma.js.map +1 -0
  106. package/dist/dev/core/abr/quality-selection.js +53 -0
  107. package/dist/dev/core/abr/quality-selection.js.map +1 -0
  108. package/dist/dev/core/actor.d.ts +27 -0
  109. package/dist/dev/core/actor.d.ts.map +1 -0
  110. package/dist/dev/core/buffer/back-buffer.js +46 -0
  111. package/dist/dev/core/buffer/back-buffer.js.map +1 -0
  112. package/dist/dev/core/buffer/forward-buffer.js +73 -0
  113. package/dist/dev/core/buffer/forward-buffer.js.map +1 -0
  114. package/dist/dev/core/create-machine-actor.d.ts +12 -0
  115. package/dist/dev/core/create-machine-actor.d.ts.map +1 -0
  116. package/dist/dev/core/create-machine-actor.js +91 -0
  117. package/dist/dev/core/create-machine-actor.js.map +1 -0
  118. package/dist/dev/core/create-machine-reactor.d.ts +8 -0
  119. package/dist/dev/core/create-machine-reactor.d.ts.map +1 -0
  120. package/dist/dev/core/create-machine-reactor.js +83 -0
  121. package/dist/dev/core/create-machine-reactor.js.map +1 -0
  122. package/dist/dev/core/create-transition-actor.d.ts +23 -0
  123. package/dist/dev/core/create-transition-actor.d.ts.map +1 -0
  124. package/dist/dev/core/create-transition-actor.js +46 -0
  125. package/dist/dev/core/create-transition-actor.js.map +1 -0
  126. package/dist/dev/core/features/calculate-presentation-duration.js +53 -0
  127. package/dist/dev/core/features/calculate-presentation-duration.js.map +1 -0
  128. package/dist/dev/core/features/quality-switching.js +69 -0
  129. package/dist/dev/core/features/quality-switching.js.map +1 -0
  130. package/dist/dev/core/features/resolve-presentation.js +73 -0
  131. package/dist/dev/core/features/resolve-presentation.js.map +1 -0
  132. package/dist/dev/core/features/resolve-track.js +66 -0
  133. package/dist/dev/core/features/resolve-track.js.map +1 -0
  134. package/dist/dev/core/features/select-tracks.js +130 -0
  135. package/dist/dev/core/features/select-tracks.js.map +1 -0
  136. package/dist/dev/core/features/sync-preload-attribute.js +26 -0
  137. package/dist/dev/core/features/sync-preload-attribute.js.map +1 -0
  138. package/dist/dev/core/hls/parse-attributes.js +134 -0
  139. package/dist/dev/core/hls/parse-attributes.js.map +1 -0
  140. package/dist/dev/core/hls/parse-media-playlist.js +82 -0
  141. package/dist/dev/core/hls/parse-media-playlist.js.map +1 -0
  142. package/dist/dev/core/hls/parse-multivariant.js +214 -0
  143. package/dist/dev/core/hls/parse-multivariant.js.map +1 -0
  144. package/dist/dev/core/hls/resolve-url.js +11 -0
  145. package/dist/dev/core/hls/resolve-url.js.map +1 -0
  146. package/dist/dev/core/machine.d.ts +21 -0
  147. package/dist/dev/core/machine.d.ts.map +1 -0
  148. package/dist/dev/core/machine.js +26 -0
  149. package/dist/dev/core/machine.js.map +1 -0
  150. package/dist/dev/core/signals/effect.d.ts +15 -0
  151. package/dist/dev/core/signals/effect.d.ts.map +1 -0
  152. package/dist/dev/core/signals/effect.js +41 -0
  153. package/dist/dev/core/signals/effect.js.map +1 -0
  154. package/dist/dev/core/signals/primitives.d.ts +10 -0
  155. package/dist/dev/core/signals/primitives.d.ts.map +1 -0
  156. package/dist/dev/core/signals/primitives.js +32 -0
  157. package/dist/dev/core/signals/primitives.js.map +1 -0
  158. package/dist/dev/core/task.js +191 -0
  159. package/dist/dev/core/task.js.map +1 -0
  160. package/dist/dev/core/types/index.d.ts +166 -0
  161. package/dist/dev/core/types/index.d.ts.map +1 -0
  162. package/dist/dev/core/types/index.js +15 -0
  163. package/dist/dev/core/types/index.js.map +1 -0
  164. package/dist/dev/core/utils/generate-id.js +21 -0
  165. package/dist/dev/core/utils/generate-id.js.map +1 -0
  166. package/dist/dev/core/utils/track-selection.d.ts +16 -0
  167. package/dist/dev/core/utils/track-selection.d.ts.map +1 -0
  168. package/dist/dev/core/utils/track-selection.js +39 -0
  169. package/dist/dev/core/utils/track-selection.js.map +1 -0
  170. package/dist/dev/dom/features/end-of-stream.js +145 -0
  171. package/dist/dev/dom/features/end-of-stream.js.map +1 -0
  172. package/dist/dev/dom/features/load-segments.d.ts +72 -0
  173. package/dist/dev/dom/features/load-segments.d.ts.map +1 -0
  174. package/dist/dev/dom/features/load-segments.js +192 -0
  175. package/dist/dev/dom/features/load-segments.js.map +1 -0
  176. package/dist/dev/dom/features/load-text-track-cues.d.ts +85 -0
  177. package/dist/dev/dom/features/load-text-track-cues.d.ts.map +1 -0
  178. package/dist/dev/dom/features/load-text-track-cues.js +104 -0
  179. package/dist/dev/dom/features/load-text-track-cues.js.map +1 -0
  180. package/dist/dev/dom/features/segment-loader-actor.d.ts +1 -0
  181. package/dist/dev/dom/features/segment-loader-actor.js +243 -0
  182. package/dist/dev/dom/features/segment-loader-actor.js.map +1 -0
  183. package/dist/dev/dom/features/setup-mediasource.js +48 -0
  184. package/dist/dev/dom/features/setup-mediasource.js.map +1 -0
  185. package/dist/dev/dom/features/setup-sourcebuffer.d.ts +9 -0
  186. package/dist/dev/dom/features/setup-sourcebuffer.d.ts.map +1 -0
  187. package/dist/dev/dom/features/setup-sourcebuffer.js +78 -0
  188. package/dist/dev/dom/features/setup-sourcebuffer.js.map +1 -0
  189. package/dist/dev/dom/features/sync-text-tracks.js +95 -0
  190. package/dist/dev/dom/features/sync-text-tracks.js.map +1 -0
  191. package/dist/dev/dom/features/text-track-segment-loader-actor.d.ts +12 -0
  192. package/dist/dev/dom/features/text-track-segment-loader-actor.d.ts.map +1 -0
  193. package/dist/dev/dom/features/text-track-segment-loader-actor.js +55 -0
  194. package/dist/dev/dom/features/text-track-segment-loader-actor.js.map +1 -0
  195. package/dist/dev/dom/features/text-tracks-actor.d.ts +31 -0
  196. package/dist/dev/dom/features/text-tracks-actor.d.ts.map +1 -0
  197. package/dist/dev/dom/features/text-tracks-actor.js +42 -0
  198. package/dist/dev/dom/features/text-tracks-actor.js.map +1 -0
  199. package/dist/dev/dom/features/track-current-time.d.ts +40 -0
  200. package/dist/dev/dom/features/track-current-time.d.ts.map +1 -0
  201. package/dist/dev/dom/features/track-current-time.js +49 -0
  202. package/dist/dev/dom/features/track-current-time.js.map +1 -0
  203. package/dist/dev/dom/features/track-playback-initiated.d.ts +46 -0
  204. package/dist/dev/dom/features/track-playback-initiated.d.ts.map +1 -0
  205. package/dist/dev/dom/features/track-playback-initiated.js +66 -0
  206. package/dist/dev/dom/features/track-playback-initiated.js.map +1 -0
  207. package/dist/dev/dom/features/track-playback-rate.d.ts +34 -0
  208. package/dist/dev/dom/features/track-playback-rate.d.ts.map +1 -0
  209. package/dist/dev/dom/features/track-playback-rate.js +32 -0
  210. package/dist/dev/dom/features/track-playback-rate.js.map +1 -0
  211. package/dist/dev/dom/features/update-duration.js +78 -0
  212. package/dist/dev/dom/features/update-duration.js.map +1 -0
  213. package/dist/dev/dom/media/append-segment.d.ts +24 -0
  214. package/dist/dev/dom/media/append-segment.d.ts.map +1 -0
  215. package/dist/dev/dom/media/append-segment.js +60 -0
  216. package/dist/dev/dom/media/append-segment.js.map +1 -0
  217. package/dist/dev/dom/media/buffer-flusher.d.ts +24 -0
  218. package/dist/dev/dom/media/buffer-flusher.d.ts.map +1 -0
  219. package/dist/dev/dom/media/buffer-flusher.js +55 -0
  220. package/dist/dev/dom/media/buffer-flusher.js.map +1 -0
  221. package/dist/dev/dom/media/mediasource-setup.js +146 -0
  222. package/dist/dev/dom/media/mediasource-setup.js.map +1 -0
  223. package/dist/dev/dom/media/source-buffer-actor.d.ts +62 -0
  224. package/dist/dev/dom/media/source-buffer-actor.d.ts.map +1 -0
  225. package/dist/dev/dom/media/source-buffer-actor.js +137 -0
  226. package/dist/dev/dom/media/source-buffer-actor.js.map +1 -0
  227. package/dist/dev/dom/network/chunked-stream-iterable.js +49 -0
  228. package/dist/dev/dom/network/chunked-stream-iterable.js.map +1 -0
  229. package/dist/dev/dom/network/fetch.js +54 -0
  230. package/dist/dev/dom/network/fetch.js.map +1 -0
  231. package/dist/dev/dom/playback-engine/adapter.d.ts +40 -0
  232. package/dist/dev/dom/playback-engine/adapter.d.ts.map +1 -0
  233. package/dist/dev/dom/playback-engine/adapter.js +100 -0
  234. package/dist/dev/dom/playback-engine/adapter.js.map +1 -0
  235. package/dist/dev/dom/playback-engine/engine.d.ts +124 -0
  236. package/dist/dev/dom/playback-engine/engine.d.ts.map +1 -0
  237. package/dist/dev/dom/playback-engine/engine.js +142 -0
  238. package/dist/dev/dom/playback-engine/engine.js.map +1 -0
  239. package/dist/dev/dom/playback-engine.d.ts +4 -0
  240. package/dist/dev/dom/playback-engine.js +4 -0
  241. package/dist/dev/dom/text/parse-vtt-segment.d.ts +12 -0
  242. package/dist/dev/dom/text/parse-vtt-segment.d.ts.map +1 -0
  243. package/dist/dev/dom/text/parse-vtt-segment.js +56 -0
  244. package/dist/dev/dom/text/parse-vtt-segment.js.map +1 -0
  245. package/dist/dev/dom.d.ts +10 -0
  246. package/dist/dev/dom.js +10 -0
  247. package/dist/dev/index.d.ts.map +1 -0
  248. package/dist/dev/index.js +14 -0
  249. package/dist/dev/index.js.map +1 -0
  250. package/package.json +26 -24
  251. package/dist/adapter-CMw-rvbk.js +0 -3512
  252. package/dist/adapter-CMw-rvbk.js.map +0 -1
  253. package/dist/adapter-l8iJN_GI.d.ts +0 -689
  254. package/dist/adapter-l8iJN_GI.d.ts.map +0 -1
  255. package/dist/dom/index.d.ts +0 -243
  256. package/dist/dom/index.d.ts.map +0 -1
  257. package/dist/dom/index.js +0 -38
  258. package/dist/dom/index.js.map +0 -1
  259. package/dist/dom/playback-engine/index.d.ts +0 -2
  260. package/dist/dom/playback-engine/index.js +0 -4
  261. package/dist/index.d.ts.map +0 -1
  262. package/dist/index.js +0 -3
  263. package/dist/src-DRsS_H80.js.map +0 -1
  264. /package/dist/{index.d.ts → dev/index.d.ts} +0 -0
@@ -0,0 +1,32 @@
1
+ import { Signal } from "signal-polyfill";
2
+ //#region src/core/signals/primitives.ts
3
+ /** Read a signal value without tracking it as a dependency. */
4
+ const untrack = Signal.subtle.untrack;
5
+ /** Create a writable reactive value. */
6
+ function signal(initialValue, options) {
7
+ return new Signal.State(initialValue, options);
8
+ }
9
+ /** Create a computed reactive value. */
10
+ function computed(fn, options) {
11
+ return new Signal.Computed(fn, options);
12
+ }
13
+ /**
14
+ * Update a writable signal. Accepts either a partial object to merge into the
15
+ * current state, or an updater function that receives the current state and
16
+ * returns the next state.
17
+ *
18
+ * @example
19
+ * update(state, { playbackRate: 2 });
20
+ * update(state, (s) => ({ ...s, count: s.count + 1 }));
21
+ */
22
+ function update(signal, updater) {
23
+ const current = untrack(() => signal.get());
24
+ signal.set(typeof updater === "function" ? updater(current) : {
25
+ ...current,
26
+ ...updater
27
+ });
28
+ }
29
+ //#endregion
30
+ export { computed, signal, untrack, update };
31
+
32
+ //# sourceMappingURL=primitives.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"primitives.js","names":["SignalNS"],"sources":["../../../../src/core/signals/primitives.ts"],"sourcesContent":["import { Signal as SignalNS } from 'signal-polyfill';\n\n/** Read a signal value without tracking it as a dependency. */\nexport const untrack: <T>(fn: () => T) => T = SignalNS.subtle.untrack;\n\n/** A writable reactive value (read + write). */\nexport type Signal<T> = SignalNS.State<T>;\n\n/** A derived reactive value that re-evaluates when its dependencies change (read-only). */\nexport type Computed<T> = SignalNS.Computed<T>;\n\n/** A read-only view of a reactive value. */\nexport type ReadonlySignal<T> = Omit<SignalNS.State<T>, 'set'>;\n\nexport interface SignalOptions<T> {\n equals?: (t: T, t2: T) => boolean;\n}\n\n/** Create a writable reactive value. */\nexport function signal<T>(initialValue: T, options?: SignalOptions<T>): Signal<T> {\n return new SignalNS.State(initialValue, options as SignalNS.Options<T>);\n}\n\n/** Create a computed reactive value. */\nexport function computed<T>(fn: () => T, options?: SignalOptions<T>): Computed<T> {\n return new SignalNS.Computed(fn, options as SignalNS.Options<T>);\n}\n\n/**\n * Update a writable signal. Accepts either a partial object to merge into the\n * current state, or an updater function that receives the current state and\n * returns the next state.\n *\n * @example\n * update(state, { playbackRate: 2 });\n * update(state, (s) => ({ ...s, count: s.count + 1 }));\n */\nexport function update<T extends object>(signal: Signal<T>, updater: Partial<T> | ((current: T) => T)): void {\n const current = untrack(() => signal.get());\n signal.set(typeof updater === 'function' ? updater(current) : { ...current, ...updater });\n}\n"],"mappings":";;;AAGA,MAAa,UAAiCA,OAAS,OAAO;;AAgB9D,SAAgB,OAAU,cAAiB,SAAuC;AAChF,QAAO,IAAIA,OAAS,MAAM,cAAc,QAA+B;;;AAIzE,SAAgB,SAAY,IAAa,SAAyC;AAChF,QAAO,IAAIA,OAAS,SAAS,IAAI,QAA+B;;;;;;;;;;;AAYlE,SAAgB,OAAyB,QAAmB,SAAiD;CAC3G,MAAM,UAAU,cAAc,OAAO,KAAK,CAAC;AAC3C,QAAO,IAAI,OAAO,YAAY,aAAa,QAAQ,QAAQ,GAAG;EAAE,GAAG;EAAS,GAAG;EAAS,CAAC"}
@@ -0,0 +1,191 @@
1
+ import { generateId } from "./utils/generate-id.js";
2
+ import { anyAbortSignal } from "@videojs/utils/events";
3
+ //#region src/core/task.ts
4
+ /**
5
+ * Generic reusable task that wraps an async run function.
6
+ *
7
+ * Owns its own AbortController so it can always be aborted independently.
8
+ * Optionally composes an external AbortSignal so that a parent's cancellation
9
+ * propagates into the task's work without requiring the caller to track the
10
+ * task separately.
11
+ *
12
+ * Ordering guarantee: `value` is written before `status` transitions to `'done'`;
13
+ * `error` is written before `status` transitions to `'error'`. Any reader
14
+ * observing `status === 'done'` is guaranteed `value` is already present.
15
+ */
16
+ var Task = class {
17
+ id;
18
+ #runFn;
19
+ #abortController = new AbortController();
20
+ #signal;
21
+ #status = "pending";
22
+ #value = void 0;
23
+ #error = void 0;
24
+ constructor(runFn, config) {
25
+ this.#runFn = runFn;
26
+ const rawId = config?.id;
27
+ this.id = typeof rawId === "function" ? rawId() : rawId ?? generateId();
28
+ this.#signal = config?.signal ? anyAbortSignal([this.#abortController.signal, config.signal]) : this.#abortController.signal;
29
+ }
30
+ get status() {
31
+ return this.#status;
32
+ }
33
+ get value() {
34
+ return this.#value;
35
+ }
36
+ get error() {
37
+ return this.#error;
38
+ }
39
+ async run() {
40
+ this.#status = "running";
41
+ try {
42
+ const result = await this.#runFn(this.#signal);
43
+ this.#value = result;
44
+ this.#status = "done";
45
+ return result;
46
+ } catch (e) {
47
+ this.#error = e;
48
+ this.#status = "error";
49
+ throw e;
50
+ }
51
+ }
52
+ abort() {
53
+ this.#abortController.abort();
54
+ }
55
+ };
56
+ /**
57
+ * Runs tasks concurrently, deduplicated by task id.
58
+ *
59
+ * If a task with a given id is already in flight, subsequent schedule() calls
60
+ * for that id are silently ignored until the first completes. Tasks are stored
61
+ * so abortAll() can cancel any in-flight work (e.g. on engine cleanup).
62
+ */
63
+ var ConcurrentRunner = class {
64
+ #pending = /* @__PURE__ */ new Map();
65
+ #settled = Promise.resolve();
66
+ #resolveSettled = null;
67
+ #destroyed = false;
68
+ schedule(task) {
69
+ if (this.#destroyed) return Promise.resolve();
70
+ const existing = this.#pending.get(task.id);
71
+ if (existing) return existing.promise;
72
+ if (this.#pending.size === 0) this.#settled = new Promise((resolve) => {
73
+ this.#resolveSettled = resolve;
74
+ });
75
+ const promise = task.run();
76
+ promise.catch(() => {});
77
+ const cleanup = () => {
78
+ this.#pending.delete(task.id);
79
+ if (this.#pending.size === 0) {
80
+ this.#resolveSettled?.();
81
+ this.#resolveSettled = null;
82
+ }
83
+ };
84
+ promise.then(cleanup, cleanup);
85
+ this.#pending.set(task.id, {
86
+ task,
87
+ promise
88
+ });
89
+ return promise;
90
+ }
91
+ /**
92
+ * Registers a callback to fire when all currently in-flight tasks settle.
93
+ * If the runner is already idle, the callback is never called. If abortAll()
94
+ * is called before the batch settles, the callback is superseded and silently
95
+ * dropped — no stale callbacks, no generation token required by the caller.
96
+ */
97
+ whenSettled(callback) {
98
+ if (this.#pending.size === 0) return;
99
+ const captured = this.#settled;
100
+ captured.then(() => {
101
+ if (this.#settled !== captured) return;
102
+ callback();
103
+ }, () => {});
104
+ }
105
+ abortAll() {
106
+ for (const { task } of this.#pending.values()) task.abort();
107
+ this.#pending.clear();
108
+ this.#resolveSettled?.();
109
+ this.#resolveSettled = null;
110
+ this.#settled = Promise.resolve();
111
+ }
112
+ destroy() {
113
+ this.#destroyed = true;
114
+ this.abortAll();
115
+ }
116
+ };
117
+ /**
118
+ * Runs tasks one at a time in submission order.
119
+ *
120
+ * Each schedule() call returns a Promise that resolves or rejects with the
121
+ * task's result when it is eventually executed. Tasks wait in queue until the
122
+ * prior task completes.
123
+ *
124
+ * Serialization is achieved by chaining each task's run() onto the tail of a
125
+ * shared promise chain — no explicit queue or drain loop needed.
126
+ *
127
+ * abortAll() aborts all pending (not yet started) tasks and the currently
128
+ * in-flight task. Pending tasks still run briefly but receive an aborted
129
+ * signal and are expected to exit early.
130
+ */
131
+ var SerialRunner = class {
132
+ #chain = Promise.resolve();
133
+ #pending = /* @__PURE__ */ new Set();
134
+ #current = null;
135
+ #destroyed = false;
136
+ schedule(task) {
137
+ if (this.#destroyed) return Promise.resolve();
138
+ const t = task;
139
+ this.#pending.add(t);
140
+ const result = this.#chain.then(() => {
141
+ this.#pending.delete(t);
142
+ this.#current = t;
143
+ return task.run();
144
+ }).finally(() => {
145
+ this.#current = null;
146
+ });
147
+ this.#chain = result.then(() => {}, () => {});
148
+ return result;
149
+ }
150
+ /**
151
+ * A promise that resolves when all currently-scheduled tasks have settled.
152
+ * Use the reference as a generation token: capture it after scheduling a
153
+ * batch, then check identity in the resolution callback to detect whether
154
+ * a subsequent abortAll() + new batch has superseded this one.
155
+ */
156
+ get settled() {
157
+ return this.#chain;
158
+ }
159
+ /**
160
+ * Registers a callback to fire when all currently-pending tasks settle.
161
+ * If the runner is already idle (no pending or running tasks), the callback
162
+ * is never called. If new tasks are scheduled before the current batch
163
+ * settles, the callback is superseded and silently dropped — no stale
164
+ * callbacks, no generation token required by the caller.
165
+ */
166
+ whenSettled(callback) {
167
+ if (this.#pending.size === 0 && this.#current === null) return;
168
+ const currentChain = this.#chain;
169
+ currentChain.then(() => {
170
+ if (this.#chain !== currentChain) return;
171
+ callback();
172
+ }, () => {});
173
+ }
174
+ /** Aborts and clears queued tasks without touching the in-flight task. */
175
+ abortPending() {
176
+ for (const task of this.#pending) task.abort();
177
+ this.#pending.clear();
178
+ }
179
+ abortAll() {
180
+ this.abortPending();
181
+ this.#current?.abort();
182
+ }
183
+ destroy() {
184
+ this.#destroyed = true;
185
+ this.abortAll();
186
+ }
187
+ };
188
+ //#endregion
189
+ export { ConcurrentRunner, SerialRunner, Task };
190
+
191
+ //# sourceMappingURL=task.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task.js","names":["#runFn","#abortController","#signal","#status","#value","#error","#pending","#destroyed","#settled","#resolveSettled","#chain","#current"],"sources":["../../../src/core/task.ts"],"sourcesContent":["import { anyAbortSignal } from '@videojs/utils/events';\nimport { generateId } from './utils/generate-id';\n\n// =============================================================================\n// DeepReadonly\n// =============================================================================\n\n/** Recursively marks all properties as readonly. */\nexport type DeepReadonly<T> = T extends (infer U)[]\n ? ReadonlyArray<DeepReadonly<U>>\n : T extends object\n ? { readonly [K in keyof T]: DeepReadonly<T[K]> }\n : T;\n\n// =============================================================================\n// Task\n// =============================================================================\n\nexport type TaskStatus = 'pending' | 'running' | 'done' | 'error';\n\n/**\n * Configuration for a Task.\n */\nexport interface TaskConfig {\n /**\n * Identifier for this task.\n * - string: used as-is\n * - () => string: called once at construction time\n * - undefined: a unique ID is generated via generateId()\n */\n id?: string | (() => string);\n\n /**\n * Optional external AbortSignal to compose with the task's internal one.\n * The task's work is aborted when either the internal controller (via abort())\n * or this external signal fires — whichever comes first.\n */\n signal?: AbortSignal;\n}\n\n/**\n * Minimal contract for a schedulable unit of async work.\n */\nexport interface TaskLike<TValue = void, TError = unknown> {\n readonly id: string;\n readonly status: TaskStatus;\n readonly value: DeepReadonly<TValue> | undefined;\n readonly error: DeepReadonly<TError> | undefined;\n run(): Promise<TValue>;\n abort(): void;\n}\n\n/**\n * Generic reusable task that wraps an async run function.\n *\n * Owns its own AbortController so it can always be aborted independently.\n * Optionally composes an external AbortSignal so that a parent's cancellation\n * propagates into the task's work without requiring the caller to track the\n * task separately.\n *\n * Ordering guarantee: `value` is written before `status` transitions to `'done'`;\n * `error` is written before `status` transitions to `'error'`. Any reader\n * observing `status === 'done'` is guaranteed `value` is already present.\n */\nexport class Task<TValue = void, TError = unknown> implements TaskLike<TValue, TError> {\n readonly id: string;\n readonly #runFn: (signal: AbortSignal) => Promise<TValue>;\n readonly #abortController = new AbortController();\n readonly #signal: AbortSignal;\n\n #status: TaskStatus = 'pending';\n #value: TValue | undefined = undefined;\n #error: TError | undefined = undefined;\n\n constructor(runFn: (signal: AbortSignal) => Promise<TValue>, config?: TaskConfig) {\n this.#runFn = runFn;\n const rawId = config?.id;\n this.id = typeof rawId === 'function' ? rawId() : (rawId ?? generateId());\n this.#signal = config?.signal\n ? anyAbortSignal([this.#abortController.signal, config.signal])\n : this.#abortController.signal;\n }\n\n get status(): TaskStatus {\n return this.#status;\n }\n\n get value(): DeepReadonly<TValue> | undefined {\n return this.#value as DeepReadonly<TValue> | undefined;\n }\n\n get error(): DeepReadonly<TError> | undefined {\n return this.#error as DeepReadonly<TError> | undefined;\n }\n\n async run(): Promise<TValue> {\n this.#status = 'running';\n try {\n const result = await this.#runFn(this.#signal);\n this.#value = result; // value before status — ordering guarantee\n this.#status = 'done';\n return result;\n } catch (e) {\n this.#error = e as TError; // error before status — ordering guarantee\n this.#status = 'error';\n throw e;\n }\n }\n\n abort(): void {\n this.#abortController.abort();\n }\n}\n\n// =============================================================================\n// ConcurrentRunner\n// =============================================================================\n\n/**\n * Runs tasks concurrently, deduplicated by task id.\n *\n * If a task with a given id is already in flight, subsequent schedule() calls\n * for that id are silently ignored until the first completes. Tasks are stored\n * so abortAll() can cancel any in-flight work (e.g. on engine cleanup).\n */\nexport class ConcurrentRunner {\n readonly #pending = new Map<string, { task: TaskLike<unknown, unknown>; promise: Promise<unknown> }>();\n #settled: Promise<void> = Promise.resolve();\n #resolveSettled: (() => void) | null = null;\n #destroyed = false;\n\n schedule<TValue = void, TError = unknown>(task: TaskLike<TValue, TError>): Promise<TValue> {\n if (this.#destroyed) return Promise.resolve() as Promise<TValue>;\n const existing = this.#pending.get(task.id);\n if (existing) return existing.promise as Promise<TValue>;\n\n if (this.#pending.size === 0) {\n this.#settled = new Promise((resolve) => {\n this.#resolveSettled = resolve;\n });\n }\n\n const promise = task.run();\n // Suppress unhandled rejection for callers that ignore the return value.\n promise.catch(() => {});\n // Cleanup: update pending and resolve settled regardless of outcome.\n const cleanup = () => {\n this.#pending.delete(task.id);\n if (this.#pending.size === 0) {\n this.#resolveSettled?.();\n this.#resolveSettled = null;\n }\n };\n promise.then(cleanup, cleanup);\n\n this.#pending.set(task.id, { task: task as TaskLike<unknown, unknown>, promise: promise as Promise<unknown> });\n return promise;\n }\n\n /**\n * Registers a callback to fire when all currently in-flight tasks settle.\n * If the runner is already idle, the callback is never called. If abortAll()\n * is called before the batch settles, the callback is superseded and silently\n * dropped — no stale callbacks, no generation token required by the caller.\n */\n whenSettled(callback: () => void): void {\n if (this.#pending.size === 0) return;\n const captured = this.#settled;\n captured.then(\n () => {\n if (this.#settled !== captured) return;\n callback();\n },\n () => {}\n );\n }\n\n abortAll(): void {\n for (const { task } of this.#pending.values()) task.abort();\n this.#pending.clear();\n // Resolve the current settled promise so any .then() handlers are queued,\n // then replace the reference — whenSettled callbacks that captured the old\n // reference will see the identity mismatch and be dropped.\n this.#resolveSettled?.();\n this.#resolveSettled = null;\n this.#settled = Promise.resolve();\n }\n\n destroy(): void {\n this.#destroyed = true;\n this.abortAll();\n }\n}\n\n// =============================================================================\n// SerialRunner\n// =============================================================================\n\n/**\n * Runs tasks one at a time in submission order.\n *\n * Each schedule() call returns a Promise that resolves or rejects with the\n * task's result when it is eventually executed. Tasks wait in queue until the\n * prior task completes.\n *\n * Serialization is achieved by chaining each task's run() onto the tail of a\n * shared promise chain — no explicit queue or drain loop needed.\n *\n * abortAll() aborts all pending (not yet started) tasks and the currently\n * in-flight task. Pending tasks still run briefly but receive an aborted\n * signal and are expected to exit early.\n */\nexport class SerialRunner {\n #chain: Promise<unknown> = Promise.resolve();\n readonly #pending = new Set<TaskLike<unknown, unknown>>();\n #current: TaskLike<unknown, unknown> | null = null;\n #destroyed = false;\n\n schedule<TValue = void, TError = unknown>(task: TaskLike<TValue, TError>): Promise<TValue> {\n if (this.#destroyed) return Promise.resolve() as Promise<TValue>;\n const t = task as TaskLike<unknown, unknown>;\n this.#pending.add(t);\n\n const result = this.#chain\n .then(() => {\n this.#pending.delete(t);\n this.#current = t;\n return task.run();\n })\n .finally(() => {\n this.#current = null;\n });\n\n // Advance the chain regardless of whether this task succeeds or fails.\n this.#chain = result.then(\n () => {},\n () => {}\n );\n\n return result as Promise<TValue>;\n }\n\n /**\n * A promise that resolves when all currently-scheduled tasks have settled.\n * Use the reference as a generation token: capture it after scheduling a\n * batch, then check identity in the resolution callback to detect whether\n * a subsequent abortAll() + new batch has superseded this one.\n */\n get settled(): Promise<void> {\n return this.#chain as Promise<void>;\n }\n\n /**\n * Registers a callback to fire when all currently-pending tasks settle.\n * If the runner is already idle (no pending or running tasks), the callback\n * is never called. If new tasks are scheduled before the current batch\n * settles, the callback is superseded and silently dropped — no stale\n * callbacks, no generation token required by the caller.\n */\n whenSettled(callback: () => void): void {\n if (this.#pending.size === 0 && this.#current === null) return;\n const currentChain = this.#chain;\n currentChain.then(\n () => {\n if (this.#chain !== currentChain) return;\n callback();\n },\n () => {}\n );\n }\n\n /** Aborts and clears queued tasks without touching the in-flight task. */\n abortPending(): void {\n for (const task of this.#pending) task.abort();\n this.#pending.clear();\n }\n\n abortAll(): void {\n this.abortPending();\n this.#current?.abort();\n }\n\n destroy(): void {\n this.#destroyed = true;\n this.abortAll();\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;AAgEA,IAAa,OAAb,MAAuF;CACrF;CACA;CACA,mBAA4B,IAAI,iBAAiB;CACjD;CAEA,UAAsB;CACtB,SAA6B,KAAA;CAC7B,SAA6B,KAAA;CAE7B,YAAY,OAAiD,QAAqB;AAChF,QAAA,QAAc;EACd,MAAM,QAAQ,QAAQ;AACtB,OAAK,KAAK,OAAO,UAAU,aAAa,OAAO,GAAI,SAAS,YAAY;AACxE,QAAA,SAAe,QAAQ,SACnB,eAAe,CAAC,MAAA,gBAAsB,QAAQ,OAAO,OAAO,CAAC,GAC7D,MAAA,gBAAsB;;CAG5B,IAAI,SAAqB;AACvB,SAAO,MAAA;;CAGT,IAAI,QAA0C;AAC5C,SAAO,MAAA;;CAGT,IAAI,QAA0C;AAC5C,SAAO,MAAA;;CAGT,MAAM,MAAuB;AAC3B,QAAA,SAAe;AACf,MAAI;GACF,MAAM,SAAS,MAAM,MAAA,MAAY,MAAA,OAAa;AAC9C,SAAA,QAAc;AACd,SAAA,SAAe;AACf,UAAO;WACA,GAAG;AACV,SAAA,QAAc;AACd,SAAA,SAAe;AACf,SAAM;;;CAIV,QAAc;AACZ,QAAA,gBAAsB,OAAO;;;;;;;;;;AAejC,IAAa,mBAAb,MAA8B;CAC5B,2BAAoB,IAAI,KAA8E;CACtG,WAA0B,QAAQ,SAAS;CAC3C,kBAAuC;CACvC,aAAa;CAEb,SAA0C,MAAiD;AACzF,MAAI,MAAA,UAAiB,QAAO,QAAQ,SAAS;EAC7C,MAAM,WAAW,MAAA,QAAc,IAAI,KAAK,GAAG;AAC3C,MAAI,SAAU,QAAO,SAAS;AAE9B,MAAI,MAAA,QAAc,SAAS,EACzB,OAAA,UAAgB,IAAI,SAAS,YAAY;AACvC,SAAA,iBAAuB;IACvB;EAGJ,MAAM,UAAU,KAAK,KAAK;AAE1B,UAAQ,YAAY,GAAG;EAEvB,MAAM,gBAAgB;AACpB,SAAA,QAAc,OAAO,KAAK,GAAG;AAC7B,OAAI,MAAA,QAAc,SAAS,GAAG;AAC5B,UAAA,kBAAwB;AACxB,UAAA,iBAAuB;;;AAG3B,UAAQ,KAAK,SAAS,QAAQ;AAE9B,QAAA,QAAc,IAAI,KAAK,IAAI;GAAQ;GAA6C;GAA6B,CAAC;AAC9G,SAAO;;;;;;;;CAST,YAAY,UAA4B;AACtC,MAAI,MAAA,QAAc,SAAS,EAAG;EAC9B,MAAM,WAAW,MAAA;AACjB,WAAS,WACD;AACJ,OAAI,MAAA,YAAkB,SAAU;AAChC,aAAU;WAEN,GACP;;CAGH,WAAiB;AACf,OAAK,MAAM,EAAE,UAAU,MAAA,QAAc,QAAQ,CAAE,MAAK,OAAO;AAC3D,QAAA,QAAc,OAAO;AAIrB,QAAA,kBAAwB;AACxB,QAAA,iBAAuB;AACvB,QAAA,UAAgB,QAAQ,SAAS;;CAGnC,UAAgB;AACd,QAAA,YAAkB;AAClB,OAAK,UAAU;;;;;;;;;;;;;;;;;AAsBnB,IAAa,eAAb,MAA0B;CACxB,SAA2B,QAAQ,SAAS;CAC5C,2BAAoB,IAAI,KAAiC;CACzD,WAA8C;CAC9C,aAAa;CAEb,SAA0C,MAAiD;AACzF,MAAI,MAAA,UAAiB,QAAO,QAAQ,SAAS;EAC7C,MAAM,IAAI;AACV,QAAA,QAAc,IAAI,EAAE;EAEpB,MAAM,SAAS,MAAA,MACZ,WAAW;AACV,SAAA,QAAc,OAAO,EAAE;AACvB,SAAA,UAAgB;AAChB,UAAO,KAAK,KAAK;IACjB,CACD,cAAc;AACb,SAAA,UAAgB;IAChB;AAGJ,QAAA,QAAc,OAAO,WACb,UACA,GACP;AAED,SAAO;;;;;;;;CAST,IAAI,UAAyB;AAC3B,SAAO,MAAA;;;;;;;;;CAUT,YAAY,UAA4B;AACtC,MAAI,MAAA,QAAc,SAAS,KAAK,MAAA,YAAkB,KAAM;EACxD,MAAM,eAAe,MAAA;AACrB,eAAa,WACL;AACJ,OAAI,MAAA,UAAgB,aAAc;AAClC,aAAU;WAEN,GACP;;;CAIH,eAAqB;AACnB,OAAK,MAAM,QAAQ,MAAA,QAAe,MAAK,OAAO;AAC9C,QAAA,QAAc,OAAO;;CAGvB,WAAiB;AACf,OAAK,cAAc;AACnB,QAAA,SAAe,OAAO;;CAGxB,UAAgB;AACd,QAAA,YAAkB;AAClB,OAAK,UAAU"}
@@ -0,0 +1,166 @@
1
+ //#region src/core/types/index.d.ts
2
+ /**
3
+ * Core SPF Types
4
+ *
5
+ * Based on CMAF-HAM (Common Media Application Format - Hypothetical Application Model)
6
+ * Protocol-agnostic representation of streaming media content.
7
+ *
8
+ * @see https://github.com/AcademySoftwareFoundation/common-media-library
9
+ */
10
+ /**
11
+ * Base identifier type for all HAM objects.
12
+ */
13
+ interface Ham {
14
+ id: string;
15
+ }
16
+ /**
17
+ * Addressable resource with optional byte range.
18
+ */
19
+ interface AddressableObject {
20
+ url: string;
21
+ byteRange?: {
22
+ start: number;
23
+ end: number;
24
+ };
25
+ }
26
+ /**
27
+ * Time span with start time and duration.
28
+ * Used for segments and other timed ranges.
29
+ */
30
+ interface TimeSpan {
31
+ startTime: number;
32
+ duration: number;
33
+ }
34
+ /**
35
+ * Track content type.
36
+ */
37
+ type TrackType = 'video' | 'audio' | 'text';
38
+ /**
39
+ * Video frame rate expressed as numerator/denominator.
40
+ *
41
+ * Examples:
42
+ * - 30 fps: { frameRateNumerator: 30 }
43
+ * - 29.97 fps: { frameRateNumerator: 30000, frameRateDenominator: 1001 }
44
+ */
45
+ interface FrameRate {
46
+ frameRateNumerator: number;
47
+ frameRateDenominator?: number;
48
+ }
49
+ /**
50
+ * Generic type for partially resolved tracks.
51
+ * Removes fields that come from media playlist parsing.
52
+ *
53
+ * @param T - Track type to make partially resolved (must extend Track)
54
+ */
55
+ type PartiallyResolved<T extends Track = Track> = Omit<T, 'segments' | 'initialization' | keyof TimeSpan> & {
56
+ segments?: never;
57
+ duration?: never;
58
+ startTime?: never;
59
+ initialization?: never;
60
+ };
61
+ /**
62
+ * Base track type containing common properties for all resolved tracks.
63
+ * A resolved track has segments, duration, and initialization data.
64
+ * All URLs are fully qualified (parsers resolve relative URLs).
65
+ */
66
+ /**
67
+ * Track startTime is always 0 (for future multi-period support).
68
+ */
69
+ type Track = Ham & AddressableObject & TimeSpan & {
70
+ type: TrackType;
71
+ codecs?: string[];
72
+ mimeType: string;
73
+ language?: string | undefined;
74
+ bandwidth: number;
75
+ initialization?: AddressableObject;
76
+ segments: Segment[];
77
+ };
78
+ /**
79
+ * Resolved video track with segments.
80
+ */
81
+ type VideoTrack = Track & Required<Pick<Track, 'initialization' | 'codecs'>> & {
82
+ type: 'video';
83
+ width?: number;
84
+ height?: number;
85
+ frameRate?: FrameRate;
86
+ audioGroupId?: string;
87
+ };
88
+ /**
89
+ * Resolved audio track with segments.
90
+ */
91
+ type AudioTrack = Track & Required<Pick<Track, 'initialization' | 'codecs'>> & {
92
+ type: 'audio';
93
+ groupId: string;
94
+ name: string;
95
+ sampleRate: number;
96
+ channels: number;
97
+ default?: boolean;
98
+ autoselect?: boolean;
99
+ };
100
+ /**
101
+ * Resolved text track with segments.
102
+ */
103
+ type TextTrack = Track & {
104
+ type: 'text';
105
+ groupId: string;
106
+ label: string;
107
+ kind: 'subtitles' | 'captions';
108
+ default?: boolean;
109
+ autoselect?: boolean;
110
+ forced?: boolean;
111
+ };
112
+ /**
113
+ * Generic switching set type.
114
+ * A group of tracks that can be switched between seamlessly.
115
+ *
116
+ * @param T - Track type (VideoTrack, AudioTrack, or TextTrack)
117
+ */
118
+ type SwitchingSetOf<T extends Track = Track> = Ham & {
119
+ type: T['type'];
120
+ tracks: (PartiallyResolved<T> | T)[];
121
+ };
122
+ /**
123
+ * Generic selection set type.
124
+ * Groups switching sets by track type.
125
+ *
126
+ * @param T - Track type (VideoTrack, AudioTrack, or TextTrack)
127
+ */
128
+ type SelectionSetOf<T extends Track = Track> = Ham & {
129
+ type: T['type'];
130
+ switchingSets: SwitchingSetOf<T>[];
131
+ };
132
+ /**
133
+ * Video selection set - contains only video switching sets.
134
+ */
135
+ type VideoSelectionSet = SelectionSetOf<VideoTrack>;
136
+ /**
137
+ * Audio selection set - contains only audio switching sets.
138
+ */
139
+ type AudioSelectionSet = SelectionSetOf<AudioTrack>;
140
+ /**
141
+ * Text selection set - contains only text switching sets.
142
+ */
143
+ type TextSelectionSet = SelectionSetOf<TextTrack>;
144
+ /**
145
+ * Selection set - groups switching sets by track type.
146
+ * Discriminated union ensures type-safe track access.
147
+ */
148
+ type SelectionSet = VideoSelectionSet | AudioSelectionSet | TextSelectionSet;
149
+ /**
150
+ * Media segment with timing information.
151
+ * Follows CMAF-HAM composition pattern.
152
+ */
153
+ type Segment = Ham & AddressableObject & TimeSpan;
154
+ /**
155
+ * Presentation - a single playable period of content.
156
+ * Uses TimeSpan fields (startTime always 0, duration optional until track resolved).
157
+ *
158
+ * Extends AddressableObject so `url` contains the original manifest URL.
159
+ * All URLs are fully qualified (parsers resolve relative URLs).
160
+ */
161
+ type Presentation = Ham & AddressableObject & Partial<TimeSpan> & {
162
+ selectionSets: SelectionSet[];
163
+ };
164
+ //#endregion
165
+ export { AddressableObject, Presentation, Segment, TextTrack, Track };
166
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../../../src/core/types/index.ts"],"mappings":";;AAgBA;;;;;AAOA;;;;;UAPiB,GAAA;EACf,EAAA;AAAA;;;AAmCF;UA7BiB,iBAAA;EACf,GAAA;EACA,SAAA;IACE,KAAA;IACA,GAAA;EAAA;AAAA;;;;;UAyBa,QAAA;EACf,SAAA;EACA,QAAA;AAAA;;;;KAUU,SAAA;;;;;;;;UAaK,SAAA;EACf,kBAAA;EACA,oBAAA;AAAA;;;;;;;KAaU,iBAAA,WAA4B,KAAA,GAAQ,KAAA,IAAS,IAAA,CAAK,CAAA,wCAAyC,QAAA;EACrG,QAAA;EACA,QAAA;EACA,SAAA;EACA,cAAA;AAAA;;;AA0CF;;;;;;KAfY,KAAA,GAAQ,GAAA,GAClB,iBAAA,GACA,QAAA;EACE,IAAA,EAAM,SAAA;EACN,MAAA;EACA,QAAA;EACA,QAAA;EACA,SAAA;EACA,cAAA,GAAiB,iBAAA;EACjB,QAAA,EAAU,OAAA;AAAA;;;;KAMF,UAAA,GAAa,KAAA,GACvB,QAAA,CAAS,IAAA,CAAK,KAAA;EACZ,IAAA;EAGA,KAAA;EACA,MAAA;EACA,SAAA,GAAY,SAAA;EACZ,YAAA;AAAA;;;;KAMQ,UAAA,GAAa,KAAA,GACvB,QAAA,CAAS,IAAA,CAAK,KAAA;EACZ,IAAA;EACA,OAAA;EACA,IAAA;EACA,UAAA;EACA,QAAA;EACA,OAAA;EACA,UAAA;AAAA;;;;KAMQ,SAAA,GAAY,KAAA;EACtB,IAAA;EACA,OAAA;EACA,KAAA;EACA,IAAA;EACA,OAAA;EACA,UAAA;EACA,MAAA;AAAA;;;;;;;KAgCU,cAAA,WAAyB,KAAA,GAAQ,KAAA,IAAS,GAAA;EACpD,IAAA,EAAM,CAAA;EACN,MAAA,GAAS,iBAAA,CAAkB,CAAA,IAAK,CAAA;AAAA;;;;;;;KA8BtB,cAAA,WAAyB,KAAA,GAAQ,KAAA,IAAS,GAAA;EACpD,IAAA,EAAM,CAAA;EACN,aAAA,EAAe,cAAA,CAAe,CAAA;AAAA;;;;KAMpB,iBAAA,GAAoB,cAAA,CAAe,UAAA;;AAA/C;;KAKY,iBAAA,GAAoB,cAAA,CAAe,UAAA;;;AAA/C;KAKY,gBAAA,GAAmB,cAAA,CAAe,SAAA;;;;AAA9C;KAMY,YAAA,GAAe,iBAAA,GAAoB,iBAAA,GAAoB,gBAAA;;;;AAAnE;KAUY,OAAA,GAAU,GAAA,GAAM,iBAAA,GAAoB,QAAA;;;;;;;;KA+BpC,YAAA,GAAe,GAAA,GACzB,iBAAA,GACA,OAAA,CAAQ,QAAA;EACN,aAAA,EAAe,YAAA;AAAA"}
@@ -0,0 +1,15 @@
1
+ //#region src/core/types/index.ts
2
+ function isResolvedTrack(track) {
3
+ return "segments" in track;
4
+ }
5
+ /**
6
+ * Check if a presentation has duration (at least one track resolved).
7
+ * Narrows type to include required duration.
8
+ */
9
+ function hasPresentationDuration(presentation) {
10
+ return presentation.duration !== void 0;
11
+ }
12
+ //#endregion
13
+ export { hasPresentationDuration, isResolvedTrack };
14
+
15
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../../src/core/types/index.ts"],"sourcesContent":["/**\n * Core SPF Types\n *\n * Based on CMAF-HAM (Common Media Application Format - Hypothetical Application Model)\n * Protocol-agnostic representation of streaming media content.\n *\n * @see https://github.com/AcademySoftwareFoundation/common-media-library\n */\n\n// =============================================================================\n// Base Types\n// =============================================================================\n\n/**\n * Base identifier type for all HAM objects.\n */\nexport interface Ham {\n id: string;\n}\n\n/**\n * Addressable resource with optional byte range.\n */\nexport interface AddressableObject {\n url: string;\n byteRange?: {\n start: number;\n end: number;\n };\n}\n\n// =============================================================================\n// Platform-agnostic Media Element\n// =============================================================================\n\n/**\n * Platform-agnostic media element interface.\n * Captures minimal shape needed for orchestration without DOM dependencies.\n * HTMLMediaElement satisfies this interface.\n */\nexport interface MediaElementLike {\n preload: string;\n}\n\n// =============================================================================\n// Time and Duration\n// =============================================================================\n\n/**\n * Time span with start time and duration.\n * Used for segments and other timed ranges.\n */\nexport interface TimeSpan {\n startTime: number;\n duration: number;\n}\n\n// =============================================================================\n// Enums\n// =============================================================================\n\n/**\n * Track content type.\n */\nexport type TrackType = 'video' | 'audio' | 'text';\n\n// =============================================================================\n// Frame Rate\n// =============================================================================\n\n/**\n * Video frame rate expressed as numerator/denominator.\n *\n * Examples:\n * - 30 fps: { frameRateNumerator: 30 }\n * - 29.97 fps: { frameRateNumerator: 30000, frameRateDenominator: 1001 }\n */\nexport interface FrameRate {\n frameRateNumerator: number;\n frameRateDenominator?: number;\n}\n\n// =============================================================================\n// Partially Resolved Tracks (before media playlist is fetched)\n// =============================================================================\n\n/**\n * Generic type for partially resolved tracks.\n * Removes fields that come from media playlist parsing.\n *\n * @param T - Track type to make partially resolved (must extend Track)\n */\nexport type PartiallyResolved<T extends Track = Track> = Omit<T, 'segments' | 'initialization' | keyof TimeSpan> & {\n segments?: never;\n duration?: never;\n startTime?: never;\n initialization?: never;\n};\n\n/**\n * Partially resolved video track from multivariant playlist.\n * Has metadata but no segments or initialization yet (media playlist not fetched).\n */\nexport type PartiallyResolvedVideoTrack = PartiallyResolved<VideoTrack>;\n\n/**\n * Partially resolved audio track from multivariant playlist.\n * Has metadata but no segments or initialization yet (media playlist not fetched).\n */\nexport type PartiallyResolvedAudioTrack = PartiallyResolved<AudioTrack>;\n\n// =============================================================================\n// Resolved Track Types (with segments from media playlist)\n// =============================================================================\n\n/**\n * Base track type containing common properties for all resolved tracks.\n * A resolved track has segments, duration, and initialization data.\n * All URLs are fully qualified (parsers resolve relative URLs).\n */\n/**\n * Track startTime is always 0 (for future multi-period support).\n */\nexport type Track = Ham &\n AddressableObject &\n TimeSpan & {\n type: TrackType;\n codecs?: string[]; // Optional per HLS spec\n mimeType: string;\n language?: string | undefined;\n bandwidth: number;\n initialization?: AddressableObject;\n segments: Segment[];\n };\n\n/**\n * Resolved video track with segments.\n */\nexport type VideoTrack = Track &\n Required<Pick<Track, 'initialization' | 'codecs'>> & {\n type: 'video';\n\n // Optional metadata from multivariant (per HLS spec)\n width?: number;\n height?: number;\n frameRate?: FrameRate;\n audioGroupId?: string;\n };\n\n/**\n * Resolved audio track with segments.\n */\nexport type AudioTrack = Track &\n Required<Pick<Track, 'initialization' | 'codecs'>> & {\n type: 'audio';\n groupId: string;\n name: string;\n sampleRate: number;\n channels: number;\n default?: boolean;\n autoselect?: boolean;\n };\n\n/**\n * Resolved text track with segments.\n */\nexport type TextTrack = Track & {\n type: 'text';\n groupId: string;\n label: string;\n kind: 'subtitles' | 'captions';\n default?: boolean;\n autoselect?: boolean;\n forced?: boolean;\n};\n\n/**\n * Partially resolved text track from multivariant playlist.\n * Has metadata but no segments or initialization yet (media playlist not fetched).\n */\nexport type PartiallyResolvedTextTrack = PartiallyResolved<TextTrack>;\n\n/**\n * Union of all resolved track types.\n */\nexport type ResolvedTrack = VideoTrack | AudioTrack | TextTrack;\n\n/**\n * Union of all partially resolved track types.\n */\nexport type PartiallyResolvedTrack =\n | PartiallyResolvedVideoTrack\n | PartiallyResolvedAudioTrack\n | PartiallyResolvedTextTrack;\n\n// =============================================================================\n// Switching and Selection Sets\n// =============================================================================\n\n/**\n * Generic switching set type.\n * A group of tracks that can be switched between seamlessly.\n *\n * @param T - Track type (VideoTrack, AudioTrack, or TextTrack)\n */\nexport type SwitchingSetOf<T extends Track = Track> = Ham & {\n type: T['type'];\n tracks: (PartiallyResolved<T> | T)[];\n};\n\n/**\n * Video switching set - contains only video tracks (partially resolved or fully resolved).\n */\nexport type VideoSwitchingSet = SwitchingSetOf<VideoTrack>;\n\n/**\n * Audio switching set - contains only audio tracks (partially resolved or fully resolved).\n */\nexport type AudioSwitchingSet = SwitchingSetOf<AudioTrack>;\n\n/**\n * Text switching set - contains only text tracks (partially resolved or fully resolved).\n */\nexport type TextSwitchingSet = SwitchingSetOf<TextTrack>;\n\n/**\n * Switching set - a group of tracks that can be switched between seamlessly.\n * Discriminated by track type.\n */\nexport type SwitchingSet = VideoSwitchingSet | AudioSwitchingSet | TextSwitchingSet;\n\n/**\n * Generic selection set type.\n * Groups switching sets by track type.\n *\n * @param T - Track type (VideoTrack, AudioTrack, or TextTrack)\n */\nexport type SelectionSetOf<T extends Track = Track> = Ham & {\n type: T['type'];\n switchingSets: SwitchingSetOf<T>[];\n};\n\n/**\n * Video selection set - contains only video switching sets.\n */\nexport type VideoSelectionSet = SelectionSetOf<VideoTrack>;\n\n/**\n * Audio selection set - contains only audio switching sets.\n */\nexport type AudioSelectionSet = SelectionSetOf<AudioTrack>;\n\n/**\n * Text selection set - contains only text switching sets.\n */\nexport type TextSelectionSet = SelectionSetOf<TextTrack>;\n\n/**\n * Selection set - groups switching sets by track type.\n * Discriminated union ensures type-safe track access.\n */\nexport type SelectionSet = VideoSelectionSet | AudioSelectionSet | TextSelectionSet;\n\n// =============================================================================\n// Segment\n// =============================================================================\n\n/**\n * Media segment with timing information.\n * Follows CMAF-HAM composition pattern.\n */\nexport type Segment = Ham & AddressableObject & TimeSpan;\n\n// =============================================================================\n// Media Playlist Info\n// =============================================================================\n\n/**\n * Intermediate representation of a parsed media playlist.\n * Used internally before assembling into full Track structure.\n */\nexport interface MediaPlaylistInfo {\n version: number;\n targetDuration: number;\n playlistType: 'VOD' | 'EVENT' | undefined;\n initSegment: AddressableObject | null;\n segments: Segment[];\n duration: number;\n endList: boolean;\n}\n\n// =============================================================================\n// Presentation\n// =============================================================================\n\n/**\n * Presentation - a single playable period of content.\n * Uses TimeSpan fields (startTime always 0, duration optional until track resolved).\n *\n * Extends AddressableObject so `url` contains the original manifest URL.\n * All URLs are fully qualified (parsers resolve relative URLs).\n */\nexport type Presentation = Ham &\n AddressableObject &\n Partial<TimeSpan> & {\n selectionSets: SelectionSet[];\n };\n\n// =============================================================================\n// Type Guards\n// =============================================================================\n\n/**\n * Check if a track is resolved (has segments).\n * Works for all track types with overloaded signatures for type narrowing.\n */\nexport function isResolvedTrack(track: PartiallyResolvedVideoTrack | VideoTrack): track is VideoTrack;\nexport function isResolvedTrack(track: PartiallyResolvedAudioTrack | AudioTrack): track is AudioTrack;\nexport function isResolvedTrack(track: PartiallyResolvedTextTrack | TextTrack): track is TextTrack;\nexport function isResolvedTrack(track: PartiallyResolvedTrack | ResolvedTrack): track is ResolvedTrack;\nexport function isResolvedTrack(track: PartiallyResolvedTrack | ResolvedTrack): track is ResolvedTrack {\n return 'segments' in track;\n}\n\n/**\n * Check if a presentation has duration (at least one track resolved).\n * Narrows type to include required duration.\n */\nexport function hasPresentationDuration(\n presentation: Presentation\n): presentation is Presentation & { duration: number } {\n return presentation.duration !== undefined;\n}\n"],"mappings":";AAgUA,SAAgB,gBAAgB,OAAuE;AACrG,QAAO,cAAc;;;;;;AAOvB,SAAgB,wBACd,cACqD;AACrD,QAAO,aAAa,aAAa,KAAA"}
@@ -0,0 +1,21 @@
1
+ //#region src/core/utils/generate-id.ts
2
+ /**
3
+ * Generate unique ID for HAM objects.
4
+ *
5
+ * Uses timestamp + random number for sufficient uniqueness.
6
+ * IDs are strings without decimals.
7
+ *
8
+ * @returns Unique string ID in format: timestamp-random
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * const id = generateId(); // "1738423156789-542891"
13
+ * ```
14
+ */
15
+ function generateId() {
16
+ return `${Date.now()}-${Math.floor(Math.random() * 1e6)}`;
17
+ }
18
+ //#endregion
19
+ export { generateId };
20
+
21
+ //# sourceMappingURL=generate-id.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-id.js","names":[],"sources":["../../../../src/core/utils/generate-id.ts"],"sourcesContent":["/**\n * Generate unique ID for HAM objects.\n *\n * Uses timestamp + random number for sufficient uniqueness.\n * IDs are strings without decimals.\n *\n * @returns Unique string ID in format: timestamp-random\n *\n * @example\n * ```ts\n * const id = generateId(); // \"1738423156789-542891\"\n * ```\n */\nexport function generateId(): string {\n return `${Date.now()}-${Math.floor(Math.random() * 1000000)}`;\n}\n"],"mappings":";;;;;;;;;;;;;;AAaA,SAAgB,aAAqB;AACnC,QAAO,GAAG,KAAK,KAAK,CAAC,GAAG,KAAK,MAAM,KAAK,QAAQ,GAAG,IAAQ"}
@@ -0,0 +1,16 @@
1
+ import { AddressableObject, Presentation } from "../types/index.js";
2
+
3
+ //#region src/core/utils/track-selection.d.ts
4
+ /**
5
+ * State shape for track selection.
6
+ * Minimal shape containing presentation and selected track IDs.
7
+ */
8
+ interface TrackSelectionState {
9
+ presentation?: AddressableObject | Presentation | undefined;
10
+ selectedVideoTrackId?: string | undefined;
11
+ selectedAudioTrackId?: string | undefined;
12
+ selectedTextTrackId?: string | undefined;
13
+ }
14
+ //#endregion
15
+ export { TrackSelectionState };
16
+ //# sourceMappingURL=track-selection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"track-selection.d.ts","names":[],"sources":["../../../../src/core/utils/track-selection.ts"],"mappings":";;;;;AAgBA;;UAAiB,mBAAA;EACf,YAAA,GAAe,iBAAA,GAAoB,YAAA;EACnC,oBAAA;EACA,oBAAA;EACA,mBAAA;AAAA"}
@@ -0,0 +1,39 @@
1
+ //#region src/core/utils/track-selection.ts
2
+ /**
3
+ * Map track type to selected track ID property key in state.
4
+ */
5
+ const SelectedTrackIdKeyByType = {
6
+ video: "selectedVideoTrackId",
7
+ audio: "selectedAudioTrackId",
8
+ text: "selectedTextTrackId"
9
+ };
10
+ /**
11
+ * Map track type to buffer owner property key.
12
+ * Used for SourceBuffer references in owners.
13
+ */
14
+ const BufferKeyByType = {
15
+ video: "videoBuffer",
16
+ audio: "audioBuffer"
17
+ };
18
+ /**
19
+ * Get selected track from state by type.
20
+ * Returns properly typed track (partially or fully resolved) or undefined.
21
+ * Type parameter T is inferred from the type argument.
22
+ *
23
+ * @example
24
+ * const videoTrack = getSelectedTrack(state, 'video');
25
+ * if (videoTrack && isResolvedTrack(videoTrack)) {
26
+ * // videoTrack is VideoTrack
27
+ * }
28
+ */
29
+ function getSelectedTrack(state, type) {
30
+ const { presentation } = state;
31
+ /** @TODO Consider moving and reusing isUnresolved(presentation) (CJP) */
32
+ if (!presentation || !("id" in presentation)) return void 0;
33
+ const trackId = state[SelectedTrackIdKeyByType[type]];
34
+ return presentation.selectionSets.find(({ type: selectionSetType }) => selectionSetType === type)?.switchingSets[0]?.tracks.find(({ id }) => id === trackId);
35
+ }
36
+ //#endregion
37
+ export { BufferKeyByType, SelectedTrackIdKeyByType, getSelectedTrack };
38
+
39
+ //# sourceMappingURL=track-selection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"track-selection.js","names":[],"sources":["../../../../src/core/utils/track-selection.ts"],"sourcesContent":["import type {\n AddressableObject,\n AudioTrack,\n PartiallyResolvedAudioTrack,\n PartiallyResolvedTextTrack,\n PartiallyResolvedVideoTrack,\n Presentation,\n TextTrack,\n TrackType,\n VideoTrack,\n} from '../types';\n\n/**\n * State shape for track selection.\n * Minimal shape containing presentation and selected track IDs.\n */\nexport interface TrackSelectionState {\n presentation?: AddressableObject | Presentation | undefined;\n selectedVideoTrackId?: string | undefined;\n selectedAudioTrackId?: string | undefined;\n selectedTextTrackId?: string | undefined;\n}\n\n/**\n * Map track type to selected track ID property key in state.\n */\nexport const SelectedTrackIdKeyByType = {\n video: 'selectedVideoTrackId',\n audio: 'selectedAudioTrackId',\n text: 'selectedTextTrackId',\n} as const;\n\n/**\n * Map track type to buffer owner property key.\n * Used for SourceBuffer references in owners.\n */\nexport const BufferKeyByType = {\n video: 'videoBuffer',\n audio: 'audioBuffer',\n} as const;\n\n/**\n * Get selected track from state by type.\n * Returns properly typed track (partially or fully resolved) or undefined.\n * Type parameter T is inferred from the type argument.\n *\n * @example\n * const videoTrack = getSelectedTrack(state, 'video');\n * if (videoTrack && isResolvedTrack(videoTrack)) {\n * // videoTrack is VideoTrack\n * }\n */\nexport function getSelectedTrack<T extends TrackType>(\n state: TrackSelectionState,\n type: T\n): T extends 'video'\n ? PartiallyResolvedVideoTrack | VideoTrack | undefined\n : T extends 'audio'\n ? PartiallyResolvedAudioTrack | AudioTrack | undefined\n : T extends 'text'\n ? PartiallyResolvedTextTrack | TextTrack | undefined\n : never {\n const { presentation } = state;\n\n /** @TODO Consider moving and reusing isUnresolved(presentation) (CJP) */\n if (!presentation || !('id' in presentation)) return undefined as any;\n\n // Get track ID based on type\n const trackIdKey = SelectedTrackIdKeyByType[type];\n const trackId = state[trackIdKey];\n return presentation.selectionSets\n .find(({ type: selectionSetType }) => selectionSetType === type)\n ?.switchingSets[0]?.tracks.find(({ id }) => id === trackId) as any;\n}\n"],"mappings":";;;;AA0BA,MAAa,2BAA2B;CACtC,OAAO;CACP,OAAO;CACP,MAAM;CACP;;;;;AAMD,MAAa,kBAAkB;CAC7B,OAAO;CACP,OAAO;CACR;;;;;;;;;;;;AAaD,SAAgB,iBACd,OACA,MAOY;CACZ,MAAM,EAAE,iBAAiB;;AAGzB,KAAI,CAAC,gBAAgB,EAAE,QAAQ,cAAe,QAAO,KAAA;CAIrD,MAAM,UAAU,MADG,yBAAyB;AAE5C,QAAO,aAAa,cACjB,MAAM,EAAE,MAAM,uBAAuB,qBAAqB,KAAK,EAC9D,cAAc,IAAI,OAAO,MAAM,EAAE,SAAS,OAAO,QAAQ"}