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

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 (461) hide show
  1. package/dist/{dev/core → default/core/actors}/create-machine-actor.js +3 -3
  2. package/dist/default/core/actors/create-machine-actor.js.map +1 -0
  3. package/dist/default/core/{create-transition-actor.js → actors/create-transition-actor.js} +3 -3
  4. package/dist/default/core/actors/create-transition-actor.js.map +1 -0
  5. package/dist/default/core/composition/create-composition.js +85 -0
  6. package/dist/default/core/composition/create-composition.js.map +1 -0
  7. package/dist/default/core/composition/share-signals.js +37 -0
  8. package/dist/default/core/composition/share-signals.js.map +1 -0
  9. package/dist/{dev/core → default/core/reactors}/create-machine-reactor.js +4 -4
  10. package/dist/default/core/reactors/create-machine-reactor.js.map +1 -0
  11. package/dist/default/core/signals/primitives.js +33 -11
  12. package/dist/default/core/signals/primitives.js.map +1 -1
  13. package/dist/default/core/{task.js → tasks/task.js} +2 -2
  14. package/dist/default/core/tasks/task.js.map +1 -0
  15. package/dist/default/dom.js +9 -10
  16. package/dist/default/hls.js +3 -0
  17. package/dist/default/index.js +12 -3
  18. package/dist/default/index.js.map +1 -1
  19. package/dist/default/media/abr/quality-selection.js +83 -0
  20. package/dist/default/media/abr/quality-selection.js.map +1 -0
  21. package/dist/default/{core → media}/buffer/back-buffer.js +2 -2
  22. package/dist/default/media/buffer/back-buffer.js.map +1 -0
  23. package/dist/{dev/core → default/media}/buffer/forward-buffer.js +14 -2
  24. package/dist/default/media/buffer/forward-buffer.js.map +1 -0
  25. package/dist/{dev/dom/media → default/media/dom/mse}/append-segment.js +1 -1
  26. package/dist/default/media/dom/mse/append-segment.js.map +1 -0
  27. package/dist/default/{dom/media → media/dom/mse}/buffer-flusher.js +1 -1
  28. package/dist/default/media/dom/mse/buffer-flusher.js.map +1 -0
  29. package/dist/default/media/dom/mse/duration.js +78 -0
  30. package/dist/default/media/dom/mse/duration.js.map +1 -0
  31. package/dist/default/media/dom/mse/end-of-stream.js +20 -0
  32. package/dist/default/media/dom/mse/end-of-stream.js.map +1 -0
  33. package/dist/default/{dom/media → media/dom/mse}/mediasource-setup.js +63 -18
  34. package/dist/default/media/dom/mse/mediasource-setup.js.map +1 -0
  35. package/dist/default/{dom/text/parse-vtt-segment.js → media/dom/text/resolve-vtt-segment.js} +5 -5
  36. package/dist/default/media/dom/text/resolve-vtt-segment.js.map +1 -0
  37. package/dist/default/media/dom/text/text-track-slots.js +69 -0
  38. package/dist/default/media/dom/text/text-track-slots.js.map +1 -0
  39. package/dist/{dev/core → default/media}/hls/parse-attributes.js +1 -1
  40. package/dist/default/media/hls/parse-attributes.js.map +1 -0
  41. package/dist/{dev/core → default/media}/hls/parse-media-playlist.js +1 -1
  42. package/dist/default/media/hls/parse-media-playlist.js.map +1 -0
  43. package/dist/{dev/core → default/media}/hls/parse-multivariant.js +2 -2
  44. package/dist/default/media/hls/parse-multivariant.js.map +1 -0
  45. package/dist/default/{core → media}/hls/resolve-url.js +1 -1
  46. package/dist/default/media/hls/resolve-url.js.map +1 -0
  47. package/dist/default/media/primitives/select-tracks.js +45 -0
  48. package/dist/default/media/primitives/select-tracks.js.map +1 -0
  49. package/dist/default/media/types/index.js +36 -0
  50. package/dist/default/media/types/index.js.map +1 -0
  51. package/dist/default/media/utils/preload.js +22 -0
  52. package/dist/default/media/utils/preload.js.map +1 -0
  53. package/dist/default/media/utils/track-selection.js +47 -0
  54. package/dist/default/media/utils/track-selection.js.map +1 -0
  55. package/dist/default/media/utils/tracks.js +103 -0
  56. package/dist/default/media/utils/tracks.js.map +1 -0
  57. package/dist/default/{core/abr → network}/bandwidth-estimator.js +6 -5
  58. package/dist/default/network/bandwidth-estimator.js.map +1 -0
  59. package/dist/{dev/dom → default}/network/chunked-stream-iterable.js +1 -1
  60. package/dist/default/network/chunked-stream-iterable.js.map +1 -0
  61. package/dist/{dev/core/abr → default/network}/ewma.js +1 -1
  62. package/dist/default/network/ewma.js.map +1 -0
  63. package/dist/default/network/fetch.js +109 -0
  64. package/dist/default/network/fetch.js.map +1 -0
  65. package/dist/{dev/dom/features/segment-loader-actor.js → default/playback/actors/dom/segment-loader.js} +24 -15
  66. package/dist/default/playback/actors/dom/segment-loader.js.map +1 -0
  67. package/dist/{dev/dom/media/source-buffer-actor.js → default/playback/actors/dom/source-buffer.js} +8 -8
  68. package/dist/default/playback/actors/dom/source-buffer.js.map +1 -0
  69. package/dist/{dev/dom/features/text-tracks-actor.js → default/playback/actors/dom/text-tracks.js} +7 -3
  70. package/dist/default/playback/actors/dom/text-tracks.js.map +1 -0
  71. package/dist/default/playback/actors/text-track-segment-loader.js +135 -0
  72. package/dist/default/playback/actors/text-track-segment-loader.js.map +1 -0
  73. package/dist/default/playback/behaviors/calculate-presentation-duration.js +58 -0
  74. package/dist/default/playback/behaviors/calculate-presentation-duration.js.map +1 -0
  75. package/dist/default/playback/behaviors/dom/end-of-stream.js +178 -0
  76. package/dist/default/playback/behaviors/dom/end-of-stream.js.map +1 -0
  77. package/dist/default/playback/behaviors/dom/load-segments.js +177 -0
  78. package/dist/default/playback/behaviors/dom/load-segments.js.map +1 -0
  79. package/dist/default/playback/behaviors/dom/setup-buffer-actors.js +173 -0
  80. package/dist/default/playback/behaviors/dom/setup-buffer-actors.js.map +1 -0
  81. package/dist/default/playback/behaviors/dom/setup-mediasource.js +89 -0
  82. package/dist/default/playback/behaviors/dom/setup-mediasource.js.map +1 -0
  83. package/dist/default/playback/behaviors/dom/setup-text-track-actors.js +54 -0
  84. package/dist/default/playback/behaviors/dom/setup-text-track-actors.js.map +1 -0
  85. package/dist/default/playback/behaviors/dom/sync-text-tracks.js +99 -0
  86. package/dist/default/playback/behaviors/dom/sync-text-tracks.js.map +1 -0
  87. package/dist/default/playback/behaviors/dom/track-current-time.js +46 -0
  88. package/dist/default/playback/behaviors/dom/track-current-time.js.map +1 -0
  89. package/dist/default/playback/behaviors/dom/track-load-triggers.js +94 -0
  90. package/dist/default/playback/behaviors/dom/track-load-triggers.js.map +1 -0
  91. package/dist/default/playback/behaviors/dom/track-playback-rate.js +37 -0
  92. package/dist/default/playback/behaviors/dom/track-playback-rate.js.map +1 -0
  93. package/dist/default/playback/behaviors/dom/update-mediasource-duration.js +90 -0
  94. package/dist/default/playback/behaviors/dom/update-mediasource-duration.js.map +1 -0
  95. package/dist/default/playback/behaviors/quality-switching.js +96 -0
  96. package/dist/default/playback/behaviors/quality-switching.js.map +1 -0
  97. package/dist/default/playback/behaviors/resolve-presentation.js +85 -0
  98. package/dist/default/playback/behaviors/resolve-presentation.js.map +1 -0
  99. package/dist/default/playback/behaviors/resolve-track.js +97 -0
  100. package/dist/default/playback/behaviors/resolve-track.js.map +1 -0
  101. package/dist/default/playback/behaviors/select-tracks.js +132 -0
  102. package/dist/default/playback/behaviors/select-tracks.js.map +1 -0
  103. package/dist/default/playback/behaviors/sync-preload.js +59 -0
  104. package/dist/default/playback/behaviors/sync-preload.js.map +1 -0
  105. package/dist/default/playback/behaviors/track-types.js +61 -0
  106. package/dist/default/playback/behaviors/track-types.js.map +1 -0
  107. package/dist/default/{dom/playback-engine → playback/engines/hls}/adapter.js +33 -26
  108. package/dist/default/playback/engines/hls/adapter.js.map +1 -0
  109. package/dist/default/playback/engines/hls/engine.js +102 -0
  110. package/dist/default/playback/engines/hls/engine.js.map +1 -0
  111. package/dist/dev/core/{actor.d.ts → actors/actor.d.ts} +2 -2
  112. package/dist/dev/core/actors/actor.d.ts.map +1 -0
  113. package/dist/dev/core/actors/create-machine-actor.d.ts +125 -0
  114. package/dist/dev/core/actors/create-machine-actor.d.ts.map +1 -0
  115. package/dist/{default/core → dev/core/actors}/create-machine-actor.js +3 -3
  116. package/dist/dev/core/actors/create-machine-actor.js.map +1 -0
  117. package/dist/dev/core/actors/create-transition-actor.d.ts +42 -0
  118. package/dist/dev/core/actors/create-transition-actor.d.ts.map +1 -0
  119. package/dist/dev/core/{create-transition-actor.js → actors/create-transition-actor.js} +3 -3
  120. package/dist/dev/core/actors/create-transition-actor.js.map +1 -0
  121. package/dist/dev/core/composition/create-composition.d.ts +274 -0
  122. package/dist/dev/core/composition/create-composition.d.ts.map +1 -0
  123. package/dist/dev/core/composition/create-composition.js +85 -0
  124. package/dist/dev/core/composition/create-composition.js.map +1 -0
  125. package/dist/dev/core/composition/share-signals.d.ts +45 -0
  126. package/dist/dev/core/composition/share-signals.d.ts.map +1 -0
  127. package/dist/dev/core/composition/share-signals.js +37 -0
  128. package/dist/dev/core/composition/share-signals.js.map +1 -0
  129. package/dist/dev/core/machine.d.ts +0 -1
  130. package/dist/dev/core/machine.d.ts.map +1 -1
  131. package/dist/dev/core/reactors/create-machine-reactor.d.ts +93 -0
  132. package/dist/dev/core/reactors/create-machine-reactor.d.ts.map +1 -0
  133. package/dist/{default/core → dev/core/reactors}/create-machine-reactor.js +4 -4
  134. package/dist/dev/core/reactors/create-machine-reactor.js.map +1 -0
  135. package/dist/dev/core/signals/primitives.d.ts +38 -1
  136. package/dist/dev/core/signals/primitives.d.ts.map +1 -1
  137. package/dist/dev/core/signals/primitives.js +33 -11
  138. package/dist/dev/core/signals/primitives.js.map +1 -1
  139. package/dist/dev/core/tasks/task.d.ts +115 -0
  140. package/dist/dev/core/tasks/task.d.ts.map +1 -0
  141. package/dist/dev/core/{task.js → tasks/task.js} +2 -2
  142. package/dist/dev/core/tasks/task.js.map +1 -0
  143. package/dist/dev/dom.d.ts +9 -10
  144. package/dist/dev/dom.js +9 -10
  145. package/dist/dev/hls.d.ts +3 -0
  146. package/dist/dev/hls.js +3 -0
  147. package/dist/dev/index.d.ts +15 -3
  148. package/dist/dev/index.d.ts.map +1 -1
  149. package/dist/dev/index.js +12 -3
  150. package/dist/dev/index.js.map +1 -1
  151. package/dist/dev/media/abr/quality-selection.d.ts +22 -0
  152. package/dist/dev/media/abr/quality-selection.d.ts.map +1 -0
  153. package/dist/dev/media/abr/quality-selection.js +83 -0
  154. package/dist/dev/media/abr/quality-selection.js.map +1 -0
  155. package/dist/dev/media/buffer/back-buffer.d.ts +14 -0
  156. package/dist/dev/media/buffer/back-buffer.d.ts.map +1 -0
  157. package/dist/dev/{core → media}/buffer/back-buffer.js +2 -2
  158. package/dist/dev/media/buffer/back-buffer.js.map +1 -0
  159. package/dist/dev/media/buffer/forward-buffer.d.ts +14 -0
  160. package/dist/dev/media/buffer/forward-buffer.d.ts.map +1 -0
  161. package/dist/{default/core → dev/media}/buffer/forward-buffer.js +14 -2
  162. package/dist/dev/media/buffer/forward-buffer.js.map +1 -0
  163. package/dist/dev/{dom/media → media/dom/mse}/append-segment.d.ts +1 -1
  164. package/dist/dev/media/dom/mse/append-segment.d.ts.map +1 -0
  165. package/dist/{default/dom/media → dev/media/dom/mse}/append-segment.js +1 -1
  166. package/dist/dev/media/dom/mse/append-segment.js.map +1 -0
  167. package/dist/dev/{dom/media → media/dom/mse}/buffer-flusher.d.ts +1 -1
  168. package/dist/dev/media/dom/mse/buffer-flusher.d.ts.map +1 -0
  169. package/dist/dev/{dom/media → media/dom/mse}/buffer-flusher.js +1 -1
  170. package/dist/dev/media/dom/mse/buffer-flusher.js.map +1 -0
  171. package/dist/dev/media/dom/mse/duration.js +78 -0
  172. package/dist/dev/media/dom/mse/duration.js.map +1 -0
  173. package/dist/dev/media/dom/mse/end-of-stream.js +20 -0
  174. package/dist/dev/media/dom/mse/end-of-stream.js.map +1 -0
  175. package/dist/dev/{dom/media → media/dom/mse}/mediasource-setup.js +63 -18
  176. package/dist/dev/media/dom/mse/mediasource-setup.js.map +1 -0
  177. package/dist/dev/media/dom/text/resolve-vtt-segment.d.ts +12 -0
  178. package/dist/dev/media/dom/text/resolve-vtt-segment.d.ts.map +1 -0
  179. package/dist/dev/{dom/text/parse-vtt-segment.js → media/dom/text/resolve-vtt-segment.js} +5 -5
  180. package/dist/dev/media/dom/text/resolve-vtt-segment.js.map +1 -0
  181. package/dist/dev/media/dom/text/text-track-slots.d.ts +31 -0
  182. package/dist/dev/media/dom/text/text-track-slots.d.ts.map +1 -0
  183. package/dist/dev/media/dom/text/text-track-slots.js +69 -0
  184. package/dist/dev/media/dom/text/text-track-slots.js.map +1 -0
  185. package/dist/{default/core → dev/media}/hls/parse-attributes.js +1 -1
  186. package/dist/dev/media/hls/parse-attributes.js.map +1 -0
  187. package/dist/{default/core → dev/media}/hls/parse-media-playlist.js +1 -1
  188. package/dist/dev/media/hls/parse-media-playlist.js.map +1 -0
  189. package/dist/{default/core → dev/media}/hls/parse-multivariant.js +2 -2
  190. package/dist/dev/media/hls/parse-multivariant.js.map +1 -0
  191. package/dist/dev/{core → media}/hls/resolve-url.js +1 -1
  192. package/dist/dev/media/hls/resolve-url.js.map +1 -0
  193. package/dist/dev/media/primitives/select-tracks.js +45 -0
  194. package/dist/dev/media/primitives/select-tracks.js.map +1 -0
  195. package/dist/dev/{core → media}/types/index.d.ts +30 -2
  196. package/dist/dev/media/types/index.d.ts.map +1 -0
  197. package/dist/dev/media/types/index.js +36 -0
  198. package/dist/dev/media/types/index.js.map +1 -0
  199. package/dist/dev/media/utils/preload.js +22 -0
  200. package/dist/dev/media/utils/preload.js.map +1 -0
  201. package/dist/dev/media/utils/track-selection.js +47 -0
  202. package/dist/dev/media/utils/track-selection.js.map +1 -0
  203. package/dist/dev/media/utils/tracks.js +103 -0
  204. package/dist/dev/media/utils/tracks.js.map +1 -0
  205. package/dist/dev/{core/abr → network}/bandwidth-estimator.d.ts +17 -2
  206. package/dist/dev/network/bandwidth-estimator.d.ts.map +1 -0
  207. package/dist/dev/{core/abr → network}/bandwidth-estimator.js +6 -5
  208. package/dist/dev/network/bandwidth-estimator.js.map +1 -0
  209. package/dist/{default/dom → dev}/network/chunked-stream-iterable.js +1 -1
  210. package/dist/dev/network/chunked-stream-iterable.js.map +1 -0
  211. package/dist/{default/core/abr → dev/network}/ewma.js +1 -1
  212. package/dist/dev/network/ewma.js.map +1 -0
  213. package/dist/dev/network/fetch.js +109 -0
  214. package/dist/dev/network/fetch.js.map +1 -0
  215. package/dist/dev/playback/actors/dom/segment-loader.d.ts +36 -0
  216. package/dist/dev/playback/actors/dom/segment-loader.d.ts.map +1 -0
  217. package/dist/{default/dom/features/segment-loader-actor.js → dev/playback/actors/dom/segment-loader.js} +24 -15
  218. package/dist/dev/playback/actors/dom/segment-loader.js.map +1 -0
  219. package/dist/dev/{dom/media/source-buffer-actor.d.ts → playback/actors/dom/source-buffer.d.ts} +5 -5
  220. package/dist/dev/playback/actors/dom/source-buffer.d.ts.map +1 -0
  221. package/dist/{default/dom/media/source-buffer-actor.js → dev/playback/actors/dom/source-buffer.js} +8 -8
  222. package/dist/dev/playback/actors/dom/source-buffer.js.map +1 -0
  223. package/dist/dev/playback/actors/dom/text-tracks.d.ts +1 -0
  224. package/dist/{default/dom/features/text-tracks-actor.js → dev/playback/actors/dom/text-tracks.js} +7 -3
  225. package/dist/dev/playback/actors/dom/text-tracks.js.map +1 -0
  226. package/dist/dev/playback/actors/text-track-segment-loader.d.ts +59 -0
  227. package/dist/dev/playback/actors/text-track-segment-loader.d.ts.map +1 -0
  228. package/dist/dev/playback/actors/text-track-segment-loader.js +135 -0
  229. package/dist/dev/playback/actors/text-track-segment-loader.js.map +1 -0
  230. package/dist/dev/playback/actors/text-tracks.d.ts +42 -0
  231. package/dist/dev/playback/actors/text-tracks.d.ts.map +1 -0
  232. package/dist/dev/playback/behaviors/calculate-presentation-duration.d.ts +17 -0
  233. package/dist/dev/playback/behaviors/calculate-presentation-duration.d.ts.map +1 -0
  234. package/dist/dev/playback/behaviors/calculate-presentation-duration.js +58 -0
  235. package/dist/dev/playback/behaviors/calculate-presentation-duration.js.map +1 -0
  236. package/dist/dev/playback/behaviors/dom/end-of-stream.js +178 -0
  237. package/dist/dev/playback/behaviors/dom/end-of-stream.js.map +1 -0
  238. package/dist/dev/playback/behaviors/dom/load-segments.d.ts +62 -0
  239. package/dist/dev/playback/behaviors/dom/load-segments.d.ts.map +1 -0
  240. package/dist/dev/playback/behaviors/dom/load-segments.js +177 -0
  241. package/dist/dev/playback/behaviors/dom/load-segments.js.map +1 -0
  242. package/dist/dev/playback/behaviors/dom/setup-buffer-actors.js +173 -0
  243. package/dist/dev/playback/behaviors/dom/setup-buffer-actors.js.map +1 -0
  244. package/dist/dev/playback/behaviors/dom/setup-mediasource.js +89 -0
  245. package/dist/dev/playback/behaviors/dom/setup-mediasource.js.map +1 -0
  246. package/dist/dev/playback/behaviors/dom/setup-text-track-actors.d.ts +31 -0
  247. package/dist/dev/playback/behaviors/dom/setup-text-track-actors.d.ts.map +1 -0
  248. package/dist/dev/playback/behaviors/dom/setup-text-track-actors.js +54 -0
  249. package/dist/dev/playback/behaviors/dom/setup-text-track-actors.js.map +1 -0
  250. package/dist/dev/playback/behaviors/dom/sync-text-tracks.js +99 -0
  251. package/dist/dev/playback/behaviors/dom/sync-text-tracks.js.map +1 -0
  252. package/dist/dev/playback/behaviors/dom/track-current-time.d.ts +34 -0
  253. package/dist/dev/playback/behaviors/dom/track-current-time.d.ts.map +1 -0
  254. package/dist/dev/playback/behaviors/dom/track-current-time.js +46 -0
  255. package/dist/dev/playback/behaviors/dom/track-current-time.js.map +1 -0
  256. package/dist/dev/playback/behaviors/dom/track-load-triggers.d.ts +48 -0
  257. package/dist/dev/playback/behaviors/dom/track-load-triggers.d.ts.map +1 -0
  258. package/dist/dev/playback/behaviors/dom/track-load-triggers.js +94 -0
  259. package/dist/dev/playback/behaviors/dom/track-load-triggers.js.map +1 -0
  260. package/dist/dev/playback/behaviors/dom/track-playback-rate.d.ts +34 -0
  261. package/dist/dev/playback/behaviors/dom/track-playback-rate.d.ts.map +1 -0
  262. package/dist/dev/playback/behaviors/dom/track-playback-rate.js +37 -0
  263. package/dist/dev/playback/behaviors/dom/track-playback-rate.js.map +1 -0
  264. package/dist/dev/playback/behaviors/dom/update-mediasource-duration.js +90 -0
  265. package/dist/dev/playback/behaviors/dom/update-mediasource-duration.js.map +1 -0
  266. package/dist/dev/playback/behaviors/quality-switching.js +96 -0
  267. package/dist/dev/playback/behaviors/quality-switching.js.map +1 -0
  268. package/dist/dev/playback/behaviors/resolve-presentation.d.ts +7 -0
  269. package/dist/dev/playback/behaviors/resolve-presentation.d.ts.map +1 -0
  270. package/dist/dev/playback/behaviors/resolve-presentation.js +85 -0
  271. package/dist/dev/playback/behaviors/resolve-presentation.js.map +1 -0
  272. package/dist/dev/playback/behaviors/resolve-track.js +97 -0
  273. package/dist/dev/playback/behaviors/resolve-track.js.map +1 -0
  274. package/dist/dev/playback/behaviors/select-tracks.js +132 -0
  275. package/dist/dev/playback/behaviors/select-tracks.js.map +1 -0
  276. package/dist/dev/playback/behaviors/sync-preload.js +59 -0
  277. package/dist/dev/playback/behaviors/sync-preload.js.map +1 -0
  278. package/dist/dev/playback/behaviors/track-types.js +61 -0
  279. package/dist/dev/playback/behaviors/track-types.js.map +1 -0
  280. package/dist/dev/{dom/playback-engine → playback/engines/hls}/adapter.d.ts +13 -12
  281. package/dist/dev/playback/engines/hls/adapter.d.ts.map +1 -0
  282. package/dist/dev/{dom/playback-engine → playback/engines/hls}/adapter.js +33 -26
  283. package/dist/dev/playback/engines/hls/adapter.js.map +1 -0
  284. package/dist/dev/playback/engines/hls/engine.d.ts +177 -0
  285. package/dist/dev/playback/engines/hls/engine.d.ts.map +1 -0
  286. package/dist/dev/playback/engines/hls/engine.js +102 -0
  287. package/dist/dev/playback/engines/hls/engine.js.map +1 -0
  288. package/package.json +7 -7
  289. package/dist/default/core/abr/bandwidth-estimator.js.map +0 -1
  290. package/dist/default/core/abr/ewma.js.map +0 -1
  291. package/dist/default/core/abr/quality-selection.js +0 -53
  292. package/dist/default/core/abr/quality-selection.js.map +0 -1
  293. package/dist/default/core/buffer/back-buffer.js.map +0 -1
  294. package/dist/default/core/buffer/forward-buffer.js.map +0 -1
  295. package/dist/default/core/create-machine-actor.js.map +0 -1
  296. package/dist/default/core/create-machine-reactor.js.map +0 -1
  297. package/dist/default/core/create-transition-actor.js.map +0 -1
  298. package/dist/default/core/features/calculate-presentation-duration.js +0 -53
  299. package/dist/default/core/features/calculate-presentation-duration.js.map +0 -1
  300. package/dist/default/core/features/quality-switching.js +0 -69
  301. package/dist/default/core/features/quality-switching.js.map +0 -1
  302. package/dist/default/core/features/resolve-presentation.js +0 -73
  303. package/dist/default/core/features/resolve-presentation.js.map +0 -1
  304. package/dist/default/core/features/resolve-track.js +0 -66
  305. package/dist/default/core/features/resolve-track.js.map +0 -1
  306. package/dist/default/core/features/select-tracks.js +0 -130
  307. package/dist/default/core/features/select-tracks.js.map +0 -1
  308. package/dist/default/core/features/sync-preload-attribute.js +0 -26
  309. package/dist/default/core/features/sync-preload-attribute.js.map +0 -1
  310. package/dist/default/core/hls/parse-attributes.js.map +0 -1
  311. package/dist/default/core/hls/parse-media-playlist.js.map +0 -1
  312. package/dist/default/core/hls/parse-multivariant.js.map +0 -1
  313. package/dist/default/core/hls/resolve-url.js.map +0 -1
  314. package/dist/default/core/task.js.map +0 -1
  315. package/dist/default/core/types/index.js +0 -15
  316. package/dist/default/core/types/index.js.map +0 -1
  317. package/dist/default/core/utils/generate-id.js +0 -21
  318. package/dist/default/core/utils/generate-id.js.map +0 -1
  319. package/dist/default/core/utils/track-selection.js +0 -39
  320. package/dist/default/core/utils/track-selection.js.map +0 -1
  321. package/dist/default/dom/features/end-of-stream.js +0 -145
  322. package/dist/default/dom/features/end-of-stream.js.map +0 -1
  323. package/dist/default/dom/features/load-segments.js +0 -192
  324. package/dist/default/dom/features/load-segments.js.map +0 -1
  325. package/dist/default/dom/features/load-text-track-cues.js +0 -104
  326. package/dist/default/dom/features/load-text-track-cues.js.map +0 -1
  327. package/dist/default/dom/features/segment-loader-actor.js.map +0 -1
  328. package/dist/default/dom/features/setup-mediasource.js +0 -48
  329. package/dist/default/dom/features/setup-mediasource.js.map +0 -1
  330. package/dist/default/dom/features/setup-sourcebuffer.js +0 -78
  331. package/dist/default/dom/features/setup-sourcebuffer.js.map +0 -1
  332. package/dist/default/dom/features/sync-text-tracks.js +0 -95
  333. package/dist/default/dom/features/sync-text-tracks.js.map +0 -1
  334. package/dist/default/dom/features/text-track-segment-loader-actor.js +0 -55
  335. package/dist/default/dom/features/text-track-segment-loader-actor.js.map +0 -1
  336. package/dist/default/dom/features/text-tracks-actor.js.map +0 -1
  337. package/dist/default/dom/features/track-current-time.js +0 -49
  338. package/dist/default/dom/features/track-current-time.js.map +0 -1
  339. package/dist/default/dom/features/track-playback-initiated.js +0 -66
  340. package/dist/default/dom/features/track-playback-initiated.js.map +0 -1
  341. package/dist/default/dom/features/track-playback-rate.js +0 -32
  342. package/dist/default/dom/features/track-playback-rate.js.map +0 -1
  343. package/dist/default/dom/features/update-duration.js +0 -78
  344. package/dist/default/dom/features/update-duration.js.map +0 -1
  345. package/dist/default/dom/media/append-segment.js.map +0 -1
  346. package/dist/default/dom/media/buffer-flusher.js.map +0 -1
  347. package/dist/default/dom/media/mediasource-setup.js.map +0 -1
  348. package/dist/default/dom/media/source-buffer-actor.js.map +0 -1
  349. package/dist/default/dom/network/chunked-stream-iterable.js.map +0 -1
  350. package/dist/default/dom/network/fetch.js +0 -54
  351. package/dist/default/dom/network/fetch.js.map +0 -1
  352. package/dist/default/dom/playback-engine/adapter.js.map +0 -1
  353. package/dist/default/dom/playback-engine/engine.js +0 -142
  354. package/dist/default/dom/playback-engine/engine.js.map +0 -1
  355. package/dist/default/dom/playback-engine.js +0 -4
  356. package/dist/default/dom/text/parse-vtt-segment.js.map +0 -1
  357. package/dist/dev/core/abr/bandwidth-estimator.d.ts.map +0 -1
  358. package/dist/dev/core/abr/bandwidth-estimator.js.map +0 -1
  359. package/dist/dev/core/abr/ewma.js.map +0 -1
  360. package/dist/dev/core/abr/quality-selection.js +0 -53
  361. package/dist/dev/core/abr/quality-selection.js.map +0 -1
  362. package/dist/dev/core/actor.d.ts.map +0 -1
  363. package/dist/dev/core/buffer/back-buffer.js.map +0 -1
  364. package/dist/dev/core/buffer/forward-buffer.js.map +0 -1
  365. package/dist/dev/core/create-machine-actor.d.ts +0 -12
  366. package/dist/dev/core/create-machine-actor.d.ts.map +0 -1
  367. package/dist/dev/core/create-machine-actor.js.map +0 -1
  368. package/dist/dev/core/create-machine-reactor.d.ts +0 -8
  369. package/dist/dev/core/create-machine-reactor.d.ts.map +0 -1
  370. package/dist/dev/core/create-machine-reactor.js.map +0 -1
  371. package/dist/dev/core/create-transition-actor.d.ts +0 -23
  372. package/dist/dev/core/create-transition-actor.d.ts.map +0 -1
  373. package/dist/dev/core/create-transition-actor.js.map +0 -1
  374. package/dist/dev/core/features/calculate-presentation-duration.js +0 -53
  375. package/dist/dev/core/features/calculate-presentation-duration.js.map +0 -1
  376. package/dist/dev/core/features/quality-switching.js +0 -69
  377. package/dist/dev/core/features/quality-switching.js.map +0 -1
  378. package/dist/dev/core/features/resolve-presentation.js +0 -73
  379. package/dist/dev/core/features/resolve-presentation.js.map +0 -1
  380. package/dist/dev/core/features/resolve-track.js +0 -66
  381. package/dist/dev/core/features/resolve-track.js.map +0 -1
  382. package/dist/dev/core/features/select-tracks.js +0 -130
  383. package/dist/dev/core/features/select-tracks.js.map +0 -1
  384. package/dist/dev/core/features/sync-preload-attribute.js +0 -26
  385. package/dist/dev/core/features/sync-preload-attribute.js.map +0 -1
  386. package/dist/dev/core/hls/parse-attributes.js.map +0 -1
  387. package/dist/dev/core/hls/parse-media-playlist.js.map +0 -1
  388. package/dist/dev/core/hls/parse-multivariant.js.map +0 -1
  389. package/dist/dev/core/hls/resolve-url.js.map +0 -1
  390. package/dist/dev/core/task.js.map +0 -1
  391. package/dist/dev/core/types/index.d.ts.map +0 -1
  392. package/dist/dev/core/types/index.js +0 -15
  393. package/dist/dev/core/types/index.js.map +0 -1
  394. package/dist/dev/core/utils/generate-id.js +0 -21
  395. package/dist/dev/core/utils/generate-id.js.map +0 -1
  396. package/dist/dev/core/utils/track-selection.d.ts +0 -16
  397. package/dist/dev/core/utils/track-selection.d.ts.map +0 -1
  398. package/dist/dev/core/utils/track-selection.js +0 -39
  399. package/dist/dev/core/utils/track-selection.js.map +0 -1
  400. package/dist/dev/dom/features/end-of-stream.js +0 -145
  401. package/dist/dev/dom/features/end-of-stream.js.map +0 -1
  402. package/dist/dev/dom/features/load-segments.d.ts +0 -72
  403. package/dist/dev/dom/features/load-segments.d.ts.map +0 -1
  404. package/dist/dev/dom/features/load-segments.js +0 -192
  405. package/dist/dev/dom/features/load-segments.js.map +0 -1
  406. package/dist/dev/dom/features/load-text-track-cues.d.ts +0 -85
  407. package/dist/dev/dom/features/load-text-track-cues.d.ts.map +0 -1
  408. package/dist/dev/dom/features/load-text-track-cues.js +0 -104
  409. package/dist/dev/dom/features/load-text-track-cues.js.map +0 -1
  410. package/dist/dev/dom/features/segment-loader-actor.d.ts +0 -1
  411. package/dist/dev/dom/features/segment-loader-actor.js.map +0 -1
  412. package/dist/dev/dom/features/setup-mediasource.js +0 -48
  413. package/dist/dev/dom/features/setup-mediasource.js.map +0 -1
  414. package/dist/dev/dom/features/setup-sourcebuffer.d.ts +0 -9
  415. package/dist/dev/dom/features/setup-sourcebuffer.d.ts.map +0 -1
  416. package/dist/dev/dom/features/setup-sourcebuffer.js +0 -78
  417. package/dist/dev/dom/features/setup-sourcebuffer.js.map +0 -1
  418. package/dist/dev/dom/features/sync-text-tracks.js +0 -95
  419. package/dist/dev/dom/features/sync-text-tracks.js.map +0 -1
  420. package/dist/dev/dom/features/text-track-segment-loader-actor.d.ts +0 -12
  421. package/dist/dev/dom/features/text-track-segment-loader-actor.d.ts.map +0 -1
  422. package/dist/dev/dom/features/text-track-segment-loader-actor.js +0 -55
  423. package/dist/dev/dom/features/text-track-segment-loader-actor.js.map +0 -1
  424. package/dist/dev/dom/features/text-tracks-actor.d.ts +0 -31
  425. package/dist/dev/dom/features/text-tracks-actor.d.ts.map +0 -1
  426. package/dist/dev/dom/features/text-tracks-actor.js.map +0 -1
  427. package/dist/dev/dom/features/track-current-time.d.ts +0 -40
  428. package/dist/dev/dom/features/track-current-time.d.ts.map +0 -1
  429. package/dist/dev/dom/features/track-current-time.js +0 -49
  430. package/dist/dev/dom/features/track-current-time.js.map +0 -1
  431. package/dist/dev/dom/features/track-playback-initiated.d.ts +0 -46
  432. package/dist/dev/dom/features/track-playback-initiated.d.ts.map +0 -1
  433. package/dist/dev/dom/features/track-playback-initiated.js +0 -66
  434. package/dist/dev/dom/features/track-playback-initiated.js.map +0 -1
  435. package/dist/dev/dom/features/track-playback-rate.d.ts +0 -34
  436. package/dist/dev/dom/features/track-playback-rate.d.ts.map +0 -1
  437. package/dist/dev/dom/features/track-playback-rate.js +0 -32
  438. package/dist/dev/dom/features/track-playback-rate.js.map +0 -1
  439. package/dist/dev/dom/features/update-duration.js +0 -78
  440. package/dist/dev/dom/features/update-duration.js.map +0 -1
  441. package/dist/dev/dom/media/append-segment.d.ts.map +0 -1
  442. package/dist/dev/dom/media/append-segment.js.map +0 -1
  443. package/dist/dev/dom/media/buffer-flusher.d.ts.map +0 -1
  444. package/dist/dev/dom/media/buffer-flusher.js.map +0 -1
  445. package/dist/dev/dom/media/mediasource-setup.js.map +0 -1
  446. package/dist/dev/dom/media/source-buffer-actor.d.ts.map +0 -1
  447. package/dist/dev/dom/media/source-buffer-actor.js.map +0 -1
  448. package/dist/dev/dom/network/chunked-stream-iterable.js.map +0 -1
  449. package/dist/dev/dom/network/fetch.js +0 -54
  450. package/dist/dev/dom/network/fetch.js.map +0 -1
  451. package/dist/dev/dom/playback-engine/adapter.d.ts.map +0 -1
  452. package/dist/dev/dom/playback-engine/adapter.js.map +0 -1
  453. package/dist/dev/dom/playback-engine/engine.d.ts +0 -124
  454. package/dist/dev/dom/playback-engine/engine.d.ts.map +0 -1
  455. package/dist/dev/dom/playback-engine/engine.js +0 -142
  456. package/dist/dev/dom/playback-engine/engine.js.map +0 -1
  457. package/dist/dev/dom/playback-engine.d.ts +0 -4
  458. package/dist/dev/dom/playback-engine.js +0 -4
  459. package/dist/dev/dom/text/parse-vtt-segment.d.ts +0 -12
  460. package/dist/dev/dom/text/parse-vtt-segment.d.ts.map +0 -1
  461. package/dist/dev/dom/text/parse-vtt-segment.js.map +0 -1
@@ -0,0 +1,89 @@
1
+ import { computed } 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 { attachMediaSource, createMediaSource, waitForMediaSourceOpen } from "../../../media/dom/mse/mediasource-setup.js";
6
+ //#region src/playback/behaviors/dom/setup-mediasource.ts
7
+ /**
8
+ * **Own the MediaSource lifecycle for the current source.** When a resolved
9
+ * presentation and a mediaElement are both in scope, creates a MediaSource,
10
+ * attaches it to the element, waits for `'open'`, and publishes it on
11
+ * `context.mediaSource`. On source change or behavior destroy, detaches the
12
+ * MediaSource and clears the slot so the next source starts fresh.
13
+ *
14
+ * Single-positive-state reactor (`'preconditions-unmet'` ↔ `'mediasource-attached'`):
15
+ * state derivation gates on `mediaElement + isResolvedPresentation`. Riding the
16
+ * resolver's resolved/unresolved lifecycle makes direct URL replacement
17
+ * structural — `resolvePresentation` routes the presentation back through
18
+ * unresolved on URL change, which drives this reactor through
19
+ * `'preconditions-unmet'` so the entry's state-exit cleanup detaches the old
20
+ * MediaSource before the new one is built.
21
+ *
22
+ * The entry resolves preconditions in sequence before publishing:
23
+ *
24
+ * 1. **Create + attach** — `createMediaSource` + `attachMediaSource` run
25
+ * synchronously on entry. The `detach` closure returned by
26
+ * `attachMediaSource` is captured for state-exit cleanup, so the cleanup
27
+ * is always bound to its setup even if the wait below is aborted.
28
+ * 2. **Wait for `'open'`** — `waitForMediaSourceOpen` defers until the first
29
+ * `sourceopen` event (or any readyState transition out of `'closed'`).
30
+ * 3. **Publish on `'open'`** — re-check `readyState === 'open'` after the
31
+ * await (covers `'ended'` / `'closed'` race) before writing to
32
+ * `context.mediaSource`. Downstream `setupVideoBufferActors` /
33
+ * `setupAudioBufferActors` call `addSourceBuffer` directly, which
34
+ * throws on non-open, so publish-only-when-open is the load-bearing
35
+ * contract.
36
+ *
37
+ * State-exit cleanup aborts the in-flight wait, detaches the MediaSource,
38
+ * and clears `context.mediaSource`. Order: abort first (prevents a late
39
+ * publish racing the slot clear), then detach, then clear.
40
+ *
41
+ * Sole writer of `context.mediaSource`; other MSE behaviors
42
+ * (`setupVideoBufferActors`, `setupAudioBufferActors`,
43
+ * `updateMediaSourceDuration`, `endOfStream`, `loadVideoSegments`) only
44
+ * read.
45
+ */
46
+ function deriveState(presentation, mediaElement) {
47
+ if (!mediaElement || !isResolvedPresentation(presentation)) return "preconditions-unmet";
48
+ return "mediasource-attached";
49
+ }
50
+ function setupMediaSourceSetup({ state, context }) {
51
+ const derivedStateSignal = computed(() => deriveState(state.presentation.get(), context.mediaElement.get()));
52
+ return createMachineReactor({
53
+ initial: "preconditions-unmet",
54
+ monitor: () => derivedStateSignal.get(),
55
+ states: {
56
+ "preconditions-unmet": {},
57
+ "mediasource-attached": { entry: () => {
58
+ const mediaElement = context.mediaElement.get();
59
+ const controller = new AbortController();
60
+ const mediaSource = createMediaSource({ preferManaged: true });
61
+ const { detach } = attachMediaSource(mediaSource, mediaElement);
62
+ const publishWhenOpen = async () => {
63
+ await waitForMediaSourceOpen(mediaSource, controller.signal);
64
+ if (controller.signal.aborted) return;
65
+ if (mediaSource.readyState !== "open") {
66
+ console.warn(`[setupMediaSource] MediaSource transitioned to '${mediaSource.readyState}' before first 'sourceopen' — slot left unpublished; recoverable on next source reset.`);
67
+ return;
68
+ }
69
+ context.mediaSource.set(mediaSource);
70
+ };
71
+ publishWhenOpen().catch((err) => console.error("[setupMediaSource] failed to publish MediaSource:", err));
72
+ return () => {
73
+ controller.abort();
74
+ detach();
75
+ context.mediaSource.set(void 0);
76
+ };
77
+ } }
78
+ }
79
+ });
80
+ }
81
+ const setupMediaSource = defineBehavior({
82
+ stateKeys: ["presentation"],
83
+ contextKeys: ["mediaElement", "mediaSource"],
84
+ setup: setupMediaSourceSetup
85
+ });
86
+ //#endregion
87
+ export { setupMediaSource };
88
+
89
+ //# sourceMappingURL=setup-mediasource.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup-mediasource.js","names":[],"sources":["../../../../../src/playback/behaviors/dom/setup-mediasource.ts"],"sourcesContent":["/**\n * **Own the MediaSource lifecycle for the current source.** When a resolved\n * presentation and a mediaElement are both in scope, creates a MediaSource,\n * attaches it to the element, waits for `'open'`, and publishes it on\n * `context.mediaSource`. On source change or behavior destroy, detaches the\n * MediaSource and clears the slot so the next source starts fresh.\n *\n * Single-positive-state reactor (`'preconditions-unmet'` ↔ `'mediasource-attached'`):\n * state derivation gates on `mediaElement + isResolvedPresentation`. Riding the\n * resolver's resolved/unresolved lifecycle makes direct URL replacement\n * structural — `resolvePresentation` routes the presentation back through\n * unresolved on URL change, which drives this reactor through\n * `'preconditions-unmet'` so the entry's state-exit cleanup detaches the old\n * MediaSource before the new one is built.\n *\n * The entry resolves preconditions in sequence before publishing:\n *\n * 1. **Create + attach** — `createMediaSource` + `attachMediaSource` run\n * synchronously on entry. The `detach` closure returned by\n * `attachMediaSource` is captured for state-exit cleanup, so the cleanup\n * is always bound to its setup even if the wait below is aborted.\n * 2. **Wait for `'open'`** — `waitForMediaSourceOpen` defers until the first\n * `sourceopen` event (or any readyState transition out of `'closed'`).\n * 3. **Publish on `'open'`** — re-check `readyState === 'open'` after the\n * await (covers `'ended'` / `'closed'` race) before writing to\n * `context.mediaSource`. Downstream `setupVideoBufferActors` /\n * `setupAudioBufferActors` call `addSourceBuffer` directly, which\n * throws on non-open, so publish-only-when-open is the load-bearing\n * contract.\n *\n * State-exit cleanup aborts the in-flight wait, detaches the MediaSource,\n * and clears `context.mediaSource`. Order: abort first (prevents a late\n * publish racing the slot clear), then detach, then clear.\n *\n * Sole writer of `context.mediaSource`; other MSE behaviors\n * (`setupVideoBufferActors`, `setupAudioBufferActors`,\n * `updateMediaSourceDuration`, `endOfStream`, `loadVideoSegments`) only\n * read.\n */\nimport { defineBehavior } from '../../../core/composition/create-composition';\nimport type { Reactor } from '../../../core/reactors/create-machine-reactor';\nimport { createMachineReactor } from '../../../core/reactors/create-machine-reactor';\nimport { computed, type ReadonlySignal, type Signal } from '../../../core/signals/primitives';\nimport { attachMediaSource, createMediaSource, waitForMediaSourceOpen } from '../../../media/dom/mse/mediasource-setup';\nimport { isResolvedPresentation, type MaybeResolvedPresentation } from '../../../media/types';\n\n/**\n * State shape required for MediaSource setup.\n */\nexport interface MediaSourceState {\n presentation?: MaybeResolvedPresentation;\n}\n\n/**\n * Context shape for MediaSource setup.\n */\nexport interface MediaSourceContext {\n mediaElement?: HTMLMediaElement | undefined;\n mediaSource?: MediaSource;\n}\n\ntype MediaSourceFsmState = 'preconditions-unmet' | 'mediasource-attached';\n\nfunction deriveState(\n presentation: MaybeResolvedPresentation | undefined,\n mediaElement: HTMLMediaElement | undefined\n): MediaSourceFsmState {\n if (!mediaElement || !isResolvedPresentation(presentation)) return 'preconditions-unmet';\n return 'mediasource-attached';\n}\n\nfunction setupMediaSourceSetup({\n state,\n context,\n}: {\n state: {\n presentation: ReadonlySignal<MediaSourceState['presentation']>;\n };\n context: {\n mediaElement: ReadonlySignal<MediaSourceContext['mediaElement']>;\n mediaSource: Signal<MediaSourceContext['mediaSource']>;\n };\n}): Reactor<MediaSourceFsmState | 'destroying' | 'destroyed'> {\n const derivedStateSignal = computed(() => deriveState(state.presentation.get(), context.mediaElement.get()));\n\n return createMachineReactor<MediaSourceFsmState>({\n initial: 'preconditions-unmet',\n monitor: () => derivedStateSignal.get(),\n states: {\n 'preconditions-unmet': {},\n\n 'mediasource-attached': {\n // entry body is auto-untracked. deriveState handles source resets via\n // the resolver's resolved/unresolved transitions; this entry creates\n // and attaches the MediaSource, awaits `'open'`, publishes on\n // context, and binds detach + slot clear to state exit + destroy.\n entry: () => {\n const mediaElement = context.mediaElement.get()!;\n const controller = new AbortController();\n\n const mediaSource = createMediaSource({ preferManaged: true });\n // Sync attach: the returned `detach` closes over the element +\n // object-URL captured at this moment, so state-exit cleanup tears\n // down exactly this attachment regardless of how the wait below\n // resolves.\n const { detach } = attachMediaSource(mediaSource, mediaElement);\n\n const publishWhenOpen = async () => {\n await waitForMediaSourceOpen(mediaSource, controller.signal);\n if (controller.signal.aborted) return;\n // `waitForMediaSourceOpen` resolves on any readyState transition\n // out of `'closed'`; if we landed in `'ended'` / `'closed'`\n // instead of `'open'`, the attach window is gone and we leave\n // the slot unpublished. The session is dead within this source\n // either way — publishing wouldn't help because\n // `setupVideoBufferActors` / `setupAudioBufferActors` calls\n // `addSourceBuffer` which throws on non-open. The next\n // source-reset re-enters this state with a fresh MediaSource\n // and recovers. Warn so the case is at least diagnosable; in\n // practice it requires the MediaSource to close/end before its\n // very first `sourceopen`, which a freshly attached MS\n // shouldn't do.\n if (mediaSource.readyState !== 'open') {\n console.warn(\n `[setupMediaSource] MediaSource transitioned to '${mediaSource.readyState}' before first 'sourceopen' — slot left unpublished; recoverable on next source reset.`\n );\n return;\n }\n context.mediaSource.set(mediaSource);\n };\n\n publishWhenOpen().catch((err) => console.error('[setupMediaSource] failed to publish MediaSource:', err));\n\n return () => {\n // Order matters: abort the wait first so a late publish can't\n // race the slot clear; then detach to release the element; then\n // clear the slot so downstream behaviors see no MediaSource.\n controller.abort();\n detach();\n context.mediaSource.set(undefined);\n };\n },\n },\n },\n });\n}\n\nexport const setupMediaSource = defineBehavior({\n stateKeys: ['presentation'],\n contextKeys: ['mediaElement', 'mediaSource'],\n setup: setupMediaSourceSetup,\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+DA,SAAS,YACP,cACA,cACqB;AACrB,KAAI,CAAC,gBAAgB,CAAC,uBAAuB,aAAa,CAAE,QAAO;AACnE,QAAO;;AAGT,SAAS,sBAAsB,EAC7B,OACA,WAS4D;CAC5D,MAAM,qBAAqB,eAAe,YAAY,MAAM,aAAa,KAAK,EAAE,QAAQ,aAAa,KAAK,CAAC,CAAC;AAE5G,QAAO,qBAA0C;EAC/C,SAAS;EACT,eAAe,mBAAmB,KAAK;EACvC,QAAQ;GACN,uBAAuB,EAAE;GAEzB,wBAAwB,EAKtB,aAAa;IACX,MAAM,eAAe,QAAQ,aAAa,KAAK;IAC/C,MAAM,aAAa,IAAI,iBAAiB;IAExC,MAAM,cAAc,kBAAkB,EAAE,eAAe,MAAM,CAAC;IAK9D,MAAM,EAAE,WAAW,kBAAkB,aAAa,aAAa;IAE/D,MAAM,kBAAkB,YAAY;AAClC,WAAM,uBAAuB,aAAa,WAAW,OAAO;AAC5D,SAAI,WAAW,OAAO,QAAS;AAa/B,SAAI,YAAY,eAAe,QAAQ;AACrC,cAAQ,KACN,mDAAmD,YAAY,WAAW,wFAC3E;AACD;;AAEF,aAAQ,YAAY,IAAI,YAAY;;AAGtC,qBAAiB,CAAC,OAAO,QAAQ,QAAQ,MAAM,qDAAqD,IAAI,CAAC;AAEzG,iBAAa;AAIX,gBAAW,OAAO;AAClB,aAAQ;AACR,aAAQ,YAAY,IAAI,KAAA,EAAU;;MAGvC;GACF;EACF,CAAC;;AAGJ,MAAa,mBAAmB,eAAe;CAC7C,WAAW,CAAC,eAAe;CAC3B,aAAa,CAAC,gBAAgB,cAAc;CAC5C,OAAO;CACR,CAAC"}
@@ -0,0 +1,54 @@
1
+ import { defineBehavior } from "../../../core/composition/create-composition.js";
2
+ import { effect } from "../../../core/signals/effect.js";
3
+ import { createTextTracksActor } from "../../actors/dom/text-tracks.js";
4
+ import { createTextTrackSegmentLoaderActor } from "../../actors/text-track-segment-loader.js";
5
+ //#region src/playback/behaviors/dom/setup-text-track-actors.ts
6
+ /**
7
+ * **Own the TextTracks actor pair for the current `mediaElement`.** When a
8
+ * `mediaElement` is in scope, creates the `TextTracksActor` (bound to the
9
+ * element's `textTracks`) and the `TextTrackSegmentLoaderActor` (bound to
10
+ * that actor + the injected cue resolver), and publishes both on
11
+ * `context`. On element identity change or behavior destroy, destroys both
12
+ * actors and clears the slots.
13
+ *
14
+ * Single-resource synchronous create/destroy driven by one signal — the
15
+ * simple-effect form is the right shape (per `behaviors.md` → "Where both
16
+ * shapes are legitimate": criterion 4b applies; sole writer of
17
+ * `textTracksActor` / `textTrackSegmentLoaderActor`, the effect's cleanup
18
+ * return handles destroy + slot clear structurally).
19
+ *
20
+ * Pairs with the `loadTextTrackSegments` behavior (a per-type variant of
21
+ * `setupSegmentLoading` in `load-segments.ts`), which only reads
22
+ * `textTrackSegmentLoaderActor`. The cue resolver is injected via
23
+ * `config` so this behavior owns the DOM-bound part of the text-track
24
+ * pipeline.
25
+ */
26
+ function setupTextTrackActorsSetup({ context, config }) {
27
+ return effect(() => {
28
+ const mediaElement = context.mediaElement.get();
29
+ if (!mediaElement) return;
30
+ const textTracksActor = createTextTracksActor(mediaElement);
31
+ const textTrackSegmentLoaderActor = createTextTrackSegmentLoaderActor(textTracksActor, config.resolveTextTrackSegment, { forwardBuffer: config.forwardBuffer });
32
+ context.textTracksActor.set(textTracksActor);
33
+ context.textTrackSegmentLoaderActor.set(textTrackSegmentLoaderActor);
34
+ return () => {
35
+ textTracksActor.destroy();
36
+ textTrackSegmentLoaderActor.destroy();
37
+ context.textTracksActor.set(void 0);
38
+ context.textTrackSegmentLoaderActor.set(void 0);
39
+ };
40
+ });
41
+ }
42
+ const setupTextTrackActors = defineBehavior({
43
+ stateKeys: [],
44
+ contextKeys: [
45
+ "mediaElement",
46
+ "textTracksActor",
47
+ "textTrackSegmentLoaderActor"
48
+ ],
49
+ setup: setupTextTrackActorsSetup
50
+ });
51
+ //#endregion
52
+ export { setupTextTrackActors };
53
+
54
+ //# sourceMappingURL=setup-text-track-actors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup-text-track-actors.js","names":[],"sources":["../../../../../src/playback/behaviors/dom/setup-text-track-actors.ts"],"sourcesContent":["/**\n * **Own the TextTracks actor pair for the current `mediaElement`.** When a\n * `mediaElement` is in scope, creates the `TextTracksActor` (bound to the\n * element's `textTracks`) and the `TextTrackSegmentLoaderActor` (bound to\n * that actor + the injected cue resolver), and publishes both on\n * `context`. On element identity change or behavior destroy, destroys both\n * actors and clears the slots.\n *\n * Single-resource synchronous create/destroy driven by one signal — the\n * simple-effect form is the right shape (per `behaviors.md` → \"Where both\n * shapes are legitimate\": criterion 4b applies; sole writer of\n * `textTracksActor` / `textTrackSegmentLoaderActor`, the effect's cleanup\n * return handles destroy + slot clear structurally).\n *\n * Pairs with the `loadTextTrackSegments` behavior (a per-type variant of\n * `setupSegmentLoading` in `load-segments.ts`), which only reads\n * `textTrackSegmentLoaderActor`. The cue resolver is injected via\n * `config` so this behavior owns the DOM-bound part of the text-track\n * pipeline.\n */\nimport { defineBehavior } from '../../../core/composition/create-composition';\nimport { effect } from '../../../core/signals/effect';\nimport type { ReadonlySignal, Signal } from '../../../core/signals/primitives';\nimport { createTextTracksActor } from '../../actors/dom/text-tracks';\nimport {\n createTextTrackSegmentLoaderActor,\n type TextTrackSegmentLoaderActor,\n type TextTrackSegmentLoaderActorConfig,\n type TextTrackSegmentResolver,\n} from '../../actors/text-track-segment-loader';\nimport type { TextTracksActor } from '../../actors/text-tracks';\n\nexport interface TextTrackActorsContext {\n mediaElement?: HTMLMediaElement | undefined;\n textTracksActor?: TextTracksActor<VTTCue> | undefined;\n textTrackSegmentLoaderActor?: TextTrackSegmentLoaderActor | undefined;\n}\n\nexport interface TextTrackActorsConfig extends TextTrackSegmentLoaderActorConfig {\n resolveTextTrackSegment: TextTrackSegmentResolver<VTTCue>;\n}\n\nfunction setupTextTrackActorsSetup({\n context,\n config,\n}: {\n context: {\n mediaElement: ReadonlySignal<TextTrackActorsContext['mediaElement']>;\n textTracksActor: Signal<TextTrackActorsContext['textTracksActor']>;\n textTrackSegmentLoaderActor: Signal<TextTrackActorsContext['textTrackSegmentLoaderActor']>;\n };\n config: TextTrackActorsConfig;\n}): () => void {\n return effect(() => {\n const mediaElement = context.mediaElement.get();\n if (!mediaElement) return;\n\n const textTracksActor = createTextTracksActor(mediaElement);\n const textTrackSegmentLoaderActor = createTextTrackSegmentLoaderActor(\n textTracksActor,\n config.resolveTextTrackSegment,\n { forwardBuffer: config.forwardBuffer }\n );\n context.textTracksActor.set(textTracksActor);\n context.textTrackSegmentLoaderActor.set(textTrackSegmentLoaderActor);\n\n return () => {\n textTracksActor.destroy();\n textTrackSegmentLoaderActor.destroy();\n context.textTracksActor.set(undefined);\n context.textTrackSegmentLoaderActor.set(undefined);\n };\n });\n}\n\nexport const setupTextTrackActors = defineBehavior({\n stateKeys: [],\n contextKeys: ['mediaElement', 'textTracksActor', 'textTrackSegmentLoaderActor'],\n setup: setupTextTrackActorsSetup,\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AA0CA,SAAS,0BAA0B,EACjC,SACA,UAQa;AACb,QAAO,aAAa;EAClB,MAAM,eAAe,QAAQ,aAAa,KAAK;AAC/C,MAAI,CAAC,aAAc;EAEnB,MAAM,kBAAkB,sBAAsB,aAAa;EAC3D,MAAM,8BAA8B,kCAClC,iBACA,OAAO,yBACP,EAAE,eAAe,OAAO,eAAe,CACxC;AACD,UAAQ,gBAAgB,IAAI,gBAAgB;AAC5C,UAAQ,4BAA4B,IAAI,4BAA4B;AAEpE,eAAa;AACX,mBAAgB,SAAS;AACzB,+BAA4B,SAAS;AACrC,WAAQ,gBAAgB,IAAI,KAAA,EAAU;AACtC,WAAQ,4BAA4B,IAAI,KAAA,EAAU;;GAEpD;;AAGJ,MAAa,uBAAuB,eAAe;CACjD,WAAW,EAAE;CACb,aAAa;EAAC;EAAgB;EAAmB;EAA8B;CAC/E,OAAO;CACR,CAAC"}
@@ -0,0 +1,99 @@
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 { getTracksByType } from "../../../media/utils/tracks.js";
5
+ import { syncTextTrackModes } from "../../../media/dom/text/text-track-slots.js";
6
+ import { listen } from "@videojs/utils/dom";
7
+ //#region src/playback/behaviors/dom/sync-text-tracks.ts
8
+ /**
9
+ * **Own the text-track slots on the host media element, mirroring the SPF
10
+ * model.** When a presentation is resolved and a media element is
11
+ * available, allocate one slot in `mediaElement.textTracks` per model text
12
+ * track — via creating `<track>` children, since that's the only spec
13
+ * mechanism for adding *and* removing entries to `textTracks` (no
14
+ * `removeTextTrack` API exists). Once slots are provisioned, mirror
15
+ * `selectedTextTrackId` into their `mode`s, and propagate user-initiated
16
+ * DOM `change` events back to `selectedTextTrackId` so non-SPF consumers
17
+ * (host-page captions buttons, browser native UI, video.js store) remain
18
+ * the canonical write path for selection.
19
+ *
20
+ * Single-positive-state reactor (`'preconditions-unmet'` ↔ `'sync-active'`):
21
+ * the entry allocates the slots, applies the initial selection, attaches
22
+ * the `change` listener, and opens a brief Chromium settling-window guard —
23
+ * all transition-driven, fire-once on state entry, with paired cleanup on
24
+ * state exit. A single `effects:` mirrors subsequent
25
+ * `selectedTextTrackId` changes into `mode`s; that's the only
26
+ * continuous-reactivity concern.
27
+ *
28
+ * State-exit cleanup also sends a `'clear'` message to the
29
+ * `TextTracksActor` so its cue+segment cache (keyed by trackId) is
30
+ * dropped alongside the DOM `<track>` slots. The actor itself is owned
31
+ * by `setupTextTrackActors` and bound to mediaElement, not presentation,
32
+ * so it survives source resets; clearing its context here keeps the
33
+ * cache consistent with the DOM. Without this, a subsequent
34
+ * presentation reusing a trackId would have `getSegmentsToLoad` treat
35
+ * its segments as already-buffered and skip loading them.
36
+ *
37
+ * Multi-writer with `selectTextTrack` is intentional and orthogonal:
38
+ * `selectTextTrack` owns the *default-on-load / clear-on-unload* contract
39
+ * for `selectedTextTrackId`; this behavior writes only from DOM `change`
40
+ * events. They reflect distinct decision-making domains (config-driven
41
+ * intent vs DOM-driven user action), so this behavior never clears the
42
+ * slot on source unload — that's `selectTextTrack`'s contract. (It *does*
43
+ * write `undefined` when a user disables all tracks via native UI, since
44
+ * that's a real user action that should round-trip into SPF state.)
45
+ */
46
+ function deriveState(presentation, mediaElement) {
47
+ if (!mediaElement || !presentation) return "preconditions-unmet";
48
+ return getTracksByType(presentation, "text").length > 0 ? "sync-active" : "preconditions-unmet";
49
+ }
50
+ function syncTextTracksSetup({ state, context, config }) {
51
+ const { addSubtitlesTracksToMedia, getShowingSubtitlesTrackFromMedia, removeAllSubtitlesTracksFromMedia } = config;
52
+ const derivedStateSignal = computed(() => deriveState(state.presentation.get(), context.mediaElement.get()));
53
+ return createMachineReactor({
54
+ initial: "preconditions-unmet",
55
+ monitor: () => derivedStateSignal.get(),
56
+ states: {
57
+ "preconditions-unmet": {},
58
+ "sync-active": {
59
+ entry: () => {
60
+ const mediaElement = context.mediaElement.get();
61
+ addSubtitlesTracksToMedia(mediaElement, getTracksByType(state.presentation.get(), "text"));
62
+ syncTextTrackModes(mediaElement.textTracks, state.selectedTextTrackId.get());
63
+ let inSettlingWindow = true;
64
+ const settlingTimeout = setTimeout(() => {
65
+ inSettlingWindow = false;
66
+ }, 0);
67
+ const onChange = () => {
68
+ if (inSettlingWindow) {
69
+ syncTextTrackModes(mediaElement.textTracks, state.selectedTextTrackId.get());
70
+ return;
71
+ }
72
+ const nextId = getShowingSubtitlesTrackFromMedia(mediaElement)?.id || void 0;
73
+ if (nextId === state.selectedTextTrackId.get()) return;
74
+ state.selectedTextTrackId.set(nextId);
75
+ };
76
+ const unlisten = listen(mediaElement.textTracks, "change", onChange);
77
+ return () => {
78
+ unlisten();
79
+ clearTimeout(settlingTimeout);
80
+ removeAllSubtitlesTracksFromMedia(mediaElement);
81
+ peek(context.textTracksActor)?.send({ type: "clear" });
82
+ };
83
+ },
84
+ effects: () => {
85
+ syncTextTrackModes(peek(context.mediaElement).textTracks, state.selectedTextTrackId.get());
86
+ }
87
+ }
88
+ }
89
+ });
90
+ }
91
+ const syncTextTracks = defineBehavior({
92
+ stateKeys: ["presentation", "selectedTextTrackId"],
93
+ contextKeys: ["mediaElement", "textTracksActor"],
94
+ setup: syncTextTracksSetup
95
+ });
96
+ //#endregion
97
+ export { syncTextTracks };
98
+
99
+ //# sourceMappingURL=sync-text-tracks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-text-tracks.js","names":[],"sources":["../../../../../src/playback/behaviors/dom/sync-text-tracks.ts"],"sourcesContent":["/**\n * **Own the text-track slots on the host media element, mirroring the SPF\n * model.** When a presentation is resolved and a media element is\n * available, allocate one slot in `mediaElement.textTracks` per model text\n * track — via creating `<track>` children, since that's the only spec\n * mechanism for adding *and* removing entries to `textTracks` (no\n * `removeTextTrack` API exists). Once slots are provisioned, mirror\n * `selectedTextTrackId` into their `mode`s, and propagate user-initiated\n * DOM `change` events back to `selectedTextTrackId` so non-SPF consumers\n * (host-page captions buttons, browser native UI, video.js store) remain\n * the canonical write path for selection.\n *\n * Single-positive-state reactor (`'preconditions-unmet'` ↔ `'sync-active'`):\n * the entry allocates the slots, applies the initial selection, attaches\n * the `change` listener, and opens a brief Chromium settling-window guard —\n * all transition-driven, fire-once on state entry, with paired cleanup on\n * state exit. A single `effects:` mirrors subsequent\n * `selectedTextTrackId` changes into `mode`s; that's the only\n * continuous-reactivity concern.\n *\n * State-exit cleanup also sends a `'clear'` message to the\n * `TextTracksActor` so its cue+segment cache (keyed by trackId) is\n * dropped alongside the DOM `<track>` slots. The actor itself is owned\n * by `setupTextTrackActors` and bound to mediaElement, not presentation,\n * so it survives source resets; clearing its context here keeps the\n * cache consistent with the DOM. Without this, a subsequent\n * presentation reusing a trackId would have `getSegmentsToLoad` treat\n * its segments as already-buffered and skip loading them.\n *\n * Multi-writer with `selectTextTrack` is intentional and orthogonal:\n * `selectTextTrack` owns the *default-on-load / clear-on-unload* contract\n * for `selectedTextTrackId`; this behavior writes only from DOM `change`\n * events. They reflect distinct decision-making domains (config-driven\n * intent vs DOM-driven user action), so this behavior never clears the\n * slot on source unload — that's `selectTextTrack`'s contract. (It *does*\n * write `undefined` when a user disables all tracks via native UI, since\n * that's a real user action that should round-trip into SPF state.)\n */\n\nimport { listen } from '@videojs/utils/dom';\nimport { defineBehavior } from '../../../core/composition/create-composition';\nimport type { Reactor } from '../../../core/reactors/create-machine-reactor';\nimport { createMachineReactor } from '../../../core/reactors/create-machine-reactor';\nimport { computed, peek, type ReadonlySignal, type Signal } from '../../../core/signals/primitives';\nimport { syncTextTrackModes } from '../../../media/dom/text/text-track-slots';\nimport type { MaybeResolvedPresentation, PartiallyResolvedTextTrack, TextTrack } from '../../../media/types';\nimport { getTracksByType } from '../../../media/utils/tracks';\nimport type { TextTracksActor } from '../../actors/text-tracks';\n\ntype SyncTextTracksFsmState = 'preconditions-unmet' | 'sync-active';\n\nexport interface SyncTextTracksConfig {\n /**\n * Create and append SPF-owned `<track>` slots on `mediaElement`, one per\n * model text track. Implementation tags each element so the read/remove\n * helpers can scope to SPF-owned slots. **Required** — the behavior is\n * DOM-binding-neutral and the composing engine supplies the integration.\n */\n addSubtitlesTracksToMedia: (\n mediaElement: HTMLMediaElement,\n modelTextTracks: readonly (PartiallyResolvedTextTrack | TextTrack)[]\n ) => void;\n /**\n * Return the SPF-owned subtitle/caption `TextTrack` currently in `'showing'`\n * mode, or `undefined` if none. Used by the DOM `change` bridge to mirror\n * native-UI selection back into `selectedTextTrackId`.\n */\n getShowingSubtitlesTrackFromMedia: (mediaElement: HTMLMediaElement) => globalThis.TextTrack | undefined;\n /**\n * Remove every SPF-owned `<track>` child from `mediaElement`. Called on\n * state exit (source unload, behavior destroy) to evict slots.\n */\n removeAllSubtitlesTracksFromMedia: (mediaElement: HTMLMediaElement) => void;\n}\n\nfunction deriveState(\n presentation: MaybeResolvedPresentation | undefined,\n mediaElement: HTMLMediaElement | undefined\n): SyncTextTracksFsmState {\n if (!mediaElement || !presentation) return 'preconditions-unmet';\n return getTracksByType(presentation, 'text').length > 0 ? 'sync-active' : 'preconditions-unmet';\n}\n\nfunction syncTextTracksSetup({\n state,\n context,\n config,\n}: {\n state: {\n presentation: ReadonlySignal<MaybeResolvedPresentation | undefined>;\n selectedTextTrackId: Signal<string | undefined>;\n };\n context: {\n mediaElement: ReadonlySignal<HTMLMediaElement | undefined>;\n textTracksActor: ReadonlySignal<TextTracksActor<VTTCue> | undefined>;\n };\n config: SyncTextTracksConfig;\n}): Reactor<SyncTextTracksFsmState | 'destroying' | 'destroyed'> {\n const { addSubtitlesTracksToMedia, getShowingSubtitlesTrackFromMedia, removeAllSubtitlesTracksFromMedia } = config;\n\n const derivedStateSignal = computed(() => deriveState(state.presentation.get(), context.mediaElement.get()));\n\n return createMachineReactor<SyncTextTracksFsmState>({\n initial: 'preconditions-unmet',\n monitor: () => derivedStateSignal.get(),\n states: {\n 'preconditions-unmet': {},\n\n 'sync-active': {\n // Allocate slots, apply the initial selection, attach the change\n // listener, and open the settling window — all transition-driven\n // (fire once per state entry). Entry is auto-untracked, so `.get()`\n // here doesn't subscribe; the returned cleanup detaches and evicts\n // on state exit (src reset through 'preconditions-unmet') and on\n // destroy.\n entry: () => {\n const mediaElement = context.mediaElement.get()!;\n // `getTracksByType('text', ...)` returns text tracks only — the\n // selection-set filter inside the helper ensures that — but its\n // declared return is the wide track union. Mirror the cast\n // pattern used by `quality-switching` for the video branch.\n const modelTextTracks = getTracksByType(state.presentation.get()!, 'text') as readonly (\n | PartiallyResolvedTextTrack\n | TextTrack\n )[];\n\n addSubtitlesTracksToMedia(mediaElement, modelTextTracks);\n // Apply our selection synchronously so the change-event tasks\n // these mode writes queue land in the macrotask queue *before*\n // the settling-close `setTimeout(0)` queued below. This keeps\n // the settling window open long enough to swallow Chromium's\n // own auto-selection task (also scheduled in the next tick after\n // `<track>` insertion); if the order flipped, our handler would\n // treat Chromium's auto-pick as a user action. The `effects:`\n // block below handles subsequent `selectedTextTrackId` changes.\n syncTextTrackModes(mediaElement.textTracks, state.selectedTextTrackId.get());\n\n // Chromium re-applies its own selection across the next task tick\n // after `<track>` insertion (default-track auto-pick, language\n // preference). During this window, the `change` event may fire\n // with browser-chosen modes that don't reflect a real user action;\n // re-apply our modes silently rather than writing them back.\n let inSettlingWindow = true;\n const settlingTimeout = setTimeout(() => {\n inSettlingWindow = false;\n }, 0);\n\n const onChange = (): void => {\n if (inSettlingWindow) {\n syncTextTrackModes(mediaElement.textTracks, state.selectedTextTrackId.get());\n return;\n }\n const showingTrack = getShowingSubtitlesTrackFromMedia(mediaElement);\n // `showingTrack.id` matches the SPF id we set when the slot was\n // allocated. Empty-string ids fall through to `undefined`.\n const nextId = showingTrack?.id || undefined;\n if (nextId === state.selectedTextTrackId.get()) return;\n state.selectedTextTrackId.set(nextId);\n };\n\n const unlisten = listen(mediaElement.textTracks, 'change', onChange);\n\n return () => {\n unlisten();\n clearTimeout(settlingTimeout);\n removeAllSubtitlesTracksFromMedia(mediaElement);\n // Clear the TextTracksActor's cue+segment cache, which is\n // keyed by trackId. If we don't, a subsequent presentation\n // reusing a trackId would have `getSegmentsToLoad` treat its\n // segments as already-buffered. The DOM cleanup above\n // already evicted the live cues; this drops the cache that\n // tracked them. The actor itself is owned by\n // `setupTextTrackActors` (mediaElement-bound lifecycle), so\n // we send rather than destroy.\n peek(context.textTracksActor)?.send({ type: 'clear' });\n };\n },\n\n // Mirror selection changes into mode. The state machine handles\n // mediaElement/presentation changes via 'preconditions-unmet'\n // round-trips, so we peek mediaElement here.\n effects: () => {\n const mediaElement = peek(context.mediaElement)!;\n syncTextTrackModes(mediaElement.textTracks, state.selectedTextTrackId.get());\n },\n },\n },\n });\n}\n\nexport const syncTextTracks = defineBehavior({\n stateKeys: ['presentation', 'selectedTextTrackId'],\n contextKeys: ['mediaElement', 'textTracksActor'],\n setup: syncTextTracksSetup,\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2EA,SAAS,YACP,cACA,cACwB;AACxB,KAAI,CAAC,gBAAgB,CAAC,aAAc,QAAO;AAC3C,QAAO,gBAAgB,cAAc,OAAO,CAAC,SAAS,IAAI,gBAAgB;;AAG5E,SAAS,oBAAoB,EAC3B,OACA,SACA,UAW+D;CAC/D,MAAM,EAAE,2BAA2B,mCAAmC,sCAAsC;CAE5G,MAAM,qBAAqB,eAAe,YAAY,MAAM,aAAa,KAAK,EAAE,QAAQ,aAAa,KAAK,CAAC,CAAC;AAE5G,QAAO,qBAA6C;EAClD,SAAS;EACT,eAAe,mBAAmB,KAAK;EACvC,QAAQ;GACN,uBAAuB,EAAE;GAEzB,eAAe;IAOb,aAAa;KACX,MAAM,eAAe,QAAQ,aAAa,KAAK;AAU/C,+BAA0B,cALF,gBAAgB,MAAM,aAAa,KAAK,EAAG,OAAO,CAKlB;AASxD,wBAAmB,aAAa,YAAY,MAAM,oBAAoB,KAAK,CAAC;KAO5E,IAAI,mBAAmB;KACvB,MAAM,kBAAkB,iBAAiB;AACvC,yBAAmB;QAClB,EAAE;KAEL,MAAM,iBAAuB;AAC3B,UAAI,kBAAkB;AACpB,0BAAmB,aAAa,YAAY,MAAM,oBAAoB,KAAK,CAAC;AAC5E;;MAKF,MAAM,SAHe,kCAAkC,aAAa,EAGvC,MAAM,KAAA;AACnC,UAAI,WAAW,MAAM,oBAAoB,KAAK,CAAE;AAChD,YAAM,oBAAoB,IAAI,OAAO;;KAGvC,MAAM,WAAW,OAAO,aAAa,YAAY,UAAU,SAAS;AAEpE,kBAAa;AACX,gBAAU;AACV,mBAAa,gBAAgB;AAC7B,wCAAkC,aAAa;AAS/C,WAAK,QAAQ,gBAAgB,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;;;IAO1D,eAAe;AAEb,wBADqB,KAAK,QAAQ,aAAa,CACf,YAAY,MAAM,oBAAoB,KAAK,CAAC;;IAE/E;GACF;EACF,CAAC;;AAGJ,MAAa,iBAAiB,eAAe;CAC3C,WAAW,CAAC,gBAAgB,sBAAsB;CAClD,aAAa,CAAC,gBAAgB,kBAAkB;CAChD,OAAO;CACR,CAAC"}
@@ -0,0 +1,46 @@
1
+ import { defineBehavior } from "../../../core/composition/create-composition.js";
2
+ import { effect } from "../../../core/signals/effect.js";
3
+ import { listen } from "@videojs/utils/dom";
4
+ //#region src/playback/behaviors/dom/track-current-time.ts
5
+ /**
6
+ * Mirror `mediaElement.currentTime` into reactive state. Listens for:
7
+ * - `timeupdate` — fires during playback (~4 Hz)
8
+ * - `seeking` — fires when a seek begins; per spec, `currentTime` is already
9
+ * at the new position when this event dispatches, so buffer management can
10
+ * react immediately rather than waiting for `timeupdate`, which does not
11
+ * fire while paused.
12
+ *
13
+ * Also syncs immediately when a media element becomes available.
14
+ *
15
+ * When no media element is attached, writes `config.defaultCurrentTime`
16
+ * (default-default `0`, matching the HTMLMediaElement spec) so consumers
17
+ * always see a defined position. Read-only mirror; does not push
18
+ * `state.currentTime` back to the element.
19
+ */
20
+ function trackCurrentTimeSetup({ state, context, config }) {
21
+ const defaultCurrentTime = config?.defaultCurrentTime ?? 0;
22
+ return effect(() => {
23
+ const mediaElement = context.mediaElement.get();
24
+ if (!mediaElement) {
25
+ state.currentTime.set(defaultCurrentTime);
26
+ return;
27
+ }
28
+ const sync = () => state.currentTime.set(mediaElement.currentTime);
29
+ sync();
30
+ const removeTimeupdate = listen(mediaElement, "timeupdate", sync);
31
+ const removeSeeking = listen(mediaElement, "seeking", sync);
32
+ return () => {
33
+ removeTimeupdate();
34
+ removeSeeking();
35
+ };
36
+ });
37
+ }
38
+ const trackCurrentTime = defineBehavior({
39
+ stateKeys: ["currentTime"],
40
+ contextKeys: ["mediaElement"],
41
+ setup: trackCurrentTimeSetup
42
+ });
43
+ //#endregion
44
+ export { trackCurrentTime };
45
+
46
+ //# sourceMappingURL=track-current-time.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"track-current-time.js","names":[],"sources":["../../../../../src/playback/behaviors/dom/track-current-time.ts"],"sourcesContent":["/**\n * Mirror `mediaElement.currentTime` into reactive state. Listens for:\n * - `timeupdate` — fires during playback (~4 Hz)\n * - `seeking` — fires when a seek begins; per spec, `currentTime` is already\n * at the new position when this event dispatches, so buffer management can\n * react immediately rather than waiting for `timeupdate`, which does not\n * fire while paused.\n *\n * Also syncs immediately when a media element becomes available.\n *\n * When no media element is attached, writes `config.defaultCurrentTime`\n * (default-default `0`, matching the HTMLMediaElement spec) so consumers\n * always see a defined position. Read-only mirror; does not push\n * `state.currentTime` back to the element.\n */\nimport { listen } from '@videojs/utils/dom';\nimport { defineBehavior } from '../../../core/composition/create-composition';\nimport { effect } from '../../../core/signals/effect';\nimport type { ReadonlySignal, Signal } from '../../../core/signals/primitives';\n\nexport interface CurrentTimeState {\n currentTime?: number;\n}\n\nexport interface CurrentTimeContext {\n mediaElement?: HTMLMediaElement | undefined;\n}\n\nexport interface TrackCurrentTimeConfig {\n /**\n * Value written to `state.currentTime` when no media element is attached.\n * Defaults to `0` — the HTMLMediaElement spec default.\n */\n defaultCurrentTime?: number;\n}\n\nfunction trackCurrentTimeSetup({\n state,\n context,\n config,\n}: {\n state: { currentTime: Signal<CurrentTimeState['currentTime']> };\n context: { mediaElement: ReadonlySignal<CurrentTimeContext['mediaElement']> };\n config?: TrackCurrentTimeConfig;\n}): () => void {\n const defaultCurrentTime = config?.defaultCurrentTime ?? 0;\n\n return effect(() => {\n const mediaElement = context.mediaElement.get();\n if (!mediaElement) {\n state.currentTime.set(defaultCurrentTime);\n return;\n }\n\n const sync = () => state.currentTime.set(mediaElement.currentTime);\n sync();\n const removeTimeupdate = listen(mediaElement, 'timeupdate', sync);\n const removeSeeking = listen(mediaElement, 'seeking', sync);\n return () => {\n removeTimeupdate();\n removeSeeking();\n };\n });\n}\n\nexport const trackCurrentTime = defineBehavior({\n stateKeys: ['currentTime'],\n contextKeys: ['mediaElement'],\n setup: trackCurrentTimeSetup,\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAoCA,SAAS,sBAAsB,EAC7B,OACA,SACA,UAKa;CACb,MAAM,qBAAqB,QAAQ,sBAAsB;AAEzD,QAAO,aAAa;EAClB,MAAM,eAAe,QAAQ,aAAa,KAAK;AAC/C,MAAI,CAAC,cAAc;AACjB,SAAM,YAAY,IAAI,mBAAmB;AACzC;;EAGF,MAAM,aAAa,MAAM,YAAY,IAAI,aAAa,YAAY;AAClE,QAAM;EACN,MAAM,mBAAmB,OAAO,cAAc,cAAc,KAAK;EACjE,MAAM,gBAAgB,OAAO,cAAc,WAAW,KAAK;AAC3D,eAAa;AACX,qBAAkB;AAClB,kBAAe;;GAEjB;;AAGJ,MAAa,mBAAmB,eAAe;CAC7C,WAAW,CAAC,cAAc;CAC1B,aAAa,CAAC,eAAe;CAC7B,OAAO;CACR,CAAC"}
@@ -0,0 +1,94 @@
1
+ import { computed } 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 { listen } from "@videojs/utils/dom";
5
+ //#region src/playback/behaviors/dom/track-load-triggers.ts
6
+ /**
7
+ * Slot-driven FSM. The slot `loadActivated` is the canonical externally-
8
+ * observable state; `deriveState` reads it (and the preconditions) to
9
+ * derive the local FSM state. The slot is *both* the state and the data —
10
+ * no separate internal bookkeeping.
11
+ *
12
+ * ```
13
+ * 'preconditions-unmet' ⟷ 'monitoring' ⟷ 'load-active'
14
+ *
15
+ * preconditions-unmet → monitoring element + URL appear
16
+ * monitoring → load-active slot flips true (listener fires
17
+ * or external write)
18
+ * load-active → monitoring within-state cleanup resets slot
19
+ * (URL or element identity change)
20
+ * any → preconditions-unmet element or URL → undefined
21
+ *
22
+ * any state → destroying → destroyed on destroy()
23
+ * ```
24
+ */
25
+ function deriveState(presentation, mediaElement, loadActivated) {
26
+ if (!mediaElement || !presentation?.url) return "preconditions-unmet";
27
+ if (loadActivated) return "load-active";
28
+ return "monitoring";
29
+ }
30
+ /**
31
+ * Track preload-overriding events per source.
32
+ *
33
+ * Writes `state.loadActivated = true` the first time a `play` or `seeking`
34
+ * event fires on the attached media element for the current source — or
35
+ * immediately on entry if the element is already in such a state
36
+ * (`!el.paused` or `el.seeking`), mirroring autoplay / native-controls /
37
+ * direct-DOM-`play()` scenarios.
38
+ *
39
+ * Sticky-true *within a source identity*: subsequent play/pause/seek
40
+ * cycles don't flip back. Source identity = (mediaElement, presentation
41
+ * URL). Either changing — including direct in-place swap with no
42
+ * `undefined` intermediate — resets the slot to `false`.
43
+ *
44
+ * Multi-writer with `hls/adapter.ts:play()` (which writes `true` directly
45
+ * on programmatic play) is intentional — different domains. The adapter
46
+ * records programmatic intent; this behavior is the DOM-side observer.
47
+ * Pre-existing `true` writes are honored because `deriveState` reads the
48
+ * slot — a `true` value routes directly to `'load-active'` without
49
+ * entering `'monitoring'`.
50
+ *
51
+ * @example
52
+ * const reactor = trackLoadTriggers.setup({ state, context });
53
+ * // later:
54
+ * reactor.destroy();
55
+ */
56
+ function trackLoadTriggersSetup({ state, context }) {
57
+ const derivedStateSignal = computed(() => deriveState(state.presentation.get(), context.mediaElement.get(), state.loadActivated.get()));
58
+ const urlSignal = computed(() => state.presentation.get()?.url);
59
+ return createMachineReactor({
60
+ initial: "preconditions-unmet",
61
+ monitor: () => derivedStateSignal.get(),
62
+ states: {
63
+ "preconditions-unmet": {},
64
+ monitoring: { effects: () => {
65
+ const el = context.mediaElement.get();
66
+ const setLoadActivated = () => state.loadActivated.set(true);
67
+ if (!el.paused || el.seeking) {
68
+ setLoadActivated();
69
+ return;
70
+ }
71
+ const cleanupPlay = listen(el, "play", setLoadActivated);
72
+ const cleanupSeeking = listen(el, "seeking", setLoadActivated);
73
+ return () => {
74
+ cleanupPlay();
75
+ cleanupSeeking();
76
+ };
77
+ } },
78
+ "load-active": { effects: () => {
79
+ context.mediaElement.get();
80
+ urlSignal.get();
81
+ return () => state.loadActivated.set(false);
82
+ } }
83
+ }
84
+ });
85
+ }
86
+ const trackLoadTriggers = defineBehavior({
87
+ stateKeys: ["loadActivated", "presentation"],
88
+ contextKeys: ["mediaElement"],
89
+ setup: trackLoadTriggersSetup
90
+ });
91
+ //#endregion
92
+ export { trackLoadTriggers };
93
+
94
+ //# sourceMappingURL=track-load-triggers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"track-load-triggers.js","names":[],"sources":["../../../../../src/playback/behaviors/dom/track-load-triggers.ts"],"sourcesContent":["import { listen } from '@videojs/utils/dom';\nimport { defineBehavior } from '../../../core/composition/create-composition';\nimport type { Reactor } from '../../../core/reactors/create-machine-reactor';\nimport { createMachineReactor } from '../../../core/reactors/create-machine-reactor';\nimport { computed, type ReadonlySignal, type Signal } from '../../../core/signals/primitives';\nimport type { MaybeResolvedPresentation } from '../../../media/types';\n\n/**\n * State shape for load-trigger tracking.\n */\nexport interface LoadTriggersState {\n /**\n * Sticky-true per source. Flips to `true` the first time an event occurs\n * that should switch loading behavior from preload-based to standard load\n * (currently DOM `play` / `seeking`). Reset to `false` on source change.\n *\n * Read by `resolvePresentation` and `loadVideoSegments` / `loadAudioSegments`\n * to mirror native `HTMLMediaElement` preload semantics — only meaningful\n * when `preload !== 'auto'`, which already loads fully regardless.\n */\n loadActivated?: boolean;\n /** Current presentation — URL is used to detect source changes. */\n presentation?: MaybeResolvedPresentation;\n}\n\n/**\n * Context shape for load-trigger tracking.\n */\nexport interface LoadTriggersContext {\n mediaElement?: HTMLMediaElement | undefined;\n}\n\ntype LoadTriggersFsmState = 'preconditions-unmet' | 'monitoring' | 'load-active';\n\n/**\n * Slot-driven FSM. The slot `loadActivated` is the canonical externally-\n * observable state; `deriveState` reads it (and the preconditions) to\n * derive the local FSM state. The slot is *both* the state and the data —\n * no separate internal bookkeeping.\n *\n * ```\n * 'preconditions-unmet' ⟷ 'monitoring' ⟷ 'load-active'\n *\n * preconditions-unmet → monitoring element + URL appear\n * monitoring → load-active slot flips true (listener fires\n * or external write)\n * load-active → monitoring within-state cleanup resets slot\n * (URL or element identity change)\n * any → preconditions-unmet element or URL → undefined\n *\n * any state → destroying → destroyed on destroy()\n * ```\n */\nfunction deriveState(\n presentation: MaybeResolvedPresentation | undefined,\n mediaElement: HTMLMediaElement | undefined,\n loadActivated: boolean | undefined\n): LoadTriggersFsmState {\n if (!mediaElement || !presentation?.url) return 'preconditions-unmet';\n if (loadActivated) return 'load-active';\n return 'monitoring';\n}\n\n/**\n * Track preload-overriding events per source.\n *\n * Writes `state.loadActivated = true` the first time a `play` or `seeking`\n * event fires on the attached media element for the current source — or\n * immediately on entry if the element is already in such a state\n * (`!el.paused` or `el.seeking`), mirroring autoplay / native-controls /\n * direct-DOM-`play()` scenarios.\n *\n * Sticky-true *within a source identity*: subsequent play/pause/seek\n * cycles don't flip back. Source identity = (mediaElement, presentation\n * URL). Either changing — including direct in-place swap with no\n * `undefined` intermediate — resets the slot to `false`.\n *\n * Multi-writer with `hls/adapter.ts:play()` (which writes `true` directly\n * on programmatic play) is intentional — different domains. The adapter\n * records programmatic intent; this behavior is the DOM-side observer.\n * Pre-existing `true` writes are honored because `deriveState` reads the\n * slot — a `true` value routes directly to `'load-active'` without\n * entering `'monitoring'`.\n *\n * @example\n * const reactor = trackLoadTriggers.setup({ state, context });\n * // later:\n * reactor.destroy();\n */\nfunction trackLoadTriggersSetup({\n state,\n context,\n}: {\n state: {\n loadActivated: Signal<LoadTriggersState['loadActivated']>;\n presentation: ReadonlySignal<LoadTriggersState['presentation']>;\n };\n context: { mediaElement: ReadonlySignal<LoadTriggersContext['mediaElement']> };\n}): Reactor<LoadTriggersFsmState | 'destroying' | 'destroyed'> {\n const derivedStateSignal = computed(() =>\n deriveState(state.presentation.get(), context.mediaElement.get(), state.loadActivated.get())\n );\n // Sparse-firing source-URL signal. Emits whenever `presentation?.url`\n // resolves to a different value — covering both URL string changes\n // (url_a → url_b) and transitions where the URL becomes defined or\n // undefined (presentation set, cleared, or replaced without a URL\n // field). Does NOT emit when `state.presentation` is rewritten with\n // the same URL: manifest parsing, duration calc, track resolution,\n // and other enrichment paths re-write the presentation object\n // frequently — tracking raw `state.presentation` here would spuriously\n // reset the 'load-active' slot.\n const urlSignal = computed(() => state.presentation.get()?.url);\n\n return createMachineReactor<LoadTriggersFsmState>({\n initial: 'preconditions-unmet',\n monitor: () => derivedStateSignal.get(),\n states: {\n 'preconditions-unmet': {},\n\n monitoring: {\n // effects (not entry) so element-identity change within this state\n // re-fires and re-attaches listeners to the new element. The monitor\n // guarantees mediaElement is truthy while we're in this state.\n effects: () => {\n const el = context.mediaElement.get()!;\n\n const setLoadActivated = () => state.loadActivated.set(true);\n if (!el.paused || el.seeking) {\n setLoadActivated();\n return;\n }\n const cleanupPlay = listen(el, 'play', setLoadActivated);\n const cleanupSeeking = listen(el, 'seeking', setLoadActivated);\n return () => {\n cleanupPlay();\n cleanupSeeking();\n };\n },\n },\n\n 'load-active': {\n // effects-based cleanup (not entry) because the cleanup must fire on\n // BOTH state exit AND within-state identity change (url_a → url_b,\n // element_a → element_b — both truthy, no state transition).\n // Re-firing tracked reads → cleanup writes loadActivated=false →\n // deriveState (which reads loadActivated) returns 'monitoring' →\n // state transitions → fresh listeners attached for the new source.\n effects: () => {\n context.mediaElement.get();\n urlSignal.get();\n return () => state.loadActivated.set(false);\n },\n },\n },\n });\n}\n\nexport const trackLoadTriggers = defineBehavior({\n stateKeys: ['loadActivated', 'presentation'],\n contextKeys: ['mediaElement'],\n setup: trackLoadTriggersSetup,\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAqDA,SAAS,YACP,cACA,cACA,eACsB;AACtB,KAAI,CAAC,gBAAgB,CAAC,cAAc,IAAK,QAAO;AAChD,KAAI,cAAe,QAAO;AAC1B,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BT,SAAS,uBAAuB,EAC9B,OACA,WAO6D;CAC7D,MAAM,qBAAqB,eACzB,YAAY,MAAM,aAAa,KAAK,EAAE,QAAQ,aAAa,KAAK,EAAE,MAAM,cAAc,KAAK,CAAC,CAC7F;CAUD,MAAM,YAAY,eAAe,MAAM,aAAa,KAAK,EAAE,IAAI;AAE/D,QAAO,qBAA2C;EAChD,SAAS;EACT,eAAe,mBAAmB,KAAK;EACvC,QAAQ;GACN,uBAAuB,EAAE;GAEzB,YAAY,EAIV,eAAe;IACb,MAAM,KAAK,QAAQ,aAAa,KAAK;IAErC,MAAM,yBAAyB,MAAM,cAAc,IAAI,KAAK;AAC5D,QAAI,CAAC,GAAG,UAAU,GAAG,SAAS;AAC5B,uBAAkB;AAClB;;IAEF,MAAM,cAAc,OAAO,IAAI,QAAQ,iBAAiB;IACxD,MAAM,iBAAiB,OAAO,IAAI,WAAW,iBAAiB;AAC9D,iBAAa;AACX,kBAAa;AACb,qBAAgB;;MAGrB;GAED,eAAe,EAOb,eAAe;AACb,YAAQ,aAAa,KAAK;AAC1B,cAAU,KAAK;AACf,iBAAa,MAAM,cAAc,IAAI,MAAM;MAE9C;GACF;EACF,CAAC;;AAGJ,MAAa,oBAAoB,eAAe;CAC9C,WAAW,CAAC,iBAAiB,eAAe;CAC5C,aAAa,CAAC,eAAe;CAC7B,OAAO;CACR,CAAC"}
@@ -0,0 +1,37 @@
1
+ import { defineBehavior } from "../../../core/composition/create-composition.js";
2
+ import { effect } from "../../../core/signals/effect.js";
3
+ import { listen } from "@videojs/utils/dom";
4
+ //#region src/playback/behaviors/dom/track-playback-rate.ts
5
+ /**
6
+ * Mirror `mediaElement.playbackRate` into reactive state. On each `ratechange`
7
+ * event, write the new value to `state.playbackRate`. Also syncs immediately
8
+ * when a media element becomes available so consumers don't wait for the first
9
+ * event.
10
+ *
11
+ * When no media element is attached, writes `config.defaultPlaybackRate`
12
+ * (default-default `1`, matching the HTMLMediaElement spec) so consumers
13
+ * always see the rate a freshly attached element would have. Read-only mirror;
14
+ * does not push `state.playbackRate` back to the element.
15
+ */
16
+ function trackPlaybackRateSetup({ state, context, config }) {
17
+ const defaultPlaybackRate = config?.defaultPlaybackRate ?? 1;
18
+ return effect(() => {
19
+ const mediaElement = context.mediaElement.get();
20
+ if (!mediaElement) {
21
+ state.playbackRate.set(defaultPlaybackRate);
22
+ return;
23
+ }
24
+ const sync = () => state.playbackRate.set(mediaElement.playbackRate);
25
+ sync();
26
+ return listen(mediaElement, "ratechange", sync);
27
+ });
28
+ }
29
+ const trackPlaybackRate = defineBehavior({
30
+ stateKeys: ["playbackRate"],
31
+ contextKeys: ["mediaElement"],
32
+ setup: trackPlaybackRateSetup
33
+ });
34
+ //#endregion
35
+ export { trackPlaybackRate };
36
+
37
+ //# sourceMappingURL=track-playback-rate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"track-playback-rate.js","names":[],"sources":["../../../../../src/playback/behaviors/dom/track-playback-rate.ts"],"sourcesContent":["/**\n * Mirror `mediaElement.playbackRate` into reactive state. On each `ratechange`\n * event, write the new value to `state.playbackRate`. Also syncs immediately\n * when a media element becomes available so consumers don't wait for the first\n * event.\n *\n * When no media element is attached, writes `config.defaultPlaybackRate`\n * (default-default `1`, matching the HTMLMediaElement spec) so consumers\n * always see the rate a freshly attached element would have. Read-only mirror;\n * does not push `state.playbackRate` back to the element.\n */\nimport { listen } from '@videojs/utils/dom';\nimport { defineBehavior } from '../../../core/composition/create-composition';\nimport { effect } from '../../../core/signals/effect';\nimport type { ReadonlySignal, Signal } from '../../../core/signals/primitives';\n\nexport interface PlaybackRateState {\n playbackRate?: number;\n}\n\nexport interface PlaybackRateContext {\n mediaElement?: HTMLMediaElement | undefined;\n}\n\nexport interface TrackPlaybackRateConfig {\n /**\n * Value written to `state.playbackRate` when no media element is attached.\n * Defaults to `1` — the HTMLMediaElement spec default.\n */\n defaultPlaybackRate?: number;\n}\n\nfunction trackPlaybackRateSetup({\n state,\n context,\n config,\n}: {\n state: { playbackRate: Signal<PlaybackRateState['playbackRate']> };\n context: { mediaElement: ReadonlySignal<PlaybackRateContext['mediaElement']> };\n config?: TrackPlaybackRateConfig;\n}): () => void {\n const defaultPlaybackRate = config?.defaultPlaybackRate ?? 1;\n\n return effect(() => {\n const mediaElement = context.mediaElement.get();\n if (!mediaElement) {\n state.playbackRate.set(defaultPlaybackRate);\n return;\n }\n\n const sync = () => state.playbackRate.set(mediaElement.playbackRate);\n sync();\n return listen(mediaElement, 'ratechange', sync);\n });\n}\n\nexport const trackPlaybackRate = defineBehavior({\n stateKeys: ['playbackRate'],\n contextKeys: ['mediaElement'],\n setup: trackPlaybackRateSetup,\n});\n"],"mappings":";;;;;;;;;;;;;;;AAgCA,SAAS,uBAAuB,EAC9B,OACA,SACA,UAKa;CACb,MAAM,sBAAsB,QAAQ,uBAAuB;AAE3D,QAAO,aAAa;EAClB,MAAM,eAAe,QAAQ,aAAa,KAAK;AAC/C,MAAI,CAAC,cAAc;AACjB,SAAM,aAAa,IAAI,oBAAoB;AAC3C;;EAGF,MAAM,aAAa,MAAM,aAAa,IAAI,aAAa,aAAa;AACpE,QAAM;AACN,SAAO,OAAO,cAAc,cAAc,KAAK;GAC/C;;AAGJ,MAAa,oBAAoB,eAAe;CAC9C,WAAW,CAAC,eAAe;CAC3B,aAAa,CAAC,eAAe;CAC7B,OAAO;CACR,CAAC"}
@@ -0,0 +1,90 @@
1
+ import { computed } 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 { getMaxBufferedEnd, shouldUpdateDuration, waitForSourceBuffersReady } from "../../../media/dom/mse/duration.js";
5
+ import { waitForMediaSourceOpen } from "../../../media/dom/mse/mediasource-setup.js";
6
+ //#region src/playback/behaviors/dom/update-mediasource-duration.ts
7
+ /**
8
+ * **Propagate `presentation.duration` to `mediaSource.duration` — exactly
9
+ * once per MediaSource.**
10
+ *
11
+ * When the presentation has a valid positive duration (including `Infinity`
12
+ * for live) and a MediaSource is in context, writes the value through to
13
+ * `mediaSource.duration` on initial setup — once, while `mediaSource.duration`
14
+ * is still `NaN`. Once any non-NaN value is present (set by us, or by
15
+ * `endOfStream` from the buffered end), the behavior leaves the property
16
+ * alone; re-syncing a drift against `presentation.duration` would race with
17
+ * concurrent `appendBuffer()` calls.
18
+ *
19
+ * The entry resolves three async preconditions in order before writing:
20
+ *
21
+ * 1. **MediaSource open** — `waitForMediaSourceOpen` defers until the first
22
+ * `sourceopen` event (or any readyState transition, since `'ended'` /
23
+ * `'closed'` mean we've missed the window and should bail).
24
+ * 2. **All SourceBuffers idle** — `waitForSourceBuffersReady` defers per
25
+ * the MSE-spec rule that `duration` cannot be set while any buffer has
26
+ * `updating === true`.
27
+ * 3. **Buffered-range clamp** — `getMaxBufferedEnd` ensures the written
28
+ * duration is at least the highest buffered end (MSE spec disallows
29
+ * a smaller `duration` than any buffered range).
30
+ *
31
+ * The buffer-set helpers operate across `mediaSource.sourceBuffers` (the
32
+ * canonical aggregate), so the behavior composes uniformly across
33
+ * audio-only, video-only, and mixed configurations.
34
+ *
35
+ * Single-positive-state reactor (`'preconditions-unmet'` ↔ `'duration-writable'`):
36
+ * state derivation is purely signal-driven (presentation validity +
37
+ * mediaSource existence). MediaSource lifecycle state (`readyState`) and
38
+ * the `duration` itself are non-signal DOM properties resolved inside the
39
+ * entry's async sequence. The state-exit cleanup aborts the in-flight
40
+ * wait, so source resets and behavior destroy structurally cancel the
41
+ * pending write. A post-await re-check of `mediaSource.readyState ===
42
+ * 'open'` covers the narrow race where `endOfStream()` synchronously
43
+ * transitions readyState to `'ended'` between our `waitForMediaSourceOpen`
44
+ * resolution and the `mediaSource.duration` write.
45
+ *
46
+ * Downstream of `calculatePresentationDuration` (which writes
47
+ * `presentation.duration`); concurrent with `endOfStream` (which may later
48
+ * write `mediaSource.duration` — the "exactly once" contract keeps us out
49
+ * of that path).
50
+ */
51
+ function deriveState(presentation, mediaSource) {
52
+ return shouldUpdateDuration(presentation, mediaSource) ? "duration-writable" : "preconditions-unmet";
53
+ }
54
+ function updateMediaSourceDurationSetup({ state, context }) {
55
+ const derivedStateSignal = computed(() => deriveState(state.presentation.get(), context.mediaSource.get()));
56
+ return createMachineReactor({
57
+ initial: "preconditions-unmet",
58
+ monitor: () => derivedStateSignal.get(),
59
+ states: {
60
+ "preconditions-unmet": {},
61
+ "duration-writable": { entry: () => {
62
+ const presentation = state.presentation.get();
63
+ const mediaSource = context.mediaSource.get();
64
+ if (!Number.isNaN(mediaSource.duration)) return;
65
+ const controller = new AbortController();
66
+ const writeWhenReady = async () => {
67
+ await waitForMediaSourceOpen(mediaSource, controller.signal);
68
+ if (controller.signal.aborted) return;
69
+ if (mediaSource.readyState !== "open") return;
70
+ await waitForSourceBuffersReady(mediaSource.sourceBuffers, controller.signal);
71
+ if (controller.signal.aborted) return;
72
+ if (mediaSource.readyState !== "open") return;
73
+ const maxBufferedEnd = getMaxBufferedEnd(mediaSource.sourceBuffers);
74
+ mediaSource.duration = maxBufferedEnd > presentation.duration ? maxBufferedEnd : presentation.duration;
75
+ };
76
+ writeWhenReady();
77
+ return () => controller.abort();
78
+ } }
79
+ }
80
+ });
81
+ }
82
+ const updateMediaSourceDuration = defineBehavior({
83
+ stateKeys: ["presentation"],
84
+ contextKeys: ["mediaSource"],
85
+ setup: updateMediaSourceDurationSetup
86
+ });
87
+ //#endregion
88
+ export { updateMediaSourceDuration };
89
+
90
+ //# sourceMappingURL=update-mediasource-duration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-mediasource-duration.js","names":[],"sources":["../../../../../src/playback/behaviors/dom/update-mediasource-duration.ts"],"sourcesContent":["/**\n * **Propagate `presentation.duration` to `mediaSource.duration` — exactly\n * once per MediaSource.**\n *\n * When the presentation has a valid positive duration (including `Infinity`\n * for live) and a MediaSource is in context, writes the value through to\n * `mediaSource.duration` on initial setup — once, while `mediaSource.duration`\n * is still `NaN`. Once any non-NaN value is present (set by us, or by\n * `endOfStream` from the buffered end), the behavior leaves the property\n * alone; re-syncing a drift against `presentation.duration` would race with\n * concurrent `appendBuffer()` calls.\n *\n * The entry resolves three async preconditions in order before writing:\n *\n * 1. **MediaSource open** — `waitForMediaSourceOpen` defers until the first\n * `sourceopen` event (or any readyState transition, since `'ended'` /\n * `'closed'` mean we've missed the window and should bail).\n * 2. **All SourceBuffers idle** — `waitForSourceBuffersReady` defers per\n * the MSE-spec rule that `duration` cannot be set while any buffer has\n * `updating === true`.\n * 3. **Buffered-range clamp** — `getMaxBufferedEnd` ensures the written\n * duration is at least the highest buffered end (MSE spec disallows\n * a smaller `duration` than any buffered range).\n *\n * The buffer-set helpers operate across `mediaSource.sourceBuffers` (the\n * canonical aggregate), so the behavior composes uniformly across\n * audio-only, video-only, and mixed configurations.\n *\n * Single-positive-state reactor (`'preconditions-unmet'` ↔ `'duration-writable'`):\n * state derivation is purely signal-driven (presentation validity +\n * mediaSource existence). MediaSource lifecycle state (`readyState`) and\n * the `duration` itself are non-signal DOM properties resolved inside the\n * entry's async sequence. The state-exit cleanup aborts the in-flight\n * wait, so source resets and behavior destroy structurally cancel the\n * pending write. A post-await re-check of `mediaSource.readyState ===\n * 'open'` covers the narrow race where `endOfStream()` synchronously\n * transitions readyState to `'ended'` between our `waitForMediaSourceOpen`\n * resolution and the `mediaSource.duration` write.\n *\n * Downstream of `calculatePresentationDuration` (which writes\n * `presentation.duration`); concurrent with `endOfStream` (which may later\n * write `mediaSource.duration` — the \"exactly once\" contract keeps us out\n * of that path).\n */\nimport { defineBehavior } from '../../../core/composition/create-composition';\nimport type { Reactor } from '../../../core/reactors/create-machine-reactor';\nimport { createMachineReactor } from '../../../core/reactors/create-machine-reactor';\nimport { computed, type ReadonlySignal } from '../../../core/signals/primitives';\nimport { getMaxBufferedEnd, shouldUpdateDuration, waitForSourceBuffersReady } from '../../../media/dom/mse/duration';\nimport { waitForMediaSourceOpen } from '../../../media/dom/mse/mediasource-setup';\nimport type { MaybeResolvedPresentation } from '../../../media/types';\n\ntype DurationFsmState = 'preconditions-unmet' | 'duration-writable';\n\nfunction deriveState(\n presentation: MaybeResolvedPresentation | undefined,\n mediaSource: MediaSource | undefined\n): DurationFsmState {\n return shouldUpdateDuration(presentation, mediaSource) ? 'duration-writable' : 'preconditions-unmet';\n}\n\nfunction updateMediaSourceDurationSetup({\n state,\n context,\n}: {\n state: { presentation: ReadonlySignal<MaybeResolvedPresentation | undefined> };\n context: { mediaSource: ReadonlySignal<MediaSource | undefined> };\n}): Reactor<DurationFsmState | 'destroying' | 'destroyed'> {\n // Purely signal-driven derivation: only `presentation` validity and\n // `mediaSource` existence. MediaSource lifecycle (`readyState`),\n // its attached `sourceBuffers`, and `mediaSource.duration` itself are\n // non-signal DOM properties consumed inside the entry — none of them\n // sneak into `computed(...)`.\n const derivedStateSignal = computed(() => deriveState(state.presentation.get(), context.mediaSource.get()));\n\n return createMachineReactor<DurationFsmState>({\n initial: 'preconditions-unmet',\n monitor: () => derivedStateSignal.get(),\n states: {\n 'preconditions-unmet': {},\n\n 'duration-writable': {\n // entry body is auto-untracked. The state machine handles source\n // resets via deriveState; this entry resolves the MediaSource\n // lifecycle preconditions in sequence and binds its cleanup\n // (abort) to state exit + destroy.\n entry: () => {\n const presentation = state.presentation.get()!;\n const mediaSource = context.mediaSource.get()!;\n\n // Idempotency: someone (us on a prior entry, or concurrent\n // `endOfStream`) has already written. No-op and leave the state\n // alone — the next source-reset transition will produce a fresh\n // entry against the new MediaSource (whose `duration` starts at\n // `NaN` again).\n if (!Number.isNaN(mediaSource.duration)) return;\n\n const controller = new AbortController();\n\n const writeWhenReady = async () => {\n await waitForMediaSourceOpen(mediaSource, controller.signal);\n if (controller.signal.aborted) return;\n // `waitForMediaSourceOpen` resolves on any readyState transition\n // out of 'closed'; if we landed in 'ended' / 'closed' instead of\n // 'open', the write window is gone.\n if (mediaSource.readyState !== 'open') return;\n\n await waitForSourceBuffersReady(mediaSource.sourceBuffers, controller.signal);\n if (controller.signal.aborted) return;\n\n // Narrow race: `endOfStream()` synchronously transitions\n // readyState to 'ended' AND writes `mediaSource.duration`\n // between our `waitForMediaSourceOpen` resolution and here.\n // Re-read the DOM property to catch this window.\n if (mediaSource.readyState !== 'open') return;\n\n // MSE spec: duration cannot be less than any buffered range.\n // Re-read sourceBuffers after the await so any buffer added\n // during the wait is included in the clamp.\n const maxBufferedEnd = getMaxBufferedEnd(mediaSource.sourceBuffers);\n const duration = maxBufferedEnd > presentation.duration! ? maxBufferedEnd : presentation.duration!;\n mediaSource.duration = duration;\n };\n\n writeWhenReady();\n\n return () => controller.abort();\n },\n },\n },\n });\n}\n\nexport const updateMediaSourceDuration = defineBehavior({\n stateKeys: ['presentation'],\n contextKeys: ['mediaSource'],\n setup: updateMediaSourceDurationSetup,\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsDA,SAAS,YACP,cACA,aACkB;AAClB,QAAO,qBAAqB,cAAc,YAAY,GAAG,sBAAsB;;AAGjF,SAAS,+BAA+B,EACtC,OACA,WAIyD;CAMzD,MAAM,qBAAqB,eAAe,YAAY,MAAM,aAAa,KAAK,EAAE,QAAQ,YAAY,KAAK,CAAC,CAAC;AAE3G,QAAO,qBAAuC;EAC5C,SAAS;EACT,eAAe,mBAAmB,KAAK;EACvC,QAAQ;GACN,uBAAuB,EAAE;GAEzB,qBAAqB,EAKnB,aAAa;IACX,MAAM,eAAe,MAAM,aAAa,KAAK;IAC7C,MAAM,cAAc,QAAQ,YAAY,KAAK;AAO7C,QAAI,CAAC,OAAO,MAAM,YAAY,SAAS,CAAE;IAEzC,MAAM,aAAa,IAAI,iBAAiB;IAExC,MAAM,iBAAiB,YAAY;AACjC,WAAM,uBAAuB,aAAa,WAAW,OAAO;AAC5D,SAAI,WAAW,OAAO,QAAS;AAI/B,SAAI,YAAY,eAAe,OAAQ;AAEvC,WAAM,0BAA0B,YAAY,eAAe,WAAW,OAAO;AAC7E,SAAI,WAAW,OAAO,QAAS;AAM/B,SAAI,YAAY,eAAe,OAAQ;KAKvC,MAAM,iBAAiB,kBAAkB,YAAY,cAAc;AAEnE,iBAAY,WADK,iBAAiB,aAAa,WAAY,iBAAiB,aAAa;;AAI3F,oBAAgB;AAEhB,iBAAa,WAAW,OAAO;MAElC;GACF;EACF,CAAC;;AAGJ,MAAa,4BAA4B,eAAe;CACtD,WAAW,CAAC,eAAe;CAC3B,aAAa,CAAC,cAAc;CAC5B,OAAO;CACR,CAAC"}