@videojs/spf 10.0.0-beta.23 → 10.0.0-beta.25

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 (516) hide show
  1. package/dist/default/background-video.js +3 -0
  2. package/dist/{dev/core → default/core/actors}/create-machine-actor.js +3 -3
  3. package/dist/default/core/actors/create-machine-actor.js.map +1 -0
  4. package/dist/default/core/{create-transition-actor.js → actors/create-transition-actor.js} +3 -3
  5. package/dist/default/core/actors/create-transition-actor.js.map +1 -0
  6. package/dist/default/core/composition/create-composition.js +85 -0
  7. package/dist/default/core/composition/create-composition.js.map +1 -0
  8. package/dist/default/core/composition/share-signals.js +39 -0
  9. package/dist/default/core/composition/share-signals.js.map +1 -0
  10. package/dist/{dev/core → default/core/reactors}/create-machine-reactor.js +4 -4
  11. package/dist/default/core/reactors/create-machine-reactor.js.map +1 -0
  12. package/dist/default/core/signals/primitives.js +33 -11
  13. package/dist/default/core/signals/primitives.js.map +1 -1
  14. package/dist/default/core/{task.js → tasks/task.js} +2 -2
  15. package/dist/default/core/tasks/task.js.map +1 -0
  16. package/dist/default/dom.js +9 -10
  17. package/dist/default/hls.js +5 -0
  18. package/dist/default/index.js +12 -3
  19. package/dist/default/index.js.map +1 -1
  20. package/dist/default/media/abr/quality-selection.js +21 -0
  21. package/dist/default/media/abr/quality-selection.js.map +1 -0
  22. package/dist/default/{core → media}/buffer/back-buffer.js +2 -2
  23. package/dist/default/media/buffer/back-buffer.js.map +1 -0
  24. package/dist/{dev/core → default/media}/buffer/forward-buffer.js +14 -2
  25. package/dist/default/media/buffer/forward-buffer.js.map +1 -0
  26. package/dist/default/media/dom/capabilities.js +70 -0
  27. package/dist/default/media/dom/capabilities.js.map +1 -0
  28. package/dist/{dev/dom/media → default/media/dom/mse}/append-segment.js +1 -1
  29. package/dist/default/media/dom/mse/append-segment.js.map +1 -0
  30. package/dist/default/{dom/media → media/dom/mse}/buffer-flusher.js +1 -1
  31. package/dist/default/media/dom/mse/buffer-flusher.js.map +1 -0
  32. package/dist/default/media/dom/mse/duration.js +78 -0
  33. package/dist/default/media/dom/mse/duration.js.map +1 -0
  34. package/dist/default/media/dom/mse/end-of-stream.js +20 -0
  35. package/dist/default/media/dom/mse/end-of-stream.js.map +1 -0
  36. package/dist/default/{dom/media → media/dom/mse}/mediasource-setup.js +63 -18
  37. package/dist/default/media/dom/mse/mediasource-setup.js.map +1 -0
  38. package/dist/default/{dom/text/parse-vtt-segment.js → media/dom/text/resolve-vtt-segment.js} +5 -5
  39. package/dist/default/media/dom/text/resolve-vtt-segment.js.map +1 -0
  40. package/dist/default/media/dom/text/text-track-slots.js +69 -0
  41. package/dist/default/media/dom/text/text-track-slots.js.map +1 -0
  42. package/dist/{dev/core → default/media}/hls/parse-attributes.js +17 -3
  43. package/dist/default/media/hls/parse-attributes.js.map +1 -0
  44. package/dist/{dev/core → default/media}/hls/parse-media-playlist.js +25 -2
  45. package/dist/default/media/hls/parse-media-playlist.js.map +1 -0
  46. package/dist/{dev/core → default/media}/hls/parse-multivariant.js +17 -8
  47. package/dist/default/media/hls/parse-multivariant.js.map +1 -0
  48. package/dist/default/{core → media}/hls/resolve-url.js +1 -1
  49. package/dist/default/media/hls/resolve-url.js.map +1 -0
  50. package/dist/default/media/primitives/select-tracks.js +130 -0
  51. package/dist/default/media/primitives/select-tracks.js.map +1 -0
  52. package/dist/default/media/types/index.js +36 -0
  53. package/dist/default/media/types/index.js.map +1 -0
  54. package/dist/default/media/utils/cdn.js +56 -0
  55. package/dist/default/media/utils/cdn.js.map +1 -0
  56. package/dist/default/media/utils/preload.js +22 -0
  57. package/dist/default/media/utils/preload.js.map +1 -0
  58. package/dist/default/media/utils/track-selection.js +47 -0
  59. package/dist/default/media/utils/track-selection.js.map +1 -0
  60. package/dist/default/media/utils/tracks.js +130 -0
  61. package/dist/default/media/utils/tracks.js.map +1 -0
  62. package/dist/default/{core/abr → network}/bandwidth-estimator.js +6 -5
  63. package/dist/default/network/bandwidth-estimator.js.map +1 -0
  64. package/dist/{dev/dom → default}/network/chunked-stream-iterable.js +1 -1
  65. package/dist/default/network/chunked-stream-iterable.js.map +1 -0
  66. package/dist/{dev/core/abr → default/network}/ewma.js +1 -1
  67. package/dist/default/network/ewma.js.map +1 -0
  68. package/dist/default/network/fetch.js +115 -0
  69. package/dist/default/network/fetch.js.map +1 -0
  70. package/dist/default/{dom/features/segment-loader-actor.js → playback/actors/dom/segment-loader.js} +63 -23
  71. package/dist/default/playback/actors/dom/segment-loader.js.map +1 -0
  72. package/dist/default/{dom/media/source-buffer-actor.js → playback/actors/dom/source-buffer.js} +10 -9
  73. package/dist/default/playback/actors/dom/source-buffer.js.map +1 -0
  74. package/dist/{dev/dom/features/text-tracks-actor.js → default/playback/actors/dom/text-tracks.js} +7 -3
  75. package/dist/default/playback/actors/dom/text-tracks.js.map +1 -0
  76. package/dist/default/playback/actors/text-track-segment-loader.js +135 -0
  77. package/dist/default/playback/actors/text-track-segment-loader.js.map +1 -0
  78. package/dist/default/playback/behaviors/calculate-presentation-duration.js +36 -0
  79. package/dist/default/playback/behaviors/calculate-presentation-duration.js.map +1 -0
  80. package/dist/default/playback/behaviors/derive-cdn-priority.js +68 -0
  81. package/dist/default/playback/behaviors/derive-cdn-priority.js.map +1 -0
  82. package/dist/default/playback/behaviors/dom/end-of-stream.js +90 -0
  83. package/dist/default/playback/behaviors/dom/end-of-stream.js.map +1 -0
  84. package/dist/default/playback/behaviors/dom/load-segments.js +177 -0
  85. package/dist/default/playback/behaviors/dom/load-segments.js.map +1 -0
  86. package/dist/default/playback/behaviors/dom/setup-buffer-actors.js +192 -0
  87. package/dist/default/playback/behaviors/dom/setup-buffer-actors.js.map +1 -0
  88. package/dist/default/playback/behaviors/dom/setup-mediasource.js +89 -0
  89. package/dist/default/playback/behaviors/dom/setup-mediasource.js.map +1 -0
  90. package/dist/default/playback/behaviors/dom/setup-text-track-actors.js +54 -0
  91. package/dist/default/playback/behaviors/dom/setup-text-track-actors.js.map +1 -0
  92. package/dist/default/playback/behaviors/dom/sync-text-tracks.js +124 -0
  93. package/dist/default/playback/behaviors/dom/sync-text-tracks.js.map +1 -0
  94. package/dist/default/playback/behaviors/dom/track-current-time.js +52 -0
  95. package/dist/default/playback/behaviors/dom/track-current-time.js.map +1 -0
  96. package/dist/default/playback/behaviors/dom/track-load-triggers.js +94 -0
  97. package/dist/default/playback/behaviors/dom/track-load-triggers.js.map +1 -0
  98. package/dist/default/playback/behaviors/dom/track-playback-rate.js +37 -0
  99. package/dist/default/playback/behaviors/dom/track-playback-rate.js.map +1 -0
  100. package/dist/default/playback/behaviors/dom/update-mediasource-duration.js +90 -0
  101. package/dist/default/playback/behaviors/dom/update-mediasource-duration.js.map +1 -0
  102. package/dist/default/playback/behaviors/resolve-presentation.js +85 -0
  103. package/dist/default/playback/behaviors/resolve-presentation.js.map +1 -0
  104. package/dist/default/playback/behaviors/resolve-track.js +120 -0
  105. package/dist/default/playback/behaviors/resolve-track.js.map +1 -0
  106. package/dist/default/playback/behaviors/select-tracks.js +101 -0
  107. package/dist/default/playback/behaviors/select-tracks.js.map +1 -0
  108. package/dist/default/playback/behaviors/setup-failover-monitor.js +66 -0
  109. package/dist/default/playback/behaviors/setup-failover-monitor.js.map +1 -0
  110. package/dist/default/playback/behaviors/sync-preload.js +59 -0
  111. package/dist/default/playback/behaviors/sync-preload.js.map +1 -0
  112. package/dist/default/playback/behaviors/track-switching.js +341 -0
  113. package/dist/default/playback/behaviors/track-switching.js.map +1 -0
  114. package/dist/default/playback/engines/background-video/adapter.js +158 -0
  115. package/dist/default/playback/engines/background-video/adapter.js.map +1 -0
  116. package/dist/default/playback/engines/background-video/engine.js +75 -0
  117. package/dist/default/playback/engines/background-video/engine.js.map +1 -0
  118. package/dist/default/playback/engines/hls/adapter-audio-only.js +119 -0
  119. package/dist/default/playback/engines/hls/adapter-audio-only.js.map +1 -0
  120. package/dist/default/{dom/playback-engine → playback/engines/hls}/adapter.js +40 -26
  121. package/dist/default/playback/engines/hls/adapter.js.map +1 -0
  122. package/dist/default/playback/engines/hls/engine-audio-only.js +80 -0
  123. package/dist/default/playback/engines/hls/engine-audio-only.js.map +1 -0
  124. package/dist/default/playback/engines/hls/engine.js +115 -0
  125. package/dist/default/playback/engines/hls/engine.js.map +1 -0
  126. package/dist/default/playback/primitives/failover-fetch.js +42 -0
  127. package/dist/default/playback/primitives/failover-fetch.js.map +1 -0
  128. package/dist/default/playback/primitives/track-types.js +61 -0
  129. package/dist/default/playback/primitives/track-types.js.map +1 -0
  130. package/dist/dev/background-video.d.ts +3 -0
  131. package/dist/dev/background-video.js +3 -0
  132. package/dist/dev/core/{actor.d.ts → actors/actor.d.ts} +2 -2
  133. package/dist/dev/core/actors/actor.d.ts.map +1 -0
  134. package/dist/dev/core/actors/create-machine-actor.d.ts +125 -0
  135. package/dist/dev/core/actors/create-machine-actor.d.ts.map +1 -0
  136. package/dist/{default/core → dev/core/actors}/create-machine-actor.js +3 -3
  137. package/dist/dev/core/actors/create-machine-actor.js.map +1 -0
  138. package/dist/dev/core/actors/create-transition-actor.d.ts +42 -0
  139. package/dist/dev/core/actors/create-transition-actor.d.ts.map +1 -0
  140. package/dist/dev/core/{create-transition-actor.js → actors/create-transition-actor.js} +3 -3
  141. package/dist/dev/core/actors/create-transition-actor.js.map +1 -0
  142. package/dist/dev/core/composition/create-composition.d.ts +274 -0
  143. package/dist/dev/core/composition/create-composition.d.ts.map +1 -0
  144. package/dist/dev/core/composition/create-composition.js +85 -0
  145. package/dist/dev/core/composition/create-composition.js.map +1 -0
  146. package/dist/dev/core/composition/share-signals.d.ts +47 -0
  147. package/dist/dev/core/composition/share-signals.d.ts.map +1 -0
  148. package/dist/dev/core/composition/share-signals.js +39 -0
  149. package/dist/dev/core/composition/share-signals.js.map +1 -0
  150. package/dist/dev/core/machine.d.ts +0 -1
  151. package/dist/dev/core/machine.d.ts.map +1 -1
  152. package/dist/dev/core/reactors/create-machine-reactor.d.ts +93 -0
  153. package/dist/dev/core/reactors/create-machine-reactor.d.ts.map +1 -0
  154. package/dist/{default/core → dev/core/reactors}/create-machine-reactor.js +4 -4
  155. package/dist/dev/core/reactors/create-machine-reactor.js.map +1 -0
  156. package/dist/dev/core/signals/primitives.d.ts +38 -1
  157. package/dist/dev/core/signals/primitives.d.ts.map +1 -1
  158. package/dist/dev/core/signals/primitives.js +33 -11
  159. package/dist/dev/core/signals/primitives.js.map +1 -1
  160. package/dist/dev/core/tasks/task.d.ts +115 -0
  161. package/dist/dev/core/tasks/task.d.ts.map +1 -0
  162. package/dist/dev/core/{task.js → tasks/task.js} +2 -2
  163. package/dist/dev/core/tasks/task.js.map +1 -0
  164. package/dist/dev/dom.d.ts +9 -10
  165. package/dist/dev/dom.js +9 -10
  166. package/dist/dev/hls.d.ts +5 -0
  167. package/dist/dev/hls.js +5 -0
  168. package/dist/dev/index.d.ts +15 -3
  169. package/dist/dev/index.d.ts.map +1 -1
  170. package/dist/dev/index.js +12 -3
  171. package/dist/dev/index.js.map +1 -1
  172. package/dist/dev/media/abr/quality-selection.d.ts +22 -0
  173. package/dist/dev/media/abr/quality-selection.d.ts.map +1 -0
  174. package/dist/dev/media/abr/quality-selection.js +21 -0
  175. package/dist/dev/media/abr/quality-selection.js.map +1 -0
  176. package/dist/dev/media/buffer/back-buffer.d.ts +14 -0
  177. package/dist/dev/media/buffer/back-buffer.d.ts.map +1 -0
  178. package/dist/dev/{core → media}/buffer/back-buffer.js +2 -2
  179. package/dist/dev/media/buffer/back-buffer.js.map +1 -0
  180. package/dist/dev/media/buffer/forward-buffer.d.ts +14 -0
  181. package/dist/dev/media/buffer/forward-buffer.d.ts.map +1 -0
  182. package/dist/{default/core → dev/media}/buffer/forward-buffer.js +14 -2
  183. package/dist/dev/media/buffer/forward-buffer.js.map +1 -0
  184. package/dist/dev/media/dom/capabilities.js +70 -0
  185. package/dist/dev/media/dom/capabilities.js.map +1 -0
  186. package/dist/dev/{dom/media → media/dom/mse}/append-segment.d.ts +1 -1
  187. package/dist/dev/media/dom/mse/append-segment.d.ts.map +1 -0
  188. package/dist/{default/dom/media → dev/media/dom/mse}/append-segment.js +1 -1
  189. package/dist/dev/media/dom/mse/append-segment.js.map +1 -0
  190. package/dist/dev/{dom/media → media/dom/mse}/buffer-flusher.d.ts +1 -1
  191. package/dist/dev/media/dom/mse/buffer-flusher.d.ts.map +1 -0
  192. package/dist/dev/{dom/media → media/dom/mse}/buffer-flusher.js +1 -1
  193. package/dist/dev/media/dom/mse/buffer-flusher.js.map +1 -0
  194. package/dist/dev/media/dom/mse/duration.js +78 -0
  195. package/dist/dev/media/dom/mse/duration.js.map +1 -0
  196. package/dist/dev/media/dom/mse/end-of-stream.js +20 -0
  197. package/dist/dev/media/dom/mse/end-of-stream.js.map +1 -0
  198. package/dist/dev/{dom/media → media/dom/mse}/mediasource-setup.js +63 -18
  199. package/dist/dev/media/dom/mse/mediasource-setup.js.map +1 -0
  200. package/dist/dev/media/dom/text/resolve-vtt-segment.d.ts +12 -0
  201. package/dist/dev/media/dom/text/resolve-vtt-segment.d.ts.map +1 -0
  202. package/dist/dev/{dom/text/parse-vtt-segment.js → media/dom/text/resolve-vtt-segment.js} +5 -5
  203. package/dist/dev/media/dom/text/resolve-vtt-segment.js.map +1 -0
  204. package/dist/dev/media/dom/text/text-track-slots.d.ts +31 -0
  205. package/dist/dev/media/dom/text/text-track-slots.d.ts.map +1 -0
  206. package/dist/dev/media/dom/text/text-track-slots.js +69 -0
  207. package/dist/dev/media/dom/text/text-track-slots.js.map +1 -0
  208. package/dist/{default/core → dev/media}/hls/parse-attributes.js +17 -3
  209. package/dist/dev/media/hls/parse-attributes.js.map +1 -0
  210. package/dist/{default/core → dev/media}/hls/parse-media-playlist.js +25 -2
  211. package/dist/dev/media/hls/parse-media-playlist.js.map +1 -0
  212. package/dist/{default/core → dev/media}/hls/parse-multivariant.js +17 -8
  213. package/dist/dev/media/hls/parse-multivariant.js.map +1 -0
  214. package/dist/dev/{core → media}/hls/resolve-url.js +1 -1
  215. package/dist/dev/media/hls/resolve-url.js.map +1 -0
  216. package/dist/dev/media/primitives/select-tracks.d.ts +36 -0
  217. package/dist/dev/media/primitives/select-tracks.d.ts.map +1 -0
  218. package/dist/dev/media/primitives/select-tracks.js +130 -0
  219. package/dist/dev/media/primitives/select-tracks.js.map +1 -0
  220. package/dist/dev/{core → media}/types/index.d.ts +55 -3
  221. package/dist/dev/media/types/index.d.ts.map +1 -0
  222. package/dist/dev/media/types/index.js +36 -0
  223. package/dist/dev/media/types/index.js.map +1 -0
  224. package/dist/dev/media/utils/cdn.d.ts +13 -0
  225. package/dist/dev/media/utils/cdn.d.ts.map +1 -0
  226. package/dist/dev/media/utils/cdn.js +56 -0
  227. package/dist/dev/media/utils/cdn.js.map +1 -0
  228. package/dist/dev/media/utils/preload.js +22 -0
  229. package/dist/dev/media/utils/preload.js.map +1 -0
  230. package/dist/dev/media/utils/track-selection.js +47 -0
  231. package/dist/dev/media/utils/track-selection.js.map +1 -0
  232. package/dist/dev/media/utils/tracks.js +130 -0
  233. package/dist/dev/media/utils/tracks.js.map +1 -0
  234. package/dist/dev/{core/abr → network}/bandwidth-estimator.d.ts +17 -2
  235. package/dist/dev/network/bandwidth-estimator.d.ts.map +1 -0
  236. package/dist/dev/{core/abr → network}/bandwidth-estimator.js +6 -5
  237. package/dist/dev/network/bandwidth-estimator.js.map +1 -0
  238. package/dist/{default/dom → dev}/network/chunked-stream-iterable.js +1 -1
  239. package/dist/dev/network/chunked-stream-iterable.js.map +1 -0
  240. package/dist/{default/core/abr → dev/network}/ewma.js +1 -1
  241. package/dist/dev/network/ewma.js.map +1 -0
  242. package/dist/dev/network/fetch.js +115 -0
  243. package/dist/dev/network/fetch.js.map +1 -0
  244. package/dist/dev/playback/actors/dom/segment-loader.d.ts +36 -0
  245. package/dist/dev/playback/actors/dom/segment-loader.d.ts.map +1 -0
  246. package/dist/dev/{dom/features/segment-loader-actor.js → playback/actors/dom/segment-loader.js} +63 -23
  247. package/dist/dev/playback/actors/dom/segment-loader.js.map +1 -0
  248. package/dist/dev/{dom/media/source-buffer-actor.d.ts → playback/actors/dom/source-buffer.d.ts} +20 -5
  249. package/dist/dev/playback/actors/dom/source-buffer.d.ts.map +1 -0
  250. package/dist/dev/{dom/media/source-buffer-actor.js → playback/actors/dom/source-buffer.js} +10 -9
  251. package/dist/dev/playback/actors/dom/source-buffer.js.map +1 -0
  252. package/dist/dev/playback/actors/dom/text-tracks.d.ts +1 -0
  253. package/dist/{default/dom/features/text-tracks-actor.js → dev/playback/actors/dom/text-tracks.js} +7 -3
  254. package/dist/dev/playback/actors/dom/text-tracks.js.map +1 -0
  255. package/dist/dev/playback/actors/text-track-segment-loader.d.ts +59 -0
  256. package/dist/dev/playback/actors/text-track-segment-loader.d.ts.map +1 -0
  257. package/dist/dev/playback/actors/text-track-segment-loader.js +135 -0
  258. package/dist/dev/playback/actors/text-track-segment-loader.js.map +1 -0
  259. package/dist/dev/playback/actors/text-tracks.d.ts +42 -0
  260. package/dist/dev/playback/actors/text-tracks.d.ts.map +1 -0
  261. package/dist/dev/playback/behaviors/calculate-presentation-duration.d.ts +25 -0
  262. package/dist/dev/playback/behaviors/calculate-presentation-duration.d.ts.map +1 -0
  263. package/dist/dev/playback/behaviors/calculate-presentation-duration.js +36 -0
  264. package/dist/dev/playback/behaviors/calculate-presentation-duration.js.map +1 -0
  265. package/dist/dev/playback/behaviors/derive-cdn-priority.js +68 -0
  266. package/dist/dev/playback/behaviors/derive-cdn-priority.js.map +1 -0
  267. package/dist/dev/playback/behaviors/dom/end-of-stream.js +90 -0
  268. package/dist/dev/playback/behaviors/dom/end-of-stream.js.map +1 -0
  269. package/dist/dev/playback/behaviors/dom/load-segments.d.ts +62 -0
  270. package/dist/dev/playback/behaviors/dom/load-segments.d.ts.map +1 -0
  271. package/dist/dev/playback/behaviors/dom/load-segments.js +177 -0
  272. package/dist/dev/playback/behaviors/dom/load-segments.js.map +1 -0
  273. package/dist/dev/playback/behaviors/dom/setup-buffer-actors.js +192 -0
  274. package/dist/dev/playback/behaviors/dom/setup-buffer-actors.js.map +1 -0
  275. package/dist/dev/playback/behaviors/dom/setup-mediasource.js +89 -0
  276. package/dist/dev/playback/behaviors/dom/setup-mediasource.js.map +1 -0
  277. package/dist/dev/playback/behaviors/dom/setup-text-track-actors.d.ts +31 -0
  278. package/dist/dev/playback/behaviors/dom/setup-text-track-actors.d.ts.map +1 -0
  279. package/dist/dev/playback/behaviors/dom/setup-text-track-actors.js +54 -0
  280. package/dist/dev/playback/behaviors/dom/setup-text-track-actors.js.map +1 -0
  281. package/dist/dev/playback/behaviors/dom/sync-text-tracks.js +124 -0
  282. package/dist/dev/playback/behaviors/dom/sync-text-tracks.js.map +1 -0
  283. package/dist/dev/playback/behaviors/dom/track-current-time.d.ts +34 -0
  284. package/dist/dev/playback/behaviors/dom/track-current-time.d.ts.map +1 -0
  285. package/dist/dev/playback/behaviors/dom/track-current-time.js +52 -0
  286. package/dist/dev/playback/behaviors/dom/track-current-time.js.map +1 -0
  287. package/dist/dev/playback/behaviors/dom/track-load-triggers.d.ts +48 -0
  288. package/dist/dev/playback/behaviors/dom/track-load-triggers.d.ts.map +1 -0
  289. package/dist/dev/playback/behaviors/dom/track-load-triggers.js +94 -0
  290. package/dist/dev/playback/behaviors/dom/track-load-triggers.js.map +1 -0
  291. package/dist/dev/playback/behaviors/dom/track-playback-rate.d.ts +34 -0
  292. package/dist/dev/playback/behaviors/dom/track-playback-rate.d.ts.map +1 -0
  293. package/dist/dev/playback/behaviors/dom/track-playback-rate.js +37 -0
  294. package/dist/dev/playback/behaviors/dom/track-playback-rate.js.map +1 -0
  295. package/dist/dev/playback/behaviors/dom/update-mediasource-duration.js +90 -0
  296. package/dist/dev/playback/behaviors/dom/update-mediasource-duration.js.map +1 -0
  297. package/dist/dev/playback/behaviors/resolve-presentation.d.ts +7 -0
  298. package/dist/dev/playback/behaviors/resolve-presentation.d.ts.map +1 -0
  299. package/dist/dev/playback/behaviors/resolve-presentation.js +85 -0
  300. package/dist/dev/playback/behaviors/resolve-presentation.js.map +1 -0
  301. package/dist/dev/playback/behaviors/resolve-track.js +120 -0
  302. package/dist/dev/playback/behaviors/resolve-track.js.map +1 -0
  303. package/dist/dev/playback/behaviors/select-tracks.d.ts +13 -0
  304. package/dist/dev/playback/behaviors/select-tracks.d.ts.map +1 -0
  305. package/dist/dev/playback/behaviors/select-tracks.js +101 -0
  306. package/dist/dev/playback/behaviors/select-tracks.js.map +1 -0
  307. package/dist/dev/playback/behaviors/setup-failover-monitor.d.ts +12 -0
  308. package/dist/dev/playback/behaviors/setup-failover-monitor.d.ts.map +1 -0
  309. package/dist/dev/playback/behaviors/setup-failover-monitor.js +66 -0
  310. package/dist/dev/playback/behaviors/setup-failover-monitor.js.map +1 -0
  311. package/dist/dev/playback/behaviors/sync-preload.js +59 -0
  312. package/dist/dev/playback/behaviors/sync-preload.js.map +1 -0
  313. package/dist/dev/playback/behaviors/track-switching.js +341 -0
  314. package/dist/dev/playback/behaviors/track-switching.js.map +1 -0
  315. package/dist/dev/playback/engines/background-video/adapter.d.ts +60 -0
  316. package/dist/dev/playback/engines/background-video/adapter.d.ts.map +1 -0
  317. package/dist/dev/playback/engines/background-video/adapter.js +158 -0
  318. package/dist/dev/playback/engines/background-video/adapter.js.map +1 -0
  319. package/dist/dev/playback/engines/background-video/engine.d.ts +107 -0
  320. package/dist/dev/playback/engines/background-video/engine.d.ts.map +1 -0
  321. package/dist/dev/playback/engines/background-video/engine.js +75 -0
  322. package/dist/dev/playback/engines/background-video/engine.js.map +1 -0
  323. package/dist/dev/playback/engines/hls/adapter-audio-only.d.ts +45 -0
  324. package/dist/dev/playback/engines/hls/adapter-audio-only.d.ts.map +1 -0
  325. package/dist/dev/playback/engines/hls/adapter-audio-only.js +119 -0
  326. package/dist/dev/playback/engines/hls/adapter-audio-only.js.map +1 -0
  327. package/dist/dev/{dom/playback-engine → playback/engines/hls}/adapter.d.ts +13 -12
  328. package/dist/dev/playback/engines/hls/adapter.d.ts.map +1 -0
  329. package/dist/dev/{dom/playback-engine → playback/engines/hls}/adapter.js +40 -26
  330. package/dist/dev/playback/engines/hls/adapter.js.map +1 -0
  331. package/dist/dev/playback/engines/hls/engine-audio-only.d.ts +127 -0
  332. package/dist/dev/playback/engines/hls/engine-audio-only.d.ts.map +1 -0
  333. package/dist/dev/playback/engines/hls/engine-audio-only.js +80 -0
  334. package/dist/dev/playback/engines/hls/engine-audio-only.js.map +1 -0
  335. package/dist/dev/playback/engines/hls/engine.d.ts +236 -0
  336. package/dist/dev/playback/engines/hls/engine.d.ts.map +1 -0
  337. package/dist/dev/playback/engines/hls/engine.js +115 -0
  338. package/dist/dev/playback/engines/hls/engine.js.map +1 -0
  339. package/dist/dev/playback/primitives/failover-fetch.js +42 -0
  340. package/dist/dev/playback/primitives/failover-fetch.js.map +1 -0
  341. package/dist/dev/playback/primitives/track-types.js +61 -0
  342. package/dist/dev/playback/primitives/track-types.js.map +1 -0
  343. package/package.json +12 -7
  344. package/dist/default/core/abr/bandwidth-estimator.js.map +0 -1
  345. package/dist/default/core/abr/ewma.js.map +0 -1
  346. package/dist/default/core/abr/quality-selection.js +0 -53
  347. package/dist/default/core/abr/quality-selection.js.map +0 -1
  348. package/dist/default/core/buffer/back-buffer.js.map +0 -1
  349. package/dist/default/core/buffer/forward-buffer.js.map +0 -1
  350. package/dist/default/core/create-machine-actor.js.map +0 -1
  351. package/dist/default/core/create-machine-reactor.js.map +0 -1
  352. package/dist/default/core/create-transition-actor.js.map +0 -1
  353. package/dist/default/core/features/calculate-presentation-duration.js +0 -53
  354. package/dist/default/core/features/calculate-presentation-duration.js.map +0 -1
  355. package/dist/default/core/features/quality-switching.js +0 -69
  356. package/dist/default/core/features/quality-switching.js.map +0 -1
  357. package/dist/default/core/features/resolve-presentation.js +0 -73
  358. package/dist/default/core/features/resolve-presentation.js.map +0 -1
  359. package/dist/default/core/features/resolve-track.js +0 -66
  360. package/dist/default/core/features/resolve-track.js.map +0 -1
  361. package/dist/default/core/features/select-tracks.js +0 -130
  362. package/dist/default/core/features/select-tracks.js.map +0 -1
  363. package/dist/default/core/features/sync-preload-attribute.js +0 -26
  364. package/dist/default/core/features/sync-preload-attribute.js.map +0 -1
  365. package/dist/default/core/hls/parse-attributes.js.map +0 -1
  366. package/dist/default/core/hls/parse-media-playlist.js.map +0 -1
  367. package/dist/default/core/hls/parse-multivariant.js.map +0 -1
  368. package/dist/default/core/hls/resolve-url.js.map +0 -1
  369. package/dist/default/core/task.js.map +0 -1
  370. package/dist/default/core/types/index.js +0 -15
  371. package/dist/default/core/types/index.js.map +0 -1
  372. package/dist/default/core/utils/generate-id.js +0 -21
  373. package/dist/default/core/utils/generate-id.js.map +0 -1
  374. package/dist/default/core/utils/track-selection.js +0 -39
  375. package/dist/default/core/utils/track-selection.js.map +0 -1
  376. package/dist/default/dom/features/end-of-stream.js +0 -145
  377. package/dist/default/dom/features/end-of-stream.js.map +0 -1
  378. package/dist/default/dom/features/load-segments.js +0 -192
  379. package/dist/default/dom/features/load-segments.js.map +0 -1
  380. package/dist/default/dom/features/load-text-track-cues.js +0 -104
  381. package/dist/default/dom/features/load-text-track-cues.js.map +0 -1
  382. package/dist/default/dom/features/segment-loader-actor.js.map +0 -1
  383. package/dist/default/dom/features/setup-mediasource.js +0 -48
  384. package/dist/default/dom/features/setup-mediasource.js.map +0 -1
  385. package/dist/default/dom/features/setup-sourcebuffer.js +0 -78
  386. package/dist/default/dom/features/setup-sourcebuffer.js.map +0 -1
  387. package/dist/default/dom/features/sync-text-tracks.js +0 -95
  388. package/dist/default/dom/features/sync-text-tracks.js.map +0 -1
  389. package/dist/default/dom/features/text-track-segment-loader-actor.js +0 -55
  390. package/dist/default/dom/features/text-track-segment-loader-actor.js.map +0 -1
  391. package/dist/default/dom/features/text-tracks-actor.js.map +0 -1
  392. package/dist/default/dom/features/track-current-time.js +0 -49
  393. package/dist/default/dom/features/track-current-time.js.map +0 -1
  394. package/dist/default/dom/features/track-playback-initiated.js +0 -66
  395. package/dist/default/dom/features/track-playback-initiated.js.map +0 -1
  396. package/dist/default/dom/features/track-playback-rate.js +0 -32
  397. package/dist/default/dom/features/track-playback-rate.js.map +0 -1
  398. package/dist/default/dom/features/update-duration.js +0 -78
  399. package/dist/default/dom/features/update-duration.js.map +0 -1
  400. package/dist/default/dom/media/append-segment.js.map +0 -1
  401. package/dist/default/dom/media/buffer-flusher.js.map +0 -1
  402. package/dist/default/dom/media/mediasource-setup.js.map +0 -1
  403. package/dist/default/dom/media/source-buffer-actor.js.map +0 -1
  404. package/dist/default/dom/network/chunked-stream-iterable.js.map +0 -1
  405. package/dist/default/dom/network/fetch.js +0 -54
  406. package/dist/default/dom/network/fetch.js.map +0 -1
  407. package/dist/default/dom/playback-engine/adapter.js.map +0 -1
  408. package/dist/default/dom/playback-engine/engine.js +0 -142
  409. package/dist/default/dom/playback-engine/engine.js.map +0 -1
  410. package/dist/default/dom/playback-engine.js +0 -4
  411. package/dist/default/dom/text/parse-vtt-segment.js.map +0 -1
  412. package/dist/dev/core/abr/bandwidth-estimator.d.ts.map +0 -1
  413. package/dist/dev/core/abr/bandwidth-estimator.js.map +0 -1
  414. package/dist/dev/core/abr/ewma.js.map +0 -1
  415. package/dist/dev/core/abr/quality-selection.js +0 -53
  416. package/dist/dev/core/abr/quality-selection.js.map +0 -1
  417. package/dist/dev/core/actor.d.ts.map +0 -1
  418. package/dist/dev/core/buffer/back-buffer.js.map +0 -1
  419. package/dist/dev/core/buffer/forward-buffer.js.map +0 -1
  420. package/dist/dev/core/create-machine-actor.d.ts +0 -12
  421. package/dist/dev/core/create-machine-actor.d.ts.map +0 -1
  422. package/dist/dev/core/create-machine-actor.js.map +0 -1
  423. package/dist/dev/core/create-machine-reactor.d.ts +0 -8
  424. package/dist/dev/core/create-machine-reactor.d.ts.map +0 -1
  425. package/dist/dev/core/create-machine-reactor.js.map +0 -1
  426. package/dist/dev/core/create-transition-actor.d.ts +0 -23
  427. package/dist/dev/core/create-transition-actor.d.ts.map +0 -1
  428. package/dist/dev/core/create-transition-actor.js.map +0 -1
  429. package/dist/dev/core/features/calculate-presentation-duration.js +0 -53
  430. package/dist/dev/core/features/calculate-presentation-duration.js.map +0 -1
  431. package/dist/dev/core/features/quality-switching.js +0 -69
  432. package/dist/dev/core/features/quality-switching.js.map +0 -1
  433. package/dist/dev/core/features/resolve-presentation.js +0 -73
  434. package/dist/dev/core/features/resolve-presentation.js.map +0 -1
  435. package/dist/dev/core/features/resolve-track.js +0 -66
  436. package/dist/dev/core/features/resolve-track.js.map +0 -1
  437. package/dist/dev/core/features/select-tracks.js +0 -130
  438. package/dist/dev/core/features/select-tracks.js.map +0 -1
  439. package/dist/dev/core/features/sync-preload-attribute.js +0 -26
  440. package/dist/dev/core/features/sync-preload-attribute.js.map +0 -1
  441. package/dist/dev/core/hls/parse-attributes.js.map +0 -1
  442. package/dist/dev/core/hls/parse-media-playlist.js.map +0 -1
  443. package/dist/dev/core/hls/parse-multivariant.js.map +0 -1
  444. package/dist/dev/core/hls/resolve-url.js.map +0 -1
  445. package/dist/dev/core/task.js.map +0 -1
  446. package/dist/dev/core/types/index.d.ts.map +0 -1
  447. package/dist/dev/core/types/index.js +0 -15
  448. package/dist/dev/core/types/index.js.map +0 -1
  449. package/dist/dev/core/utils/generate-id.js +0 -21
  450. package/dist/dev/core/utils/generate-id.js.map +0 -1
  451. package/dist/dev/core/utils/track-selection.d.ts +0 -16
  452. package/dist/dev/core/utils/track-selection.d.ts.map +0 -1
  453. package/dist/dev/core/utils/track-selection.js +0 -39
  454. package/dist/dev/core/utils/track-selection.js.map +0 -1
  455. package/dist/dev/dom/features/end-of-stream.js +0 -145
  456. package/dist/dev/dom/features/end-of-stream.js.map +0 -1
  457. package/dist/dev/dom/features/load-segments.d.ts +0 -72
  458. package/dist/dev/dom/features/load-segments.d.ts.map +0 -1
  459. package/dist/dev/dom/features/load-segments.js +0 -192
  460. package/dist/dev/dom/features/load-segments.js.map +0 -1
  461. package/dist/dev/dom/features/load-text-track-cues.d.ts +0 -85
  462. package/dist/dev/dom/features/load-text-track-cues.d.ts.map +0 -1
  463. package/dist/dev/dom/features/load-text-track-cues.js +0 -104
  464. package/dist/dev/dom/features/load-text-track-cues.js.map +0 -1
  465. package/dist/dev/dom/features/segment-loader-actor.d.ts +0 -1
  466. package/dist/dev/dom/features/segment-loader-actor.js.map +0 -1
  467. package/dist/dev/dom/features/setup-mediasource.js +0 -48
  468. package/dist/dev/dom/features/setup-mediasource.js.map +0 -1
  469. package/dist/dev/dom/features/setup-sourcebuffer.d.ts +0 -9
  470. package/dist/dev/dom/features/setup-sourcebuffer.d.ts.map +0 -1
  471. package/dist/dev/dom/features/setup-sourcebuffer.js +0 -78
  472. package/dist/dev/dom/features/setup-sourcebuffer.js.map +0 -1
  473. package/dist/dev/dom/features/sync-text-tracks.js +0 -95
  474. package/dist/dev/dom/features/sync-text-tracks.js.map +0 -1
  475. package/dist/dev/dom/features/text-track-segment-loader-actor.d.ts +0 -12
  476. package/dist/dev/dom/features/text-track-segment-loader-actor.d.ts.map +0 -1
  477. package/dist/dev/dom/features/text-track-segment-loader-actor.js +0 -55
  478. package/dist/dev/dom/features/text-track-segment-loader-actor.js.map +0 -1
  479. package/dist/dev/dom/features/text-tracks-actor.d.ts +0 -31
  480. package/dist/dev/dom/features/text-tracks-actor.d.ts.map +0 -1
  481. package/dist/dev/dom/features/text-tracks-actor.js.map +0 -1
  482. package/dist/dev/dom/features/track-current-time.d.ts +0 -40
  483. package/dist/dev/dom/features/track-current-time.d.ts.map +0 -1
  484. package/dist/dev/dom/features/track-current-time.js +0 -49
  485. package/dist/dev/dom/features/track-current-time.js.map +0 -1
  486. package/dist/dev/dom/features/track-playback-initiated.d.ts +0 -46
  487. package/dist/dev/dom/features/track-playback-initiated.d.ts.map +0 -1
  488. package/dist/dev/dom/features/track-playback-initiated.js +0 -66
  489. package/dist/dev/dom/features/track-playback-initiated.js.map +0 -1
  490. package/dist/dev/dom/features/track-playback-rate.d.ts +0 -34
  491. package/dist/dev/dom/features/track-playback-rate.d.ts.map +0 -1
  492. package/dist/dev/dom/features/track-playback-rate.js +0 -32
  493. package/dist/dev/dom/features/track-playback-rate.js.map +0 -1
  494. package/dist/dev/dom/features/update-duration.js +0 -78
  495. package/dist/dev/dom/features/update-duration.js.map +0 -1
  496. package/dist/dev/dom/media/append-segment.d.ts.map +0 -1
  497. package/dist/dev/dom/media/append-segment.js.map +0 -1
  498. package/dist/dev/dom/media/buffer-flusher.d.ts.map +0 -1
  499. package/dist/dev/dom/media/buffer-flusher.js.map +0 -1
  500. package/dist/dev/dom/media/mediasource-setup.js.map +0 -1
  501. package/dist/dev/dom/media/source-buffer-actor.d.ts.map +0 -1
  502. package/dist/dev/dom/media/source-buffer-actor.js.map +0 -1
  503. package/dist/dev/dom/network/chunked-stream-iterable.js.map +0 -1
  504. package/dist/dev/dom/network/fetch.js +0 -54
  505. package/dist/dev/dom/network/fetch.js.map +0 -1
  506. package/dist/dev/dom/playback-engine/adapter.d.ts.map +0 -1
  507. package/dist/dev/dom/playback-engine/adapter.js.map +0 -1
  508. package/dist/dev/dom/playback-engine/engine.d.ts +0 -124
  509. package/dist/dev/dom/playback-engine/engine.d.ts.map +0 -1
  510. package/dist/dev/dom/playback-engine/engine.js +0 -142
  511. package/dist/dev/dom/playback-engine/engine.js.map +0 -1
  512. package/dist/dev/dom/playback-engine.d.ts +0 -4
  513. package/dist/dev/dom/playback-engine.js +0 -4
  514. package/dist/dev/dom/text/parse-vtt-segment.d.ts +0 -12
  515. package/dist/dev/dom/text/parse-vtt-segment.d.ts.map +0 -1
  516. package/dist/dev/dom/text/parse-vtt-segment.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select-tracks.js","names":[],"sources":["../../../../src/playback/behaviors/select-tracks.ts"],"sourcesContent":["/**\n * **Default audio/video track selection on src load / unselect on src unload.**\n * When a presentation is resolved, sets `selectedVideoTrackId` /\n * `selectedAudioTrackId` to a per-type-picker default if no selection already\n * exists. When the presentation is unset/reset (transitions back to unresolved),\n * clears the selection so a stale id from the previous source doesn't persist.\n *\n * Lifecycle-driven: each transition fires its work once. Does not police the\n * selection between transitions; external writes (user picks, ABR, programmatic\n * filter-driven re-picks) are left alone.\n *\n * Picker is config-driven: each per-type export wires a sensible default\n * (`pickAudioTrack` for audio — three-tier language-aware; `pickFirstTrackId`\n * for video) and the caller can supply their own via `config.picker` for custom\n * selection logic. The behavior's `config` is forwarded to the picker as its\n * second argument, so options like `preferredAudioLanguage` reach the picker\n * without an intermediate wrapping layer.\n *\n * Compose `selectVideoTrack` for the simple \"pick a default video track\"\n * behavior, or `switchVideoTrack` (`./track-switching.ts`) for the\n * ABR-driven variant. Compose `selectAudioTrack` for the simple default\n * pick, or `switchAudioTrack` (`./track-switching.ts`) for the\n * filter-reactive + mid-stream-flush slot-owner variant — when audio-abr\n * lands, `switchAudioTrack` extends into `switchAudioQuality`. Compose\n * only one per type — they're alternatives, not stackable (each writes\n * the same `selected*TrackId` slot). The simple variants tree-shake out\n * the heavier machinery (bandwidth estimator, quality selection, flush\n * orchestration).\n *\n * Text selection has no simple variant here — it's owned by `switchTextTrack`\n * (`./track-switching.ts`), which resolves standing `userTextTrackSelection`\n * intent against the constrained, CDN-scoped renditions.\n */\n\nimport { defineBehavior } from '../../core/composition/create-composition';\nimport { createMachineReactor } from '../../core/reactors/create-machine-reactor';\nimport { computed, type ReadonlySignal, type Signal } from '../../core/signals/primitives';\nimport {\n type AudioSelectionConfig,\n pickAudioTrack,\n pickFirstTrackId,\n type TrackPicker,\n type TrackSelectionState,\n type VideoSelectionConfig,\n} from '../../media/primitives/select-tracks';\nimport { isResolvedPresentation } from '../../media/types';\nimport { AUDIO_TYPE_CONFIG, VIDEO_TYPE_CONFIG } from '../primitives/track-types';\n\n// ============================================================================\n// Specialization helper\n//\n// `setupTrackSelection` has the same shape as a Behavior `setup` function:\n// `({ state, config }) => Reactor`. Each `selectXTrack` export below calls\n// it from inside its own `defineBehavior` setup, supplying its per-type\n// `selectedKey`, default picker, and forwarded picker config. The lifecycle\n// — pick on entering 'presentation-resolved' if not already selected; clear\n// on entering 'presentation-unresolved' — is shared.\n// ============================================================================\n\ntype SelectedTrackKey = 'selectedVideoTrackId' | 'selectedAudioTrackId';\n\ntype SelectStateMap<K extends SelectedTrackKey> = {\n presentation: ReadonlySignal<TrackSelectionState['presentation']>;\n} & { [P in K]: Signal<TrackSelectionState[P]> };\n\ninterface TrackSelectionSetupConfig<K extends SelectedTrackKey, PickerConfig> {\n selectedKey: K;\n picker: TrackPicker<PickerConfig>;\n pickerConfig?: PickerConfig;\n}\n\nfunction setupTrackSelection<K extends SelectedTrackKey, PickerConfig>({\n state,\n config: { selectedKey, picker, pickerConfig },\n}: {\n state: SelectStateMap<K>;\n config: TrackSelectionSetupConfig<K, PickerConfig>;\n}) {\n const derivedStateSignal = computed(() =>\n isResolvedPresentation(state.presentation.get())\n ? ('presentation-resolved' as const)\n : ('presentation-unresolved' as const)\n );\n\n return createMachineReactor({\n initial: 'presentation-unresolved',\n monitor: () => derivedStateSignal.get(),\n states: {\n 'presentation-unresolved': {},\n 'presentation-resolved': {\n // Entry: pick a default on entering presentation-resolved if none\n // is set. External writes (user picks, ABR) that already populated\n // the slot are left alone.\n //\n // The returned cleanup runs on state exit — which fires on src\n // unload (presentation-resolved → presentation-unresolved) AND on\n // behavior destroy (presentation-resolved → destroying →\n // destroyed). Putting the clear here rather than as\n // presentation-unresolved.entry is more cohesive (operation +\n // cleanup co-located) and correctly covers destroy (destroy\n // doesn't pass through presentation-unresolved).\n entry: () => {\n if (!state[selectedKey].get()) {\n // `state.presentation.get()` is non-null inside this entry —\n // the reactor's `'presentation-resolved'` gate is exactly\n // `isResolvedPresentation(state.presentation.get())`, which\n // requires a truthy Presentation.\n const id = picker(state.presentation.get()!, pickerConfig);\n if (id) state[selectedKey].set(id);\n }\n return () => state[selectedKey].set(undefined);\n },\n },\n },\n });\n}\n\n// ============================================================================\n// Default pickers\n//\n// Each variant resolves its picker as `config?.picker ?? <default>` and\n// forwards the whole engine config as `pickerConfig`, so a rich picker\n// (`pickAudioTrack`) reads its options directly. Audio uses its primitive\n// picker as-is; video adapts `pickFirstTrackId` (positional `type` arg) into\n// the `TrackPicker` shape.\n// ============================================================================\n\n/** Default video picker: first track in the video selection set. */\nconst defaultVideoPicker: TrackPicker = (presentation) => pickFirstTrackId(presentation, 'video');\n\n// ============================================================================\n// Specialized exports — one per track type\n// ============================================================================\n\n/**\n * Config for `selectVideoTrack`. Pass `picker` to fully override selection\n * logic; otherwise the default `pickFirstTrackId` is used.\n */\nexport interface SelectVideoTrackConfig extends VideoSelectionConfig {\n picker?: TrackPicker<SelectVideoTrackConfig>;\n}\n\n/**\n * Select a video track when a presentation loads. Clears the selection on\n * src unload.\n *\n * This is the simple, non-ABR counterpart to `switchVideoTrack` — compose\n * one or the other, not both (both write `selectedVideoTrackId`). Composing\n * `selectVideoTrack` alone tree-shakes out the ABR code path\n * (bandwidth-estimator, quality-selection); use it for sources without\n * meaningful quality variants, test setups, or players that intentionally\n * pin a quality.\n *\n * @example\n * const reactor = selectVideoTrack.setup({ state });\n */\nexport const selectVideoTrack = defineBehavior({\n stateKeys: ['presentation', 'selectedVideoTrackId'],\n contextKeys: [],\n setup: ({ state, config }: { state: SelectStateMap<'selectedVideoTrackId'>; config?: SelectVideoTrackConfig }) =>\n setupTrackSelection({\n state,\n config: {\n selectedKey: VIDEO_TYPE_CONFIG.selectedKey,\n picker: config?.picker ?? defaultVideoPicker,\n pickerConfig: config,\n },\n }),\n});\n\n/**\n * Config for `selectAudioTrack`. Pass `picker` to fully override selection\n * logic; otherwise the default `pickAudioTrack` is used (three-tier:\n * `preferredAudioLanguage` → `DEFAULT=YES` → first track).\n */\nexport interface SelectAudioTrackConfig extends AudioSelectionConfig {\n picker?: TrackPicker<SelectAudioTrackConfig>;\n}\n\n/**\n * Select an audio track when a presentation loads. Clears the selection\n * on src unload.\n *\n * This is the simple, lifecycle-only counterpart to `switchAudioTrack`\n * (in `./track-switching.ts`) — compose one or the other, not both\n * (both write `selectedAudioTrackId`). `switchAudioTrack` adds\n * filter-reactivity (`userAudioTrackSelection`) and mid-stream-flush\n * orchestration; `selectAudioTrack` covers the default-on-load case\n * without those. Use this variant for test setups, audio-only flows\n * that don't expose language switching, or composition variants that\n * intentionally pin a track.\n *\n * @example\n * const reactor = selectAudioTrack.setup({ state });\n *\n * @example\n * // Custom picker with language preference\n * const reactor = selectAudioTrack.setup({\n * state,\n * config: { preferredAudioLanguage: 'en', picker: myLanguageAwarePicker },\n * });\n */\nexport const selectAudioTrack = defineBehavior({\n stateKeys: ['presentation', 'selectedAudioTrackId'],\n contextKeys: [],\n setup: ({ state, config }: { state: SelectStateMap<'selectedAudioTrackId'>; config?: SelectAudioTrackConfig }) =>\n setupTrackSelection({\n state,\n config: {\n selectedKey: AUDIO_TYPE_CONFIG.selectedKey,\n picker: config?.picker ?? pickAudioTrack,\n pickerConfig: config,\n },\n }),\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuEA,SAAS,oBAA8D,EACrE,OACA,QAAQ,EAAE,aAAa,QAAQ,kBAI9B;CACD,MAAM,qBAAqB,eACzB,uBAAuB,MAAM,aAAa,KAAK,CAAC,GAC3C,0BACA,0BACN;AAED,QAAO,qBAAqB;EAC1B,SAAS;EACT,eAAe,mBAAmB,KAAK;EACvC,QAAQ;GACN,2BAA2B,EAAE;GAC7B,yBAAyB,EAYvB,aAAa;AACX,QAAI,CAAC,MAAM,aAAa,KAAK,EAAE;KAK7B,MAAM,KAAK,OAAO,MAAM,aAAa,KAAK,EAAG,aAAa;AAC1D,SAAI,GAAI,OAAM,aAAa,IAAI,GAAG;;AAEpC,iBAAa,MAAM,aAAa,IAAI,KAAA,EAAU;MAEjD;GACF;EACF,CAAC;;;AAcJ,MAAM,sBAAmC,iBAAiB,iBAAiB,cAAc,QAAQ;;;;;;;;;;;;;;;AA4BjG,MAAa,mBAAmB,eAAe;CAC7C,WAAW,CAAC,gBAAgB,uBAAuB;CACnD,aAAa,EAAE;CACf,QAAQ,EAAE,OAAO,aACf,oBAAoB;EAClB;EACA,QAAQ;GACN,aAAa,kBAAkB;GAC/B,QAAQ,QAAQ,UAAU;GAC1B,cAAc;GACf;EACF,CAAC;CACL,CAAC;AAkC8B,eAAe;CAC7C,WAAW,CAAC,gBAAgB,uBAAuB;CACnD,aAAa,EAAE;CACf,QAAQ,EAAE,OAAO,aACf,oBAAoB;EAClB;EACA,QAAQ;GACN,aAAa,kBAAkB;GAC/B,QAAQ,QAAQ,UAAU;GAC1B,cAAc;GACf;EACF,CAAC;CACL,CAAC"}
@@ -0,0 +1,66 @@
1
+ import { computed, update } from "../../core/signals/primitives.js";
2
+ import { defineBehavior } from "../../core/composition/create-composition.js";
3
+ import { createMachineReactor } from "../../core/reactors/create-machine-reactor.js";
4
+ import { isResolvedPresentation } from "../../media/types/index.js";
5
+ //#region src/playback/behaviors/setup-failover-monitor.ts
6
+ /**
7
+ * **CDN failover cooldown.** The expiry half of multi-CDN failover. Fetch sites
8
+ * own the *trip*: on a failed fetch they add the failing CDN (origin) to the
9
+ * `failedCdns` state signal directly. This behavior owns the *expiry*: while a
10
+ * presentation is resolved, it watches `failedCdns` and, for each CDN that
11
+ * appears, schedules a timer to remove it once its cooldown lapses.
12
+ * `track-switching`'s `excludeFailedCdns` constraint prunes a failed CDN's
13
+ * tracks and the active-CDN scope falls to the next one — and back, once the
14
+ * cooldown removes it here.
15
+ *
16
+ * Lifecycle is per-source: timers + `failedCdns` are cleared on exit (a new
17
+ * source starts with a clean slate). Policy (cooldown) is engine config. This is
18
+ * the minimal `network-resilience` slice — a single failure trips a CDN, since
19
+ * transient blips are the retry layer's job (it sits below the fetch sites, so
20
+ * anything that reaches `failedCdns` is already terminal).
21
+ */
22
+ const DEFAULT_FAILOVER_MONITOR_CONFIG = { cooldownMs: 3e5 };
23
+ /**
24
+ * Expire failed CDNs from `failedCdns` once their cooldown lapses, for the
25
+ * resolved source.
26
+ *
27
+ * @example
28
+ * const reactor = setupFailoverMonitor.setup({ state });
29
+ */
30
+ const setupFailoverMonitor = defineBehavior({
31
+ stateKeys: ["presentation", "failedCdns"],
32
+ contextKeys: [],
33
+ setup: ({ state, config = {} }) => {
34
+ const cooldownMs = config.failover?.cooldownMs ?? DEFAULT_FAILOVER_MONITOR_CONFIG.cooldownMs;
35
+ const timers = /* @__PURE__ */ new Map();
36
+ const derivedStateSignal = computed(() => isResolvedPresentation(state.presentation.get()) ? "presentation-resolved" : "presentation-unresolved");
37
+ return createMachineReactor({
38
+ initial: "presentation-unresolved",
39
+ monitor: () => derivedStateSignal.get(),
40
+ states: {
41
+ "presentation-unresolved": {},
42
+ "presentation-resolved": {
43
+ entry: () => () => {
44
+ timers.forEach((timer) => clearTimeout(timer));
45
+ timers.clear();
46
+ state.failedCdns.set(void 0);
47
+ },
48
+ effects: [() => {
49
+ (state.failedCdns.get() ?? []).forEach((cdn) => {
50
+ if (timers.has(cdn)) return;
51
+ const timer = setTimeout(() => {
52
+ timers.delete(cdn);
53
+ update(state.failedCdns, (current) => current?.filter((c) => c !== cdn));
54
+ }, cooldownMs);
55
+ timers.set(cdn, timer);
56
+ });
57
+ }]
58
+ }
59
+ }
60
+ });
61
+ }
62
+ });
63
+ //#endregion
64
+ export { setupFailoverMonitor };
65
+
66
+ //# sourceMappingURL=setup-failover-monitor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup-failover-monitor.js","names":[],"sources":["../../../../src/playback/behaviors/setup-failover-monitor.ts"],"sourcesContent":["/**\n * **CDN failover cooldown.** The expiry half of multi-CDN failover. Fetch sites\n * own the *trip*: on a failed fetch they add the failing CDN (origin) to the\n * `failedCdns` state signal directly. This behavior owns the *expiry*: while a\n * presentation is resolved, it watches `failedCdns` and, for each CDN that\n * appears, schedules a timer to remove it once its cooldown lapses.\n * `track-switching`'s `excludeFailedCdns` constraint prunes a failed CDN's\n * tracks and the active-CDN scope falls to the next one — and back, once the\n * cooldown removes it here.\n *\n * Lifecycle is per-source: timers + `failedCdns` are cleared on exit (a new\n * source starts with a clean slate). Policy (cooldown) is engine config. This is\n * the minimal `network-resilience` slice — a single failure trips a CDN, since\n * transient blips are the retry layer's job (it sits below the fetch sites, so\n * anything that reaches `failedCdns` is already terminal).\n */\n\nimport { defineBehavior } from '../../core/composition/create-composition';\nimport { createMachineReactor } from '../../core/reactors/create-machine-reactor';\nimport { computed, type ReadonlySignal, type Signal, update } from '../../core/signals/primitives';\nimport { isResolvedPresentation, type MaybeResolvedPresentation } from '../../media/types';\n\n/**\n * Failover policy: how long a CDN stays excluded after a failed fetch trips it.\n * Supplied via engine config.\n */\nexport interface FailoverMonitorConfig {\n /** How long a tripped CDN stays excluded, in milliseconds. */\n cooldownMs: number;\n}\n\nexport const DEFAULT_FAILOVER_MONITOR_CONFIG: FailoverMonitorConfig = {\n // 5 minutes — a CDN outage is an infrastructure problem that outlasts a\n // transient blip, so re-probing it sooner mostly re-trips. Matches the\n // prevailing prior-art default (ExoPlayer's location exclusion, hls.js's\n // content-steering penalty box).\n cooldownMs: 300_000,\n};\n\nexport interface SetupFailoverMonitorState {\n presentation?: MaybeResolvedPresentation;\n failedCdns?: string[];\n}\n\nexport interface SetupFailoverMonitorConfig {\n /** Failover policy (cooldown); defaults to `DEFAULT_FAILOVER_MONITOR_CONFIG`. */\n failover?: Partial<FailoverMonitorConfig>;\n}\n\n/**\n * Expire failed CDNs from `failedCdns` once their cooldown lapses, for the\n * resolved source.\n *\n * @example\n * const reactor = setupFailoverMonitor.setup({ state });\n */\nexport const setupFailoverMonitor = defineBehavior({\n stateKeys: ['presentation', 'failedCdns'],\n contextKeys: [],\n setup: ({\n state,\n config = {},\n }: {\n state: {\n presentation: ReadonlySignal<SetupFailoverMonitorState['presentation']>;\n failedCdns: Signal<SetupFailoverMonitorState['failedCdns']>;\n };\n config?: SetupFailoverMonitorConfig;\n }) => {\n const cooldownMs = config.failover?.cooldownMs ?? DEFAULT_FAILOVER_MONITOR_CONFIG.cooldownMs;\n // CDN id → its pending cooldown-removal timer. Shared by the `effects`\n // scheduler (adds a timer per newly-failed CDN) and the exit cleanup\n // (clears them). Per-source: emptied on exit, so it re-enters clean.\n const timers = new Map<string, ReturnType<typeof setTimeout>>();\n const derivedStateSignal = computed(() =>\n isResolvedPresentation(state.presentation.get())\n ? ('presentation-resolved' as const)\n : ('presentation-unresolved' as const)\n );\n\n return createMachineReactor({\n initial: 'presentation-unresolved',\n monitor: () => derivedStateSignal.get(),\n states: {\n 'presentation-unresolved': {},\n 'presentation-resolved': {\n // Cleanup-binds-to-setup: on exit (src unload + destroy) clear the\n // pending timers and reset `failedCdns` for the next source.\n entry: () => () => {\n timers.forEach((timer) => clearTimeout(timer));\n timers.clear();\n state.failedCdns.set(undefined);\n },\n effects: [\n () => {\n const failed = state.failedCdns.get() ?? [];\n failed.forEach((cdn) => {\n // Idempotent: a CDN already counting down keeps its original\n // deadline (re-failing it mid-cooldown doesn't extend it).\n if (timers.has(cdn)) return;\n const timer = setTimeout(() => {\n timers.delete(cdn);\n update(state.failedCdns, (current) => current?.filter((c) => c !== cdn));\n }, cooldownMs);\n timers.set(cdn, timer);\n });\n },\n ],\n },\n },\n });\n },\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AA+BA,MAAa,kCAAyD,EAKpE,YAAY,KACb;;;;;;;;AAmBD,MAAa,uBAAuB,eAAe;CACjD,WAAW,CAAC,gBAAgB,aAAa;CACzC,aAAa,EAAE;CACf,QAAQ,EACN,OACA,SAAS,EAAE,OAOP;EACJ,MAAM,aAAa,OAAO,UAAU,cAAc,gCAAgC;EAIlF,MAAM,yBAAS,IAAI,KAA4C;EAC/D,MAAM,qBAAqB,eACzB,uBAAuB,MAAM,aAAa,KAAK,CAAC,GAC3C,0BACA,0BACN;AAED,SAAO,qBAAqB;GAC1B,SAAS;GACT,eAAe,mBAAmB,KAAK;GACvC,QAAQ;IACN,2BAA2B,EAAE;IAC7B,yBAAyB;KAGvB,mBAAmB;AACjB,aAAO,SAAS,UAAU,aAAa,MAAM,CAAC;AAC9C,aAAO,OAAO;AACd,YAAM,WAAW,IAAI,KAAA,EAAU;;KAEjC,SAAS,OACD;AAEJ,OADe,MAAM,WAAW,KAAK,IAAI,EAAE,EACpC,SAAS,QAAQ;AAGtB,WAAI,OAAO,IAAI,IAAI,CAAE;OACrB,MAAM,QAAQ,iBAAiB;AAC7B,eAAO,OAAO,IAAI;AAClB,eAAO,MAAM,aAAa,YAAY,SAAS,QAAQ,MAAM,MAAM,IAAI,CAAC;UACvE,WAAW;AACd,cAAO,IAAI,KAAK,MAAM;QACtB;OAEL;KACF;IACF;GACF,CAAC;;CAEL,CAAC"}
@@ -0,0 +1,59 @@
1
+ import { computed, peek } from "../../core/signals/primitives.js";
2
+ import { defineBehavior } from "../../core/composition/create-composition.js";
3
+ import { effect } from "../../core/signals/effect.js";
4
+ import { isStandardPreload } from "../../media/utils/preload.js";
5
+ //#region src/playback/behaviors/sync-preload.ts
6
+ /**
7
+ * **Bidirectional sync between `state.preload` and `mediaElement.preload`.**
8
+ *
9
+ * Two effects:
10
+ * - **Read (DOM → state)** — on `context.mediaElement` swap or
11
+ * `state.presentation.url` change, copies `mediaElement.preload` into
12
+ * `state.preload` if it's a W3C value and `state.preload` isn't holding
13
+ * an extended (non-W3C) value. When the DOM has no W3C opinion and
14
+ * `state.preload` is undefined, backfills from `config.defaultPreload`
15
+ * (default-default `'metadata'`) so `state.preload` is never undefined
16
+ * in steady state.
17
+ * - **Write (state → DOM)** — on `state.preload` change or
18
+ * `context.mediaElement` swap, writes `state.preload` back to
19
+ * `mediaElement.preload` if the value is W3C.
20
+ *
21
+ * Extended values (e.g. `'canplay'`) written externally to `state.preload`
22
+ * are sticky: read won't overwrite them, write won't push them to the DOM.
23
+ * All writes are deduped to break echo loops and avoid spurious re-triggers
24
+ * downstream (notably `resolvePresentation`, which reads `state.preload`).
25
+ */
26
+ function syncPreloadSetup({ state, context, config }) {
27
+ const defaultPreload = config?.defaultPreload ?? "metadata";
28
+ const presentationUrl = computed(() => state.presentation.get()?.url);
29
+ const cleanupRead = effect(() => {
30
+ presentationUrl.get();
31
+ const mediaElement = context.mediaElement.get();
32
+ const current = peek(state.preload);
33
+ if (current !== void 0 && !isStandardPreload(current)) return;
34
+ const target = mediaElement && isStandardPreload(mediaElement.preload) ? mediaElement.preload : current === void 0 ? defaultPreload : void 0;
35
+ if (target === void 0 || target === current) return;
36
+ state.preload.set(target);
37
+ });
38
+ const cleanupWrite = effect(() => {
39
+ const next = state.preload.get();
40
+ const mediaElement = context.mediaElement.get();
41
+ if (!mediaElement) return;
42
+ if (!isStandardPreload(next)) return;
43
+ if (mediaElement.preload === next) return;
44
+ mediaElement.preload = next;
45
+ });
46
+ return () => {
47
+ cleanupRead();
48
+ cleanupWrite();
49
+ };
50
+ }
51
+ const syncPreload = defineBehavior({
52
+ stateKeys: ["preload", "presentation"],
53
+ contextKeys: ["mediaElement"],
54
+ setup: syncPreloadSetup
55
+ });
56
+ //#endregion
57
+ export { syncPreload };
58
+
59
+ //# sourceMappingURL=sync-preload.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-preload.js","names":[],"sources":["../../../../src/playback/behaviors/sync-preload.ts"],"sourcesContent":["/**\n * **Bidirectional sync between `state.preload` and `mediaElement.preload`.**\n *\n * Two effects:\n * - **Read (DOM → state)** — on `context.mediaElement` swap or\n * `state.presentation.url` change, copies `mediaElement.preload` into\n * `state.preload` if it's a W3C value and `state.preload` isn't holding\n * an extended (non-W3C) value. When the DOM has no W3C opinion and\n * `state.preload` is undefined, backfills from `config.defaultPreload`\n * (default-default `'metadata'`) so `state.preload` is never undefined\n * in steady state.\n * - **Write (state → DOM)** — on `state.preload` change or\n * `context.mediaElement` swap, writes `state.preload` back to\n * `mediaElement.preload` if the value is W3C.\n *\n * Extended values (e.g. `'canplay'`) written externally to `state.preload`\n * are sticky: read won't overwrite them, write won't push them to the DOM.\n * All writes are deduped to break echo loops and avoid spurious re-triggers\n * downstream (notably `resolvePresentation`, which reads `state.preload`).\n */\nimport { defineBehavior } from '../../core/composition/create-composition';\nimport { effect } from '../../core/signals/effect';\nimport { computed, peek, type ReadonlySignal, type Signal } from '../../core/signals/primitives';\nimport type { MediaElementLike } from '../../media/types';\nimport { DEFAULT_PRELOAD, isStandardPreload, type StandardPreload } from '../../media/utils/preload';\nimport type { PresentationState } from './resolve-presentation';\n\nexport interface SyncPreloadConfig {\n /**\n * Backfill applied to `state.preload` when neither the host element nor\n * external code has supplied a W3C value. Defaults to `'metadata'`.\n */\n defaultPreload?: StandardPreload;\n}\n\ntype State = Pick<PresentationState, 'preload' | 'presentation'>;\n\nfunction syncPreloadSetup({\n state,\n context,\n config,\n}: {\n state: {\n preload: Signal<State['preload']>;\n presentation: ReadonlySignal<State['presentation']>;\n };\n context: { mediaElement: ReadonlySignal<MediaElementLike | undefined> };\n config?: SyncPreloadConfig;\n}): () => void {\n const defaultPreload: StandardPreload = config?.defaultPreload ?? DEFAULT_PRELOAD;\n const presentationUrl = computed(() => state.presentation.get()?.url);\n\n // Read must be registered before write: effects re-run in registration\n // order (see core/signals/effect.ts → runPending). On a shared-signal\n // change (e.g. `context.mediaElement` swap), read fires first and gets to\n // claim `state.preload`, giving \"most-recent-wins on attach\" — a freshly\n // mounted <video> with a W3C `preload` attribute overrides whatever\n // `state.preload` previously held. Swap the order or split these into\n // separate behaviors and the \"most-recent-wins on attach\" test pins the\n // regression.\n const cleanupRead = effect(() => {\n presentationUrl.get();\n const mediaElement = context.mediaElement.get();\n // peek (not get): external writes to state.preload shouldn't re-trigger\n // this effect, only mediaElement / presentation-url changes should.\n const current = peek(state.preload);\n // Extended sticky: leave non-W3C values (e.g. 'canplay') alone.\n if (current !== undefined && !isStandardPreload(current)) return;\n\n const target: StandardPreload | undefined =\n mediaElement && isStandardPreload(mediaElement.preload)\n ? mediaElement.preload\n : current === undefined\n ? defaultPreload\n : undefined;\n if (target === undefined || target === current) return;\n state.preload.set(target);\n });\n\n const cleanupWrite = effect(() => {\n const next = state.preload.get();\n const mediaElement = context.mediaElement.get();\n if (!mediaElement) return;\n if (!isStandardPreload(next)) return;\n if (mediaElement.preload === next) return;\n mediaElement.preload = next;\n });\n\n return () => {\n cleanupRead();\n cleanupWrite();\n };\n}\n\nexport const syncPreload = defineBehavior({\n stateKeys: ['preload', 'presentation'],\n contextKeys: ['mediaElement'],\n setup: syncPreloadSetup,\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAqCA,SAAS,iBAAiB,EACxB,OACA,SACA,UAQa;CACb,MAAM,iBAAkC,QAAQ,kBAAA;CAChD,MAAM,kBAAkB,eAAe,MAAM,aAAa,KAAK,EAAE,IAAI;CAUrE,MAAM,cAAc,aAAa;AAC/B,kBAAgB,KAAK;EACrB,MAAM,eAAe,QAAQ,aAAa,KAAK;EAG/C,MAAM,UAAU,KAAK,MAAM,QAAQ;AAEnC,MAAI,YAAY,KAAA,KAAa,CAAC,kBAAkB,QAAQ,CAAE;EAE1D,MAAM,SACJ,gBAAgB,kBAAkB,aAAa,QAAQ,GACnD,aAAa,UACb,YAAY,KAAA,IACV,iBACA,KAAA;AACR,MAAI,WAAW,KAAA,KAAa,WAAW,QAAS;AAChD,QAAM,QAAQ,IAAI,OAAO;GACzB;CAEF,MAAM,eAAe,aAAa;EAChC,MAAM,OAAO,MAAM,QAAQ,KAAK;EAChC,MAAM,eAAe,QAAQ,aAAa,KAAK;AAC/C,MAAI,CAAC,aAAc;AACnB,MAAI,CAAC,kBAAkB,KAAK,CAAE;AAC9B,MAAI,aAAa,YAAY,KAAM;AACnC,eAAa,UAAU;GACvB;AAEF,cAAa;AACX,eAAa;AACb,gBAAc;;;AAIlB,MAAa,cAAc,eAAe;CACxC,WAAW,CAAC,WAAW,eAAe;CACtC,aAAa,CAAC,eAAe;CAC7B,OAAO;CACR,CAAC"}
@@ -0,0 +1,341 @@
1
+ import { computed, peek } from "../../core/signals/primitives.js";
2
+ import { defineBehavior } from "../../core/composition/create-composition.js";
3
+ import { createMachineReactor } from "../../core/reactors/create-machine-reactor.js";
4
+ import { isResolvedPresentation } from "../../media/types/index.js";
5
+ import { getTracksByType } from "../../media/utils/tracks.js";
6
+ import { getCdnId } from "../../media/utils/cdn.js";
7
+ import { DEFAULT_BANDWIDTH_CONFIG, getBandwidthEstimate } from "../../network/bandwidth-estimator.js";
8
+ import { DEFAULT_QUALITY_CONFIG, resolutionArea } from "../../media/abr/quality-selection.js";
9
+ import { matchesPartialTrack, pickTextTrackFromTracks } from "../../media/primitives/select-tracks.js";
10
+ /**
11
+ * Apply rules to a candidate list in order; the pick is the first survivor.
12
+ * Two responsibilities the rules don't carry: a rule that returns nothing is
13
+ * skipped (fall-through — a preference never empties the set), and once one
14
+ * survivor remains the chain stops (early-bail — later rules, including the
15
+ * bandwidth ranker, never run, so the effect doesn't subscribe to their
16
+ * signals while the choice is fixed).
17
+ *
18
+ * @param rules - Rules to apply, most authoritative first
19
+ * @param tracks - Candidate tracks
20
+ * @param deps - The behavior's `{ state, context, config }`, passed through to each rule
21
+ * @returns The surviving candidates, pick first
22
+ */
23
+ function applyRules(rules, tracks, deps) {
24
+ let current = tracks;
25
+ for (const rule of rules) {
26
+ const remaining = rule(current, deps);
27
+ if (remaining.length === 0) continue;
28
+ current = remaining;
29
+ if (current.length === 1) break;
30
+ }
31
+ return current;
32
+ }
33
+ /**
34
+ * Apply hard constraints to a candidate list — the pre-pass that runs before the
35
+ * rule chain. A constraint shares a rule's signature but its exclusion is
36
+ * *hard*: it removes the unplayable (a codec the environment can't decode, a CDN
37
+ * in failover cooldown) and a removed track is never attempted. Unlike
38
+ * `applyRules`, this never skips an empty result and never early-bails — every
39
+ * constraint always applies, and an empty survivor set is a real outcome
40
+ * ("nothing playable here"), not a fall-through. Because each constraint only
41
+ * removes, the order they run in can't change the result.
42
+ *
43
+ * @param constraints - Constraints to apply (pooled, order-independent)
44
+ * @param tracks - Candidate tracks
45
+ * @param deps - The behavior's `{ state, context, config }`, passed to each constraint
46
+ * @returns The playable survivors (possibly empty)
47
+ */
48
+ function applyConstraints(constraints, tracks, deps) {
49
+ let current = tracks;
50
+ for (const constraint of constraints) current = constraint(current, deps);
51
+ return current;
52
+ }
53
+ /**
54
+ * User intent — a soft filter. Narrows to tracks matching the partial-track
55
+ * selection in `user*TrackSelection`; an empty match falls through (the
56
+ * composer skips it) to the unfiltered set — e.g. a stale id from a previous
57
+ * source.
58
+ */
59
+ function filterByUserSelection(tracks, { state, config }) {
60
+ const key = config.userSelectionKey;
61
+ if (!key) return tracks;
62
+ const filter = state[key]?.get();
63
+ return filter ? tracks.filter((track) => matchesPartialTrack(track, filter)) : tracks;
64
+ }
65
+ /**
66
+ * Failed-CDN constraint — a *hard* filter (constraints pre-pass), shared by
67
+ * video and audio. Removes tracks served from a CDN currently in failover
68
+ * cooldown (`failedCdns`, written by the failover monitor). Removed tracks are never
69
+ * attempted; the scope then narrows to the next surviving CDN in `cdnPriority`,
70
+ * and snaps back to the primary once it leaves cooldown.
71
+ *
72
+ * Passes everything through when there's no `failedCdns` signal/value. When it
73
+ * prunes *every* track (all CDNs cooled down), the empty result is preserved
74
+ * (per `applyConstraints`) — "nothing playable," which clears the selection (no
75
+ * pick); a later CDN recovery refills the candidate set and re-picks.
76
+ */
77
+ function excludeFailedCdns(tracks, { state, config }) {
78
+ const failed = state.failedCdns?.get();
79
+ if (!failed?.length) return tracks;
80
+ const getCdnId$1 = config.getCdnId ?? getCdnId;
81
+ const failedSet = new Set(failed);
82
+ return tracks.filter((track) => !failedSet.has(getCdnId$1(track.url)));
83
+ }
84
+ /**
85
+ * Capability constraint — a *hard* filter (constraints pre-pass), shared by
86
+ * video and audio. Removes renditions this environment can't decode, probed via
87
+ * the injected `canPlayTrack` (codec → `MediaSource.isTypeSupported`). Moving
88
+ * the check here — before selection — means an unplayable variant (e.g. HEVC on
89
+ * a browser without HEVC) is pruned upstream and never picked, instead of
90
+ * surviving into the pipeline to fail late at `createSourceBuffer`. That late
91
+ * throw stays as a defensive structural guarantee; with this constraint it
92
+ * should rarely fire.
93
+ *
94
+ * Passes everything through when there's no `canPlayTrack` probe (a composition
95
+ * that didn't wire it, or DOM-free tests). When it prunes *every* track (no
96
+ * decodable rendition), the empty result is preserved (per `applyConstraints`)
97
+ * — "nothing playable," so the behavior clears the selection (no pick) and the
98
+ * late `createSourceBuffer` check stays as the backstop.
99
+ */
100
+ function excludeUnplayableTracks(tracks, { config }) {
101
+ const canPlay = config.canPlayTrack;
102
+ if (!canPlay) return tracks;
103
+ return tracks.filter((track) => canPlay(track));
104
+ }
105
+ /**
106
+ * Active-CDN scope — a soft filter, shared by video and audio. Narrows to the
107
+ * highest-priority CDN in `cdnPriority` (owned by `deriveCdnPriority`) that
108
+ * still has tracks, so every track type stays on one CDN. A redundant-streams
109
+ * source lists the same renditions on multiple hosts; this keeps the pick on one
110
+ * host rather than letting the ranker drift across them.
111
+ *
112
+ * "Active" is derived, not stored: constraints run before the rule chain, so a
113
+ * failed CDN's tracks are already pruned by the time this runs — "first CDN with
114
+ * survivors" *is* the active CDN, and it falls through to the next on failover
115
+ * (and snaps back to the primary when it recovers). Content steering reorders
116
+ * `cdnPriority`; this rule just honors the order.
117
+ *
118
+ * Soft-filter semantics: passes through when there's no `cdnPriority` signal/value
119
+ * (no preference) or when nothing matches (`applyRules` skips an empty result).
120
+ * Non-redundant sources have one CDN, so the narrow is a no-op.
121
+ *
122
+ * The CDN-id derivation defaults to origin-based `getCdnId`, overridable via the
123
+ * `getCdnId` config — it must match the one `deriveCdnPriority` used to build
124
+ * `cdnPriority`, or no track's CDN would ever equal an entry.
125
+ */
126
+ function preferActiveCdn(tracks, { state, config }) {
127
+ const cdnPriority = state.cdnPriority?.get();
128
+ if (!cdnPriority?.length) return tracks;
129
+ const getCdnId$2 = config.getCdnId ?? getCdnId;
130
+ for (const cdn of cdnPriority) {
131
+ const tracksUsingCdn = tracks.filter((track) => getCdnId$2(track.url) === cdn);
132
+ if (tracksUsingCdn.length) return tracksUsingCdn;
133
+ }
134
+ return tracks;
135
+ }
136
+ /**
137
+ * Bandwidth ranking — the terminal sort, shared by video and audio. Orders by
138
+ * the throughput estimate: tracks within the bandwidth threshold first
139
+ * (fitting), highest bitrate first; then over-threshold tracks, least-over
140
+ * first. The head is the best-quality track that fits, falling back to the
141
+ * smallest over-throughput track when nothing fits.
142
+ *
143
+ * Hysteresis without temporal state: the current track's effective bitrate is
144
+ * boosted by `upgradeMargin` in the fitting sort, so a higher track only
145
+ * outranks it once it clears `current.bitrate * upgradeMargin` (no flapping on
146
+ * marginal bandwidth gains). Downgrades fall out for free — a current track
147
+ * over the threshold isn't in the fitting set to be boosted, so the best fit (a
148
+ * downgrade) wins immediately. Equal-bitrate tracks break by resolution (higher
149
+ * `width × height` first), so an equal-bitrate ladder never picks a lower-
150
+ * quality rendition by manifest order; audio tracks carry no dimensions, so
151
+ * they area-compare equal and a stable sort keeps their candidate order (e.g.
152
+ * same-bitrate language variants). Early-bail skips this rule when a prior one
153
+ * narrowed to a single track, so the estimate is neither read nor subscribed
154
+ * while that holds.
155
+ */
156
+ function rankByBandwidth(tracks, { state, config }) {
157
+ const safetyMargin = config.quality?.safetyMargin ?? DEFAULT_QUALITY_CONFIG.safetyMargin;
158
+ const upgradeMargin = config.quality?.upgradeMargin ?? DEFAULT_QUALITY_CONFIG.upgradeMargin;
159
+ const initialBandwidth = config.initialBandwidth ?? 5e6;
160
+ const bandwidthConfig = {
161
+ ...DEFAULT_BANDWIDTH_CONFIG,
162
+ ...config.bandwidth
163
+ };
164
+ if (!state.bandwidthState) console.debug("[track-switching] rankByBandwidth: no bandwidthState signal in composition; ranking on initialBandwidth");
165
+ const threshold = getBandwidthEstimate(state.bandwidthState?.get(), initialBandwidth, bandwidthConfig) * safetyMargin;
166
+ const currentId = state[config.selectionKey].get();
167
+ const bitrate = (track) => track.bandwidth ?? 0;
168
+ const rank = (track) => track.id === currentId ? bitrate(track) * upgradeMargin : bitrate(track);
169
+ const fitting = tracks.filter((track) => bitrate(track) <= threshold).sort((a, b) => rank(b) - rank(a) || resolutionArea(b) - resolutionArea(a));
170
+ const over = tracks.filter((track) => bitrate(track) > threshold).sort((a, b) => bitrate(a) - bitrate(b) || resolutionArea(b) - resolutionArea(a));
171
+ return [...fitting, ...over];
172
+ }
173
+ /**
174
+ * Default final pick: the chain head. `applyRules` never narrows to nothing and
175
+ * early-bails to a single survivor, so video and audio always converge to a
176
+ * track and the head is the pick.
177
+ */
178
+ function selectChainHead(candidates) {
179
+ return candidates[0].id;
180
+ }
181
+ /**
182
+ * Terminal pick for text — the `resolveSelection` the text variant supplies.
183
+ * Resolves the standing `userTextTrackSelection` intent against the chain's
184
+ * survivors (already CDN-failover-pruned and active-CDN-scoped):
185
+ *
186
+ * - `'off'` → no selection (clear the slot). Sticky through re-evaluation, so a
187
+ * live refresh or failover re-run can't re-assert a default.
188
+ * - explicit `Partial<TextTrack>` → narrow to the match (language-based). A
189
+ * stale pick whose match is gone (e.g. the language dropped on a source
190
+ * change) falls through to the default policy.
191
+ * - auto (`undefined`) → the opt-in default policy (`preferredSubtitleLanguage`
192
+ * → `DEFAULT=YES + AUTOSELECT=YES` → none), shared with `pickTextTrack`.
193
+ *
194
+ * Returning `undefined` is a real outcome (captions are opt-in), which is why the
195
+ * text variant relies on `setupTrackSwitching`'s no-selection seam.
196
+ */
197
+ function pickResolvedTextTrack(candidates, { state, config }) {
198
+ const intent = state.userTextTrackSelection?.get();
199
+ if (intent === "off") return void 0;
200
+ if (intent) {
201
+ const matched = candidates.filter((track) => matchesPartialTrack(track, intent));
202
+ if (matched.length) return matched[0].id;
203
+ }
204
+ return pickTextTrackFromTracks(candidates, config);
205
+ }
206
+ function setupTrackSwitching(deps) {
207
+ const { state, config } = deps;
208
+ const { selectionKey, getTracks, rules, resolveSelection = selectChainHead } = config;
209
+ const derivedStateSignal = computed(() => isResolvedPresentation(state.presentation.get()) ? "presentation-resolved" : "presentation-unresolved");
210
+ const candidateSet = computed(() => {
211
+ const presentation = state.presentation.get();
212
+ if (!isResolvedPresentation(presentation)) return [];
213
+ return applyConstraints(config.constraints ?? [], getTracks(presentation), deps);
214
+ }, { equals: (a, b) => a.length === b.length && a.every((track) => b.some((other) => other.id === track.id)) });
215
+ return createMachineReactor({
216
+ initial: "presentation-unresolved",
217
+ monitor: () => derivedStateSignal.get(),
218
+ states: {
219
+ "presentation-unresolved": {},
220
+ "presentation-resolved": {
221
+ entry: () => () => state[selectionKey].set(void 0),
222
+ effects: [() => {
223
+ const tracks = candidateSet.get();
224
+ if (!tracks.length) {
225
+ const presentation = peek(state.presentation);
226
+ if (isResolvedPresentation(presentation) && getTracks(presentation).length > 0) {
227
+ console.error(`[track-switching] every ${selectionKey} candidate was filtered out by constraints; clearing selection`);
228
+ state[selectionKey].set(void 0);
229
+ }
230
+ return;
231
+ }
232
+ const candidates = applyRules(rules, tracks, deps);
233
+ if (!candidates.length) {
234
+ console.error("[track-switching] applyRules returned no candidates");
235
+ return;
236
+ }
237
+ state[selectionKey].set(resolveSelection(candidates, deps));
238
+ }]
239
+ }
240
+ }
241
+ });
242
+ }
243
+ /**
244
+ * Manage `selectedVideoTrackId`: pick a default on src load, dynamically
245
+ * adjust based on bandwidth, clear on src unload. Honors
246
+ * `userVideoTrackSelection` as a partial-track constraint on candidates;
247
+ * short-circuits ABR when the constraint narrows to a single track.
248
+ *
249
+ * @example
250
+ * const reactor = switchVideoTrack.setup({ state });
251
+ */
252
+ const switchVideoTrack = defineBehavior({
253
+ stateKeys: ["presentation", "selectedVideoTrackId"],
254
+ contextKeys: [],
255
+ setup: ({ state, config, ...otherProps }) => setupTrackSwitching({
256
+ ...otherProps,
257
+ state,
258
+ config: {
259
+ ...config,
260
+ selectionKey: "selectedVideoTrackId",
261
+ userSelectionKey: "userVideoTrackSelection",
262
+ getTracks: (presentation) => getTracksByType(presentation, "video"),
263
+ constraints: [excludeFailedCdns, excludeUnplayableTracks],
264
+ rules: [
265
+ filterByUserSelection,
266
+ preferActiveCdn,
267
+ rankByBandwidth
268
+ ]
269
+ }
270
+ })
271
+ });
272
+ /**
273
+ * Manage `selectedAudioTrackId`: pick a default on src load, narrow by
274
+ * `userAudioTrackSelection` filter, re-pick on filter change, clear on
275
+ * src unload.
276
+ *
277
+ * Mid-stream flush on language switch is handled by the segment-loader's
278
+ * `planTasks` (see `playback/actors/dom/segment-loader.ts`) — not this
279
+ * behavior. Same split as the video pipeline: slot owner writes; loader
280
+ * orchestrates segment + flush plans.
281
+ *
282
+ * @example
283
+ * const reactor = switchAudioTrack.setup({ state });
284
+ */
285
+ const switchAudioTrack = defineBehavior({
286
+ stateKeys: ["presentation", "selectedAudioTrackId"],
287
+ contextKeys: [],
288
+ setup: ({ state, config, ...otherProps }) => setupTrackSwitching({
289
+ ...otherProps,
290
+ state,
291
+ config: {
292
+ ...config,
293
+ selectionKey: "selectedAudioTrackId",
294
+ userSelectionKey: "userAudioTrackSelection",
295
+ getTracks: (presentation) => getTracksByType(presentation, "audio"),
296
+ constraints: [excludeFailedCdns, excludeUnplayableTracks],
297
+ rules: [
298
+ filterByUserSelection,
299
+ preferActiveCdn,
300
+ rankByBandwidth
301
+ ]
302
+ }
303
+ })
304
+ });
305
+ /**
306
+ * Manage `selectedTextTrackId` as the single-writer **output** of standing user
307
+ * intent (`userTextTrackSelection`) resolved against the playable, CDN-scoped
308
+ * text renditions: clear on src unload; re-resolve when a CDN fails or recovers.
309
+ *
310
+ * Unlike video/audio, the selection is *optional* — captions are opt-in and the
311
+ * user can turn them off — so the chain skips the bandwidth ranker and the shared
312
+ * user-selection filter, and supplies a text-specific terminal
313
+ * (`pickResolvedTextTrack`) that may resolve to no-selection via
314
+ * `setupTrackSwitching`'s `resolveSelection` seam. Constraints are failed-CDN only
315
+ * (`excludeUnplayableTracks`/`canPlayTrack` is MSE-based — the wrong probe for
316
+ * text, whose playability is SPF-parser support); the active-CDN scope co-locates
317
+ * captions with the surviving CDN on failover.
318
+ *
319
+ * @example
320
+ * const reactor = switchTextTrack.setup({ state, config: { preferredSubtitleLanguage: 'en' } });
321
+ */
322
+ const switchTextTrack = defineBehavior({
323
+ stateKeys: ["presentation", "selectedTextTrackId"],
324
+ contextKeys: [],
325
+ setup: ({ state, config, ...otherProps }) => setupTrackSwitching({
326
+ ...otherProps,
327
+ state,
328
+ config: {
329
+ ...config,
330
+ selectionKey: "selectedTextTrackId",
331
+ getTracks: (presentation) => getTracksByType(presentation, "text"),
332
+ constraints: [excludeFailedCdns],
333
+ rules: [preferActiveCdn],
334
+ resolveSelection: pickResolvedTextTrack
335
+ }
336
+ })
337
+ });
338
+ //#endregion
339
+ export { switchAudioTrack, switchTextTrack, switchVideoTrack };
340
+
341
+ //# sourceMappingURL=track-switching.js.map