@videojs/spf 10.0.0-beta.1 → 10.0.0-beta.11

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 (242) hide show
  1. package/README.md +16 -40
  2. package/dist/default/all.js +16 -0
  3. package/dist/default/core/abr/bandwidth-estimator.js +96 -0
  4. package/dist/default/core/abr/bandwidth-estimator.js.map +1 -0
  5. package/dist/default/core/abr/ewma.js +71 -0
  6. package/dist/default/core/abr/ewma.js.map +1 -0
  7. package/dist/default/core/abr/quality-selection.js +53 -0
  8. package/dist/default/core/abr/quality-selection.js.map +1 -0
  9. package/dist/default/core/buffer/back-buffer.js +46 -0
  10. package/dist/default/core/buffer/back-buffer.js.map +1 -0
  11. package/dist/default/core/buffer/forward-buffer.js +73 -0
  12. package/dist/default/core/buffer/forward-buffer.js.map +1 -0
  13. package/dist/default/core/events/create-event-stream.js +48 -0
  14. package/dist/default/core/events/create-event-stream.js.map +1 -0
  15. package/dist/default/core/features/calculate-presentation-duration.js +51 -0
  16. package/dist/default/core/features/calculate-presentation-duration.js.map +1 -0
  17. package/dist/default/core/features/quality-switching.js +68 -0
  18. package/dist/default/core/features/quality-switching.js.map +1 -0
  19. package/dist/default/core/features/resolve-presentation.js +99 -0
  20. package/dist/default/core/features/resolve-presentation.js.map +1 -0
  21. package/dist/default/core/features/resolve-track.js +77 -0
  22. package/dist/default/core/features/resolve-track.js.map +1 -0
  23. package/dist/default/core/features/select-tracks.js +147 -0
  24. package/dist/default/core/features/select-tracks.js.map +1 -0
  25. package/dist/default/core/hls/parse-attributes.js +134 -0
  26. package/dist/default/core/hls/parse-attributes.js.map +1 -0
  27. package/dist/default/core/hls/parse-media-playlist.js +83 -0
  28. package/dist/default/core/hls/parse-media-playlist.js.map +1 -0
  29. package/dist/default/core/hls/parse-multivariant.js +215 -0
  30. package/dist/default/core/hls/parse-multivariant.js.map +1 -0
  31. package/dist/default/core/hls/resolve-url.js +11 -0
  32. package/dist/default/core/hls/resolve-url.js.map +1 -0
  33. package/dist/default/core/reactive/combine-latest.js +72 -0
  34. package/dist/default/core/reactive/combine-latest.js.map +1 -0
  35. package/dist/default/core/state/create-state.js +143 -0
  36. package/dist/default/core/state/create-state.js.map +1 -0
  37. package/dist/default/core/task.js +122 -0
  38. package/dist/default/core/task.js.map +1 -0
  39. package/dist/default/core/types/index.js +15 -0
  40. package/dist/default/core/types/index.js.map +1 -0
  41. package/dist/default/core/utils/generate-id.js +21 -0
  42. package/dist/default/core/utils/generate-id.js.map +1 -0
  43. package/dist/default/core/utils/track-selection.js +31 -0
  44. package/dist/default/core/utils/track-selection.js.map +1 -0
  45. package/dist/default/dom/features/end-of-stream.js +169 -0
  46. package/dist/default/dom/features/end-of-stream.js.map +1 -0
  47. package/dist/default/dom/features/load-segments.js +188 -0
  48. package/dist/default/dom/features/load-segments.js.map +1 -0
  49. package/dist/default/dom/features/load-text-track-cues.js +151 -0
  50. package/dist/default/dom/features/load-text-track-cues.js.map +1 -0
  51. package/dist/default/dom/features/segment-loader-actor.js +197 -0
  52. package/dist/default/dom/features/segment-loader-actor.js.map +1 -0
  53. package/dist/default/dom/features/setup-mediasource.js +55 -0
  54. package/dist/default/dom/features/setup-mediasource.js.map +1 -0
  55. package/dist/default/dom/features/setup-sourcebuffer.js +70 -0
  56. package/dist/default/dom/features/setup-sourcebuffer.js.map +1 -0
  57. package/dist/default/dom/features/setup-text-tracks.js +88 -0
  58. package/dist/default/dom/features/setup-text-tracks.js.map +1 -0
  59. package/dist/default/dom/features/sync-selected-text-track-from-dom.js +63 -0
  60. package/dist/default/dom/features/sync-selected-text-track-from-dom.js.map +1 -0
  61. package/dist/default/dom/features/sync-text-track-modes.js +38 -0
  62. package/dist/default/dom/features/sync-text-track-modes.js.map +1 -0
  63. package/dist/default/dom/features/track-current-time.js +46 -0
  64. package/dist/default/dom/features/track-current-time.js.map +1 -0
  65. package/dist/default/dom/features/track-playback-initiated.js +55 -0
  66. package/dist/default/dom/features/track-playback-initiated.js.map +1 -0
  67. package/dist/{dom/index.js → default/dom/features/track-playback-rate.js} +2 -4
  68. package/dist/default/dom/features/track-playback-rate.js.map +1 -0
  69. package/dist/default/dom/features/update-duration.js +72 -0
  70. package/dist/default/dom/features/update-duration.js.map +1 -0
  71. package/dist/default/dom/media/append-segment.js +60 -0
  72. package/dist/default/dom/media/append-segment.js.map +1 -0
  73. package/dist/default/dom/media/buffer-flusher.js +55 -0
  74. package/dist/default/dom/media/buffer-flusher.js.map +1 -0
  75. package/dist/default/dom/media/mediasource-setup.js +154 -0
  76. package/dist/default/dom/media/mediasource-setup.js.map +1 -0
  77. package/dist/default/dom/media/source-buffer-actor.js +186 -0
  78. package/dist/default/dom/media/source-buffer-actor.js.map +1 -0
  79. package/dist/default/dom/network/chunked-stream-iterable.js +49 -0
  80. package/dist/default/dom/network/chunked-stream-iterable.js.map +1 -0
  81. package/dist/default/dom/network/fetch.js +56 -0
  82. package/dist/default/dom/network/fetch.js.map +1 -0
  83. package/dist/default/dom/playback-engine/adapter.js +95 -0
  84. package/dist/default/dom/playback-engine/adapter.js.map +1 -0
  85. package/dist/default/dom/playback-engine/engine.js +176 -0
  86. package/dist/default/dom/playback-engine/engine.js.map +1 -0
  87. package/dist/default/dom/playback-engine.js +4 -0
  88. package/dist/default/dom/text/parse-vtt-segment.js +56 -0
  89. package/dist/default/dom/text/parse-vtt-segment.js.map +1 -0
  90. package/dist/default/dom.js +11 -0
  91. package/dist/{src-DRsS_H80.js → default/index.js} +2 -2
  92. package/dist/default/index.js.map +1 -0
  93. package/dist/dev/all.js +16 -0
  94. package/dist/dev/core/abr/bandwidth-estimator.d.ts +35 -0
  95. package/dist/dev/core/abr/bandwidth-estimator.d.ts.map +1 -0
  96. package/dist/dev/core/abr/bandwidth-estimator.js +96 -0
  97. package/dist/dev/core/abr/bandwidth-estimator.js.map +1 -0
  98. package/dist/dev/core/abr/ewma.js +71 -0
  99. package/dist/dev/core/abr/ewma.js.map +1 -0
  100. package/dist/dev/core/abr/quality-selection.js +53 -0
  101. package/dist/dev/core/abr/quality-selection.js.map +1 -0
  102. package/dist/dev/core/actor.d.ts +31 -0
  103. package/dist/dev/core/actor.d.ts.map +1 -0
  104. package/dist/dev/core/buffer/back-buffer.js +46 -0
  105. package/dist/dev/core/buffer/back-buffer.js.map +1 -0
  106. package/dist/dev/core/buffer/forward-buffer.js +73 -0
  107. package/dist/dev/core/buffer/forward-buffer.js.map +1 -0
  108. package/dist/dev/core/events/create-event-stream.d.ts +48 -0
  109. package/dist/dev/core/events/create-event-stream.d.ts.map +1 -0
  110. package/dist/dev/core/events/create-event-stream.js +48 -0
  111. package/dist/dev/core/events/create-event-stream.js.map +1 -0
  112. package/dist/dev/core/features/calculate-presentation-duration.js +51 -0
  113. package/dist/dev/core/features/calculate-presentation-duration.js.map +1 -0
  114. package/dist/dev/core/features/quality-switching.js +68 -0
  115. package/dist/dev/core/features/quality-switching.js.map +1 -0
  116. package/dist/dev/core/features/resolve-presentation.d.ts +16 -0
  117. package/dist/dev/core/features/resolve-presentation.d.ts.map +1 -0
  118. package/dist/dev/core/features/resolve-presentation.js +99 -0
  119. package/dist/dev/core/features/resolve-presentation.js.map +1 -0
  120. package/dist/dev/core/features/resolve-track.d.ts +13 -0
  121. package/dist/dev/core/features/resolve-track.d.ts.map +1 -0
  122. package/dist/dev/core/features/resolve-track.js +77 -0
  123. package/dist/dev/core/features/resolve-track.js.map +1 -0
  124. package/dist/dev/core/features/select-tracks.d.ts +11 -0
  125. package/dist/dev/core/features/select-tracks.d.ts.map +1 -0
  126. package/dist/dev/core/features/select-tracks.js +147 -0
  127. package/dist/dev/core/features/select-tracks.js.map +1 -0
  128. package/dist/dev/core/hls/parse-attributes.js +134 -0
  129. package/dist/dev/core/hls/parse-attributes.js.map +1 -0
  130. package/dist/dev/core/hls/parse-media-playlist.js +83 -0
  131. package/dist/dev/core/hls/parse-media-playlist.js.map +1 -0
  132. package/dist/dev/core/hls/parse-multivariant.js +215 -0
  133. package/dist/dev/core/hls/parse-multivariant.js.map +1 -0
  134. package/dist/dev/core/hls/resolve-url.js +11 -0
  135. package/dist/dev/core/hls/resolve-url.js.map +1 -0
  136. package/dist/dev/core/reactive/combine-latest.js +72 -0
  137. package/dist/dev/core/reactive/combine-latest.js.map +1 -0
  138. package/dist/dev/core/state/create-state.d.ts +104 -0
  139. package/dist/dev/core/state/create-state.d.ts.map +1 -0
  140. package/dist/dev/core/state/create-state.js +143 -0
  141. package/dist/dev/core/state/create-state.js.map +1 -0
  142. package/dist/dev/core/task.js +122 -0
  143. package/dist/dev/core/task.js.map +1 -0
  144. package/dist/dev/core/types/index.d.ts +173 -0
  145. package/dist/dev/core/types/index.d.ts.map +1 -0
  146. package/dist/dev/core/types/index.js +15 -0
  147. package/dist/dev/core/types/index.js.map +1 -0
  148. package/dist/dev/core/utils/generate-id.js +21 -0
  149. package/dist/dev/core/utils/generate-id.js.map +1 -0
  150. package/dist/dev/core/utils/track-selection.d.ts +16 -0
  151. package/dist/dev/core/utils/track-selection.d.ts.map +1 -0
  152. package/dist/dev/core/utils/track-selection.js +31 -0
  153. package/dist/dev/core/utils/track-selection.js.map +1 -0
  154. package/dist/dev/dom/features/end-of-stream.js +169 -0
  155. package/dist/dev/dom/features/end-of-stream.js.map +1 -0
  156. package/dist/dev/dom/features/load-segments.d.ts +72 -0
  157. package/dist/dev/dom/features/load-segments.d.ts.map +1 -0
  158. package/dist/dev/dom/features/load-segments.js +188 -0
  159. package/dist/dev/dom/features/load-segments.js.map +1 -0
  160. package/dist/dev/dom/features/load-text-track-cues.d.ts +61 -0
  161. package/dist/dev/dom/features/load-text-track-cues.d.ts.map +1 -0
  162. package/dist/dev/dom/features/load-text-track-cues.js +151 -0
  163. package/dist/dev/dom/features/load-text-track-cues.js.map +1 -0
  164. package/dist/dev/dom/features/segment-loader-actor.js +197 -0
  165. package/dist/dev/dom/features/segment-loader-actor.js.map +1 -0
  166. package/dist/dev/dom/features/setup-mediasource.js +55 -0
  167. package/dist/dev/dom/features/setup-mediasource.js.map +1 -0
  168. package/dist/dev/dom/features/setup-sourcebuffer.d.ts +9 -0
  169. package/dist/dev/dom/features/setup-sourcebuffer.d.ts.map +1 -0
  170. package/dist/dev/dom/features/setup-sourcebuffer.js +70 -0
  171. package/dist/dev/dom/features/setup-sourcebuffer.js.map +1 -0
  172. package/dist/dev/dom/features/setup-text-tracks.js +88 -0
  173. package/dist/dev/dom/features/setup-text-tracks.js.map +1 -0
  174. package/dist/dev/dom/features/sync-selected-text-track-from-dom.js +63 -0
  175. package/dist/dev/dom/features/sync-selected-text-track-from-dom.js.map +1 -0
  176. package/dist/dev/dom/features/sync-text-track-modes.js +38 -0
  177. package/dist/dev/dom/features/sync-text-track-modes.js.map +1 -0
  178. package/dist/dev/dom/features/track-current-time.d.ts +40 -0
  179. package/dist/dev/dom/features/track-current-time.d.ts.map +1 -0
  180. package/dist/dev/dom/features/track-current-time.js +46 -0
  181. package/dist/dev/dom/features/track-current-time.js.map +1 -0
  182. package/dist/dev/dom/features/track-playback-initiated.d.ts +53 -0
  183. package/dist/dev/dom/features/track-playback-initiated.d.ts.map +1 -0
  184. package/dist/dev/dom/features/track-playback-initiated.js +55 -0
  185. package/dist/dev/dom/features/track-playback-initiated.js.map +1 -0
  186. package/dist/dev/dom/features/track-playback-rate.d.ts +34 -0
  187. package/dist/dev/dom/features/track-playback-rate.d.ts.map +1 -0
  188. package/dist/dev/dom/features/track-playback-rate.js +36 -0
  189. package/dist/dev/dom/features/track-playback-rate.js.map +1 -0
  190. package/dist/dev/dom/features/update-duration.js +72 -0
  191. package/dist/dev/dom/features/update-duration.js.map +1 -0
  192. package/dist/dev/dom/media/append-segment.d.ts +24 -0
  193. package/dist/dev/dom/media/append-segment.d.ts.map +1 -0
  194. package/dist/dev/dom/media/append-segment.js +60 -0
  195. package/dist/dev/dom/media/append-segment.js.map +1 -0
  196. package/dist/dev/dom/media/buffer-flusher.d.ts +24 -0
  197. package/dist/dev/dom/media/buffer-flusher.d.ts.map +1 -0
  198. package/dist/dev/dom/media/buffer-flusher.js +55 -0
  199. package/dist/dev/dom/media/buffer-flusher.js.map +1 -0
  200. package/dist/dev/dom/media/mediasource-setup.js +154 -0
  201. package/dist/dev/dom/media/mediasource-setup.js.map +1 -0
  202. package/dist/dev/dom/media/source-buffer-actor.d.ts +56 -0
  203. package/dist/dev/dom/media/source-buffer-actor.d.ts.map +1 -0
  204. package/dist/dev/dom/media/source-buffer-actor.js +186 -0
  205. package/dist/dev/dom/media/source-buffer-actor.js.map +1 -0
  206. package/dist/dev/dom/network/chunked-stream-iterable.js +49 -0
  207. package/dist/dev/dom/network/chunked-stream-iterable.js.map +1 -0
  208. package/dist/dev/dom/network/fetch.js +56 -0
  209. package/dist/dev/dom/network/fetch.js.map +1 -0
  210. package/dist/dev/dom/playback-engine/adapter.d.ts +41 -0
  211. package/dist/dev/dom/playback-engine/adapter.d.ts.map +1 -0
  212. package/dist/dev/dom/playback-engine/adapter.js +95 -0
  213. package/dist/dev/dom/playback-engine/adapter.js.map +1 -0
  214. package/dist/dev/dom/playback-engine/engine.d.ts +135 -0
  215. package/dist/dev/dom/playback-engine/engine.d.ts.map +1 -0
  216. package/dist/dev/dom/playback-engine/engine.js +176 -0
  217. package/dist/dev/dom/playback-engine/engine.js.map +1 -0
  218. package/dist/dev/dom/playback-engine.d.ts +3 -0
  219. package/dist/dev/dom/playback-engine.js +4 -0
  220. package/dist/dev/dom/text/parse-vtt-segment.d.ts +12 -0
  221. package/dist/dev/dom/text/parse-vtt-segment.d.ts.map +1 -0
  222. package/dist/dev/dom/text/parse-vtt-segment.js +56 -0
  223. package/dist/dev/dom/text/parse-vtt-segment.js.map +1 -0
  224. package/dist/dev/dom.d.ts +10 -0
  225. package/dist/dev/dom.js +11 -0
  226. package/dist/dev/index.d.ts.map +1 -0
  227. package/dist/dev/index.js +14 -0
  228. package/dist/dev/index.js.map +1 -0
  229. package/package.json +22 -22
  230. package/dist/adapter-CMw-rvbk.js +0 -3512
  231. package/dist/adapter-CMw-rvbk.js.map +0 -1
  232. package/dist/adapter-l8iJN_GI.d.ts +0 -689
  233. package/dist/adapter-l8iJN_GI.d.ts.map +0 -1
  234. package/dist/dom/index.d.ts +0 -243
  235. package/dist/dom/index.d.ts.map +0 -1
  236. package/dist/dom/index.js.map +0 -1
  237. package/dist/dom/playback-engine/index.d.ts +0 -2
  238. package/dist/dom/playback-engine/index.js +0 -4
  239. package/dist/index.d.ts.map +0 -1
  240. package/dist/index.js +0 -3
  241. package/dist/src-DRsS_H80.js.map +0 -1
  242. /package/dist/{index.d.ts → dev/index.d.ts} +0 -0
@@ -0,0 +1,72 @@
1
+ import { BandwidthState } from "../../core/abr/bandwidth-estimator.js";
2
+ import { WritableState } from "../../core/state/create-state.js";
3
+ import { Presentation } from "../../core/types/index.js";
4
+ import { TrackSelectionState } from "../../core/utils/track-selection.js";
5
+ import { SourceBufferActor } from "../media/source-buffer-actor.js";
6
+ import { MediaTrackType } from "./setup-sourcebuffer.js";
7
+
8
+ //#region src/dom/features/load-segments.d.ts
9
+ /**
10
+ * State shape for segment loading.
11
+ */
12
+ interface SegmentLoadingState extends TrackSelectionState {
13
+ presentation?: Presentation;
14
+ preload?: string;
15
+ bandwidthState?: BandwidthState;
16
+ /** Current playback position in seconds. Defaults to 0 when undefined. */
17
+ currentTime?: number;
18
+ /** True once the user has initiated playback. Allows segment loading regardless of preload setting. */
19
+ playbackInitiated?: boolean;
20
+ }
21
+ /**
22
+ * Owners shape for segment loading.
23
+ */
24
+ interface SegmentLoadingOwners {
25
+ videoBuffer?: SourceBuffer;
26
+ audioBuffer?: SourceBuffer;
27
+ videoBufferActor?: SourceBufferActor;
28
+ audioBufferActor?: SourceBufferActor;
29
+ }
30
+ /**
31
+ * Load segments orchestration — Reactor layer.
32
+ *
33
+ * Sends typed load messages to a SegmentLoaderActor when relevant conditions
34
+ * change. Uses targeted subscriptions rather than broad combineLatest so only
35
+ * meaningful state changes trigger evaluation.
36
+ *
37
+ * Condition hierarchy (see SegmentLoadingKey for detail):
38
+ *
39
+ * !playbackInitiated
40
+ * preload==='none' (or unset) → dormant; no trigger
41
+ * preload==='metadata' → trigger on transition to 'metadata'
42
+ * preload==='auto' → trigger on transition to 'auto'
43
+ *
44
+ * !playbackInitiated → playbackInitiated
45
+ * preload !== 'auto' → trigger (message shape changes)
46
+ * preload === 'auto' → suppressed (was already full-range mode;
47
+ * let segmentStart take over post-play)
48
+ * KNOWN LIMITATION: seek-before-play with
49
+ * preload='auto' is not supported — if the
50
+ * user seeks before pressing play, the
51
+ * first re-send is delayed until the next
52
+ * segment boundary crossing post-play.
53
+ *
54
+ * playbackInitiated
55
+ * resolvedTrackId changes → trigger
56
+ * segmentStart(currentTime) changes → trigger (segment boundary only)
57
+ *
58
+ * @example
59
+ * const cleanup = loadSegments({ state, owners }, { type: 'video' });
60
+ */
61
+ declare function loadSegments({
62
+ state,
63
+ owners
64
+ }: {
65
+ state: WritableState<SegmentLoadingState>;
66
+ owners: WritableState<SegmentLoadingOwners>;
67
+ }, config: {
68
+ type: MediaTrackType;
69
+ }): () => void;
70
+ //#endregion
71
+ export { loadSegments };
72
+ //# sourceMappingURL=load-segments.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load-segments.d.ts","names":[],"sources":["../../../../src/dom/features/load-segments.ts"],"mappings":";;;;;;;;AA8FA;;;AAAA,UAAiB,mBAAA,SAA4B,mBAAA;EAC3C,YAAA,GAAe,YAAA;EACf,OAAA;EACA,cAAA,GAAiB,cAAA;EAH6C;EAK9D,WAAA;EAJA;EAMA,iBAAA;AAAA;;;;UAMe,oBAAA;EACf,WAAA,GAAc,YAAA;EACd,WAAA,GAAc,YAAA;EACd,gBAAA,GAAmB,iBAAA;EACnB,gBAAA,GAAmB,iBAAA;AAAA;;;;;;;;;;;;;;;;;AAgIrB;;;;;;;;;;;;;;;iBAAgB,YAAA,CAAA;EAEZ,KAAA;EACA;AAAA;EAEA,KAAA,EAAO,aAAA,CAAc,mBAAA;EACrB,MAAA,EAAQ,aAAA,CAAc,oBAAA;AAAA,GAExB,MAAA;EAAU,IAAA,EAAM,cAAA;AAAA"}
@@ -0,0 +1,188 @@
1
+ import { createState } from "../../core/state/create-state.js";
2
+ import { sampleBandwidth } from "../../core/abr/bandwidth-estimator.js";
3
+ import { DEFAULT_FORWARD_BUFFER_CONFIG } from "../../core/buffer/forward-buffer.js";
4
+ import { isResolvedTrack } from "../../core/types/index.js";
5
+ import { ChunkedStreamIterable } from "../network/chunked-stream-iterable.js";
6
+ import { fetchResolvable } from "../network/fetch.js";
7
+ import { combineLatest } from "../../core/reactive/combine-latest.js";
8
+ import "../../all.js";
9
+ import { getSelectedTrack } from "../../core/utils/track-selection.js";
10
+ import { createSegmentLoaderActor } from "./segment-loader-actor.js";
11
+
12
+ //#region src/dom/features/load-segments.ts
13
+ const ActorKeyByType = {
14
+ video: "videoBufferActor",
15
+ audio: "audioBufferActor"
16
+ };
17
+ function createTrackedFetch(throughput, onSample) {
18
+ return async (addressable, options) => {
19
+ const { minChunkSize, ...fetchOptions } = options ?? {};
20
+ const response = await fetchResolvable(addressable, fetchOptions);
21
+ if (!response.body) throw new Error("Response has no body");
22
+ const body = response.body;
23
+ return { [Symbol.asyncIterator]: async function* () {
24
+ let chunkStart = performance.now();
25
+ for await (const chunk of new ChunkedStreamIterable(body, ...minChunkSize !== void 0 ? [{ minChunkSize }] : [])) {
26
+ const elapsed = performance.now() - chunkStart;
27
+ const next = sampleBandwidth(throughput.current, elapsed, chunk.byteLength);
28
+ throughput.patch(next);
29
+ throughput.flush();
30
+ onSample?.(next);
31
+ yield chunk;
32
+ chunkStart = performance.now();
33
+ }
34
+ } };
35
+ };
36
+ }
37
+ /**
38
+ * Non-tracking fetch: eagerly starts the request and returns the response body
39
+ * as a lazy chunk iterable. Used for audio tracks which don't sample bandwidth.
40
+ * Pass `minChunkSize: Infinity` to accumulate the full body as a single chunk
41
+ * (equivalent to arrayBuffer() but through the same streaming path).
42
+ */
43
+ async function fetchStream(addressable, options) {
44
+ const { minChunkSize, ...fetchOptions } = options ?? {};
45
+ const response = await fetchResolvable(addressable, fetchOptions);
46
+ if (!response.body) throw new Error("Response has no body");
47
+ return new ChunkedStreamIterable(response.body, ...minChunkSize !== void 0 ? [{ minChunkSize }] : []);
48
+ }
49
+ function selectLoadingInputs([segmentsCanLoad, state], type) {
50
+ const { playbackInitiated, preload, currentTime } = state;
51
+ return {
52
+ playbackInitiated,
53
+ preload,
54
+ currentTime,
55
+ track: getSelectedTrack(state, type),
56
+ segmentsCanLoad
57
+ };
58
+ }
59
+ /**
60
+ * Equality function encoding the condition hierarchy for relevant changes.
61
+ *
62
+ * Pre-play (!playbackInitiated):
63
+ * Only preload changes matter. currentTime and resolvedTrackId are ignored
64
+ * (track changes not supported pre-play; currentTime value is used at
65
+ * trigger time but changes don't re-trigger).
66
+ *
67
+ * playbackInitiated transition:
68
+ * Always fires (handled in the subscriber; preload='auto' suppression
69
+ * applied there since equality functions have no memory of prior values).
70
+ *
71
+ * Post-play (playbackInitiated):
72
+ * resolvedTrackId changes (track switch or previously-unresolved track
73
+ * resolving) and currentTime changes both trigger. preload is irrelevant.
74
+ */
75
+ const segmentStartFor = (currentTime, track) => {
76
+ if (currentTime == null) return void 0;
77
+ return track?.segments.find(({ startTime, duration }, i, segments) => currentTime >= startTime && (currentTime < startTime + duration || i === segments.length - 1))?.startTime;
78
+ };
79
+ /**
80
+ * Returns true when the inputs are equal (no meaningful change — don't fire).
81
+ * Returns false when the inputs differ in a way that requires a new message.
82
+ *
83
+ * This IS the shouldLoadSegments logic, expressed as an equality function.
84
+ */
85
+ function loadingInputsEq(prevState, curState) {
86
+ if (!curState.segmentsCanLoad) return true;
87
+ if (!curState.playbackInitiated) {
88
+ if (curState.preload === "none") return true;
89
+ return curState.preload === prevState.preload;
90
+ }
91
+ if (!prevState.playbackInitiated && curState.playbackInitiated) {
92
+ if (prevState.preload !== "auto") return false;
93
+ }
94
+ if (!curState.track || !isResolvedTrack(curState.track)) return true;
95
+ if (prevState.track?.id !== curState.track.id && isResolvedTrack(curState.track)) return false;
96
+ return segmentStartFor(prevState.currentTime, curState.track) === segmentStartFor(curState.currentTime, curState.track);
97
+ }
98
+ /**
99
+ * Load segments orchestration — Reactor layer.
100
+ *
101
+ * Sends typed load messages to a SegmentLoaderActor when relevant conditions
102
+ * change. Uses targeted subscriptions rather than broad combineLatest so only
103
+ * meaningful state changes trigger evaluation.
104
+ *
105
+ * Condition hierarchy (see SegmentLoadingKey for detail):
106
+ *
107
+ * !playbackInitiated
108
+ * preload==='none' (or unset) → dormant; no trigger
109
+ * preload==='metadata' → trigger on transition to 'metadata'
110
+ * preload==='auto' → trigger on transition to 'auto'
111
+ *
112
+ * !playbackInitiated → playbackInitiated
113
+ * preload !== 'auto' → trigger (message shape changes)
114
+ * preload === 'auto' → suppressed (was already full-range mode;
115
+ * let segmentStart take over post-play)
116
+ * KNOWN LIMITATION: seek-before-play with
117
+ * preload='auto' is not supported — if the
118
+ * user seeks before pressing play, the
119
+ * first re-send is delayed until the next
120
+ * segment boundary crossing post-play.
121
+ *
122
+ * playbackInitiated
123
+ * resolvedTrackId changes → trigger
124
+ * segmentStart(currentTime) changes → trigger (segment boundary only)
125
+ *
126
+ * @example
127
+ * const cleanup = loadSegments({ state, owners }, { type: 'video' });
128
+ */
129
+ function loadSegments({ state, owners }, config) {
130
+ const { type } = config;
131
+ const actorKey = ActorKeyByType[type];
132
+ const initialBandwidth = state.current.bandwidthState;
133
+ const throughput = createState(initialBandwidth ?? {
134
+ fastEstimate: 0,
135
+ fastTotalWeight: 0,
136
+ slowEstimate: 0,
137
+ slowTotalWeight: 0,
138
+ bytesSampled: 0
139
+ });
140
+ const fetchBytes = type === "video" ? createTrackedFetch(throughput, initialBandwidth !== void 0 ? (next) => {
141
+ state.patch({ bandwidthState: next });
142
+ state.flush();
143
+ } : void 0) : fetchStream;
144
+ const segmentLoader = createState(void 0);
145
+ const unsubActorLifecycle = owners.subscribe((o) => o[actorKey], (actor) => {
146
+ if (actor) segmentLoader.patch(createSegmentLoaderActor(actor, fetchBytes));
147
+ else if (!actor && segmentLoader.current) {
148
+ segmentLoader.current.destroy();
149
+ segmentLoader.patch(void 0);
150
+ }
151
+ return () => {
152
+ segmentLoader.current?.destroy();
153
+ segmentLoader.patch(void 0);
154
+ };
155
+ });
156
+ const segmentsCanLoad = createState(false);
157
+ const unsubscribeCanLoadSegments = combineLatest([state, segmentLoader]).subscribe(([currentState, currentSegmentLoader]) => {
158
+ const track = getSelectedTrack(currentState, type);
159
+ const trackResolved = !!track && isResolvedTrack(track);
160
+ const segmentLoaderActorExists = !!currentSegmentLoader;
161
+ segmentsCanLoad.patch(trackResolved && segmentLoaderActorExists);
162
+ });
163
+ const unsubscribeShouldLoadSegments = combineLatest([segmentsCanLoad, state]).subscribe(([segmentsCanLoad, state]) => selectLoadingInputs([segmentsCanLoad, state], type), ({ preload, playbackInitiated, currentTime, track }) => {
164
+ if (!(preload === "auto" || !!playbackInitiated))
165
+ /** @ts-expect-error */
166
+ segmentLoader.current?.send({
167
+ type: "load",
168
+ track
169
+ });
170
+ else segmentLoader.current?.send({
171
+ type: "load",
172
+ track,
173
+ range: {
174
+ start: currentTime,
175
+ end: currentTime + DEFAULT_FORWARD_BUFFER_CONFIG.bufferDuration
176
+ }
177
+ });
178
+ }, { equalityFn: loadingInputsEq });
179
+ return () => {
180
+ unsubscribeCanLoadSegments();
181
+ unsubscribeShouldLoadSegments();
182
+ unsubActorLifecycle();
183
+ };
184
+ }
185
+
186
+ //#endregion
187
+ export { loadSegments };
188
+ //# sourceMappingURL=load-segments.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load-segments.js","names":[],"sources":["../../../../src/dom/features/load-segments.ts"],"sourcesContent":["import { combineLatest } from '../../all';\nimport { type BandwidthState, sampleBandwidth } from '../../core/abr/bandwidth-estimator';\nimport { DEFAULT_FORWARD_BUFFER_CONFIG } from '../../core/buffer/forward-buffer';\nimport { createState, type WritableState } from '../../core/state/create-state';\nimport type { AddressableObject, Presentation, ResolvedTrack } from '../../core/types';\nimport { isResolvedTrack } from '../../core/types';\nimport { getSelectedTrack, type TrackSelectionState } from '../../core/utils/track-selection';\nimport type { SourceBufferActor } from '../media/source-buffer-actor';\nimport { ChunkedStreamIterable, type ChunkedStreamIterableOptions } from '../network/chunked-stream-iterable';\nimport { fetchResolvable } from '../network/fetch';\nimport {\n type BufferState,\n createSegmentLoaderActor,\n type SegmentLoaderActor,\n type SourceBufferState,\n} from './segment-loader-actor';\nimport type { MediaTrackType } from './setup-sourcebuffer';\n\n// Re-export buffer state types for consumers that import them from this module.\nexport type { BufferState, SourceBufferState };\n\n// Map track type to SourceBufferActor owner property key.\nconst ActorKeyByType = {\n video: 'videoBufferActor',\n audio: 'audioBufferActor',\n} as const;\n\n// ============================================================================\n// TRACKED FETCH\n// ============================================================================\n\n/**\n * Creates a fetch function that eagerly starts the HTTP request (TTFB is\n * awaited), then returns a lazy iterable over the response body that\n * transparently samples bandwidth per chunk.\n *\n * Separating connection start from body reading ensures `fetch()` is called\n * as soon as the task begins — not deferred until the actor's append loop\n * first pulls a chunk. This makes fetch timing predictable and observable\n * (e.g. in tests that record fetched URLs) regardless of downstream consumers.\n *\n * `onSample` bridges throughput state outward; see Phase 2 comment for detail.\n */\ntype FetchOptions = RequestInit & ChunkedStreamIterableOptions;\n\nfunction createTrackedFetch(\n throughput: WritableState<BandwidthState>,\n onSample?: (next: BandwidthState) => void\n): (addressable: AddressableObject, options?: FetchOptions) => Promise<AsyncIterable<Uint8Array>> {\n return async (addressable, options) => {\n const { minChunkSize, ...fetchOptions } = options ?? {};\n const response = await fetchResolvable(addressable, fetchOptions);\n if (!response.body) throw new Error('Response has no body');\n const body = response.body;\n return {\n [Symbol.asyncIterator]: async function* () {\n let chunkStart = performance.now();\n for await (const chunk of new ChunkedStreamIterable(\n body,\n ...(minChunkSize !== undefined ? [{ minChunkSize }] : [])\n )) {\n const elapsed = performance.now() - chunkStart;\n const next = sampleBandwidth(throughput.current, elapsed, chunk.byteLength);\n throughput.patch(next);\n throughput.flush();\n onSample?.(next);\n yield chunk;\n chunkStart = performance.now();\n }\n },\n };\n };\n}\n\n/**\n * Non-tracking fetch: eagerly starts the request and returns the response body\n * as a lazy chunk iterable. Used for audio tracks which don't sample bandwidth.\n * Pass `minChunkSize: Infinity` to accumulate the full body as a single chunk\n * (equivalent to arrayBuffer() but through the same streaming path).\n */\nasync function fetchStream(addressable: AddressableObject, options?: FetchOptions): Promise<AsyncIterable<Uint8Array>> {\n const { minChunkSize, ...fetchOptions } = options ?? {};\n const response = await fetchResolvable(addressable, fetchOptions);\n if (!response.body) throw new Error('Response has no body');\n return new ChunkedStreamIterable(response.body, ...(minChunkSize !== undefined ? [{ minChunkSize }] : []));\n}\n\n// ============================================================================\n// STATE & OWNERS\n// ============================================================================\n\n/**\n * State shape for segment loading.\n */\nexport interface SegmentLoadingState extends TrackSelectionState {\n presentation?: Presentation;\n preload?: string;\n bandwidthState?: BandwidthState;\n /** Current playback position in seconds. Defaults to 0 when undefined. */\n currentTime?: number;\n /** True once the user has initiated playback. Allows segment loading regardless of preload setting. */\n playbackInitiated?: boolean;\n}\n\n/**\n * Owners shape for segment loading.\n */\nexport interface SegmentLoadingOwners {\n videoBuffer?: SourceBuffer;\n audioBuffer?: SourceBuffer;\n videoBufferActor?: SourceBufferActor;\n audioBufferActor?: SourceBufferActor;\n}\n\n// ============================================================================\n// STATE SELECTOR\n// ============================================================================\n\n/**\n * The subset of state that drives segment loading decisions.\n * A plain \"pick\" with one derived field (resolved track ID).\n */\ntype LoadingInputs = {\n playbackInitiated: boolean | undefined;\n preload: string | undefined;\n currentTime: number | undefined;\n /** @TODO cleanup type precision via inference+generics */\n track: ReturnType<typeof getSelectedTrack>;\n segmentsCanLoad: boolean;\n};\n\nfunction selectLoadingInputs(\n [segmentsCanLoad, state]: [boolean, SegmentLoadingState],\n type: MediaTrackType\n): LoadingInputs {\n const { playbackInitiated, preload, currentTime } = state;\n const track = getSelectedTrack(state, type);\n return {\n playbackInitiated,\n preload,\n currentTime,\n track,\n segmentsCanLoad,\n };\n}\n\n/**\n * Equality function encoding the condition hierarchy for relevant changes.\n *\n * Pre-play (!playbackInitiated):\n * Only preload changes matter. currentTime and resolvedTrackId are ignored\n * (track changes not supported pre-play; currentTime value is used at\n * trigger time but changes don't re-trigger).\n *\n * playbackInitiated transition:\n * Always fires (handled in the subscriber; preload='auto' suppression\n * applied there since equality functions have no memory of prior values).\n *\n * Post-play (playbackInitiated):\n * resolvedTrackId changes (track switch or previously-unresolved track\n * resolving) and currentTime changes both trigger. preload is irrelevant.\n */\nconst segmentStartFor = (currentTime: number | undefined, track: ResolvedTrack | undefined) => {\n if (currentTime == null) return undefined;\n return track?.segments.find(\n ({ startTime, duration }, i, segments) =>\n currentTime >= startTime && (currentTime < startTime + duration || i === segments.length - 1)\n )?.startTime;\n};\n\n/**\n * Returns true when the inputs are equal (no meaningful change — don't fire).\n * Returns false when the inputs differ in a way that requires a new message.\n *\n * This IS the shouldLoadSegments logic, expressed as an equality function.\n */\nfunction loadingInputsEq(prevState: LoadingInputs, curState: LoadingInputs): boolean {\n if (!curState.segmentsCanLoad) return true;\n // Haven't started playback. Only care about preload changes.\n if (!curState.playbackInitiated) {\n if (curState.preload === 'none') return true; // blocked — equal, don't fire\n return curState.preload === prevState.preload; // equal if preload unchanged\n }\n // Transition: !playbackInitiated → playbackInitiated\n if (!prevState.playbackInitiated && curState.playbackInitiated) {\n if (prevState.preload !== 'auto') return false; // fire — message shape changes\n // preload was 'auto': fall through to segment comparison (suppress if same position)\n }\n\n // Don't treat as a change if we don't (yet) have a currently resolved track.\n if (!curState.track || !isResolvedTrack(curState.track)) return true;\n\n // If we *do* have a currently resolved track, treat as a change if the track ids have changed\n if (prevState.track?.id !== curState.track.id && isResolvedTrack(curState.track)) return false;\n\n // Finally, if playback has initiated *and* we have a resolved track, check if currentTime\n // has changed \"significantly\" (based on segment time range boundaries) and treat as a change\n // if so (regardless of whether or not the track has changed)\n return (\n segmentStartFor(prevState.currentTime, curState.track as ResolvedTrack) ===\n segmentStartFor(curState.currentTime, curState.track as ResolvedTrack)\n );\n}\n\n// ============================================================================\n// REACTOR\n// ============================================================================\n\n/**\n * Load segments orchestration — Reactor layer.\n *\n * Sends typed load messages to a SegmentLoaderActor when relevant conditions\n * change. Uses targeted subscriptions rather than broad combineLatest so only\n * meaningful state changes trigger evaluation.\n *\n * Condition hierarchy (see SegmentLoadingKey for detail):\n *\n * !playbackInitiated\n * preload==='none' (or unset) → dormant; no trigger\n * preload==='metadata' → trigger on transition to 'metadata'\n * preload==='auto' → trigger on transition to 'auto'\n *\n * !playbackInitiated → playbackInitiated\n * preload !== 'auto' → trigger (message shape changes)\n * preload === 'auto' → suppressed (was already full-range mode;\n * let segmentStart take over post-play)\n * KNOWN LIMITATION: seek-before-play with\n * preload='auto' is not supported — if the\n * user seeks before pressing play, the\n * first re-send is delayed until the next\n * segment boundary crossing post-play.\n *\n * playbackInitiated\n * resolvedTrackId changes → trigger\n * segmentStart(currentTime) changes → trigger (segment boundary only)\n *\n * @example\n * const cleanup = loadSegments({ state, owners }, { type: 'video' });\n */\nexport function loadSegments(\n {\n state,\n owners,\n }: {\n state: WritableState<SegmentLoadingState>;\n owners: WritableState<SegmentLoadingOwners>;\n },\n config: { type: MediaTrackType }\n): () => void {\n const { type } = config;\n const actorKey = ActorKeyByType[type];\n\n // Local throughput state — owns BandwidthState for this track's fetch loop.\n // Sampling is handled transparently inside fetchBytes; callers never touch it.\n //\n // MIGRATION BRIDGE: the onSample callback below keeps global state.bandwidthState\n // in sync so ABR (selectVideoTrack) continues to work unchanged. Remove this\n // bridge once ABR reads from throughput directly.\n const initialBandwidth = state.current.bandwidthState;\n const throughput = createState<BandwidthState>(\n initialBandwidth ?? {\n fastEstimate: 0,\n fastTotalWeight: 0,\n slowEstimate: 0,\n slowTotalWeight: 0,\n bytesSampled: 0,\n }\n );\n\n const fetchBytes =\n type === 'video'\n ? createTrackedFetch(\n throughput,\n initialBandwidth !== undefined\n ? (next) => {\n state.patch({ bandwidthState: next });\n // Flush immediately so switchQuality sees the new estimate before the\n // next segment fetch starts, rather than waiting for the microtask queue.\n state.flush();\n }\n : undefined\n )\n : fetchStream;\n\n const segmentLoader = createState<SegmentLoaderActor | undefined>(undefined);\n\n // SegmentLoaderActor lifecycle — watch only the actor key in owners so this\n // subscription does not fire on unrelated owners changes.\n const unsubActorLifecycle = owners.subscribe(\n (o) => o[actorKey],\n (actor) => {\n if (actor) {\n segmentLoader.patch(createSegmentLoaderActor(actor, fetchBytes));\n } else if (!actor && segmentLoader.current) {\n segmentLoader.current.destroy();\n segmentLoader.patch(undefined);\n }\n return () => {\n segmentLoader.current?.destroy();\n segmentLoader.patch(undefined);\n };\n }\n );\n\n const segmentsCanLoad = createState<boolean>(false);\n const unsubscribeCanLoadSegments = combineLatest([state, segmentLoader]).subscribe(\n ([currentState, currentSegmentLoader]) => {\n const track = getSelectedTrack(currentState, type);\n const trackResolved = !!track && isResolvedTrack(track);\n const segmentLoaderActorExists = !!currentSegmentLoader;\n segmentsCanLoad.patch(trackResolved && segmentLoaderActorExists);\n }\n );\n\n const unsubscribeShouldLoadSegments = combineLatest([segmentsCanLoad, state]).subscribe(\n ([segmentsCanLoad, state]) => selectLoadingInputs([segmentsCanLoad, state], type),\n ({ preload, playbackInitiated, currentTime, track }) => {\n const fullMode = preload === 'auto' || !!playbackInitiated;\n\n if (!fullMode) {\n // Metadata mode: init only, no range.\n /** @ts-expect-error */\n segmentLoader.current?.send({ type: 'load', track });\n } else {\n segmentLoader.current?.send({\n type: 'load',\n /** @ts-expect-error */\n track,\n range: {\n start: currentTime as number,\n end: (currentTime as number) + DEFAULT_FORWARD_BUFFER_CONFIG.bufferDuration,\n },\n });\n }\n },\n { equalityFn: loadingInputsEq }\n );\n\n return () => {\n unsubscribeCanLoadSegments();\n unsubscribeShouldLoadSegments();\n unsubActorLifecycle();\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ import { WritableState } from "../../core/state/create-state.js";
2
+ import { Presentation } from "../../core/types/index.js";
3
+
4
+ //#region src/dom/features/load-text-track-cues.d.ts
5
+ /**
6
+ * Loaded-segment record for a single text track.
7
+ */
8
+ interface TextTrackSegmentState {
9
+ segments: Array<{
10
+ id: string;
11
+ }>;
12
+ }
13
+ /**
14
+ * Buffer model for text track cues — keyed by track ID.
15
+ *
16
+ * Using a per-track-ID map (rather than fixed 'video'/'audio' keys) because
17
+ * there can be N text tracks — one per language/subtitle variant.
18
+ */
19
+ type TextTrackBufferState = Record<string, TextTrackSegmentState>;
20
+ /**
21
+ * State shape for text track cue loading.
22
+ */
23
+ interface TextTrackCueLoadingState {
24
+ selectedTextTrackId?: string;
25
+ presentation?: Presentation;
26
+ /** Current playback position — used to gate VTT segment fetching to the forward buffer window. */
27
+ currentTime?: number;
28
+ /** Loaded-segment model for text tracks, keyed by track ID. */
29
+ textBufferState?: TextTrackBufferState;
30
+ }
31
+ /**
32
+ * Owners shape for text track cue loading.
33
+ */
34
+ interface TextTrackCueLoadingOwners {
35
+ textTracks?: Map<string, HTMLTrackElement>;
36
+ }
37
+ /**
38
+ * Load text track cues orchestration.
39
+ *
40
+ * Triggers when:
41
+ * - Text track is selected
42
+ * - Track is resolved (has segments)
43
+ * - Track element exists
44
+ *
45
+ * Fetches and parses VTT segments within the forward buffer window, then adds
46
+ * cues to the track incrementally. Continues on segment errors to provide
47
+ * partial subtitles.
48
+ *
49
+ * @example
50
+ * const cleanup = loadTextTrackCues({ state, owners });
51
+ */
52
+ declare function loadTextTrackCues({
53
+ state,
54
+ owners
55
+ }: {
56
+ state: WritableState<TextTrackCueLoadingState>;
57
+ owners: WritableState<TextTrackCueLoadingOwners>;
58
+ }): () => void;
59
+ //#endregion
60
+ export { TextTrackBufferState, loadTextTrackCues };
61
+ //# sourceMappingURL=load-text-track-cues.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load-text-track-cues.d.ts","names":[],"sources":["../../../../src/dom/features/load-text-track-cues.ts"],"mappings":";;;;;;AA8GA;UAAiB,qBAAA;EACf,QAAA,EAAU,KAAA;IAAQ,EAAA;EAAA;AAAA;;;;AASpB;;;KAAY,oBAAA,GAAuB,MAAA,SAAe,qBAAA;;AAKlD;;UAAiB,wBAAA;EACf,mBAAA;EACA,YAAA,GAAe,YAAA;EAAf;EAEA,WAAA;EAAA;EAEA,eAAA,GAAkB,oBAAA;AAAA;;;AAMpB;UAAiB,yBAAA;EACf,UAAA,GAAa,GAAA,SAAY,gBAAA;AAAA;;;;;;;;;;;;;;;;iBAoGX,iBAAA,CAAA;EACd,KAAA;EACA;AAAA;EAEA,KAAA,EAAO,aAAA,CAAc,wBAAA;EACrB,MAAA,EAAQ,aAAA,CAAc,yBAAA;AAAA"}
@@ -0,0 +1,151 @@
1
+ import { getSegmentsToLoad } from "../../core/buffer/forward-buffer.js";
2
+ import { isResolvedTrack } from "../../core/types/index.js";
3
+ import { combineLatest } from "../../core/reactive/combine-latest.js";
4
+ import { parseVttSegment } from "../text/parse-vtt-segment.js";
5
+
6
+ //#region src/dom/features/load-text-track-cues.ts
7
+ function isDuplicateCue(cue, textTrack) {
8
+ const { cues } = textTrack;
9
+ if (!cues) return false;
10
+ for (let i = 0; i < cues.length; i++) {
11
+ const existing = cues[i];
12
+ if (existing.startTime === cue.startTime && existing.endTime === cue.endTime && existing.text === cue.text) return true;
13
+ }
14
+ return false;
15
+ }
16
+ const loadVttSegmentTask = async ({ segment }, context) => {
17
+ const cues = await parseVttSegment(segment.url);
18
+ for (const cue of cues) if (!isDuplicateCue(cue, context.textTrack)) context.textTrack.addCue(cue);
19
+ };
20
+ /**
21
+ * Load text track cues task (composite - orchestrates VTT segment subtasks).
22
+ */
23
+ const loadTextTrackCuesTask = async ({ currentState }, context) => {
24
+ const track = findSelectedTextTrack(currentState);
25
+ if (!track || !isResolvedTrack(track)) return;
26
+ const { segments } = track;
27
+ if (segments.length === 0) return;
28
+ const trackId = track.id;
29
+ const loadedIds = new Set((currentState.textBufferState?.[trackId]?.segments ?? []).map((s) => s.id));
30
+ const segmentsToLoad = getSegmentsToLoad(segments, segments.filter((s) => loadedIds.has(s.id)), currentState.currentTime ?? 0).filter((s) => !loadedIds.has(s.id));
31
+ if (segmentsToLoad.length === 0) return;
32
+ for (const segment of segmentsToLoad) {
33
+ if (context.signal.aborted) break;
34
+ try {
35
+ await loadVttSegmentTask({ segment }, { textTrack: context.textTrack });
36
+ const latest = context.state.current.textBufferState ?? {};
37
+ const trackState = latest[trackId] ?? { segments: [] };
38
+ context.state.patch({ textBufferState: {
39
+ ...latest,
40
+ [trackId]: { segments: [...trackState.segments, { id: segment.id }] }
41
+ } });
42
+ } catch (error) {
43
+ if (error instanceof Error && error.name === "AbortError") break;
44
+ console.error("Failed to load VTT segment:", error);
45
+ }
46
+ }
47
+ if (context.textTrack.mode === "showing" && context.textTrack.cues) Array.from(context.textTrack.cues).forEach((cue) => {
48
+ context.textTrack.addCue(cue);
49
+ });
50
+ await new Promise((resolve) => requestAnimationFrame(resolve));
51
+ };
52
+ /**
53
+ * Find the selected text track in the presentation.
54
+ */
55
+ function findSelectedTextTrack(state) {
56
+ if (!state.presentation || !state.selectedTextTrackId) return;
57
+ const textSet = state.presentation.selectionSets.find((set) => set.type === "text");
58
+ if (!textSet?.switchingSets?.[0]?.tracks) return;
59
+ return textSet.switchingSets[0].tracks.find((t) => t.id === state.selectedTextTrackId);
60
+ }
61
+ /**
62
+ * Get the browser's TextTrack object for the selected text track.
63
+ *
64
+ * Retrieves the live TextTrack interface from the track element in owners,
65
+ * which is used for adding cues, checking mode, and managing track state.
66
+ *
67
+ * Note: Returns the DOM TextTrack interface (HTMLTrackElement.track),
68
+ * not the presentation Track metadata type.
69
+ *
70
+ * @param state - Current playback state (track selection)
71
+ * @param owners - DOM owners containing track elements map
72
+ * @returns DOM TextTrack interface or undefined if not found
73
+ */
74
+ function getSelectedTextTrackFromOwners(state, owners) {
75
+ const trackId = state.selectedTextTrackId;
76
+ if (!trackId || !owners.textTracks) return;
77
+ return owners.textTracks.get(trackId)?.track;
78
+ }
79
+ /**
80
+ * Check if we can load text track cues.
81
+ *
82
+ * Requires:
83
+ * - Selected text track ID exists
84
+ * - Track elements map exists
85
+ * - Track element exists for selected track
86
+ */
87
+ function canLoadTextTrackCues(state, owners) {
88
+ return !!state.selectedTextTrackId && !!owners.textTracks && owners.textTracks.has(state.selectedTextTrackId);
89
+ }
90
+ /**
91
+ * Check if we should load text track cues.
92
+ *
93
+ * Only load if:
94
+ * - Track is resolved (has segments)
95
+ * - Track has at least one segment
96
+ * - Track element exists
97
+ */
98
+ function shouldLoadTextTrackCues(state, owners) {
99
+ if (!canLoadTextTrackCues(state, owners)) return false;
100
+ const track = findSelectedTextTrack(state);
101
+ if (!track || !isResolvedTrack(track) || track.segments.length === 0) return false;
102
+ if (!getSelectedTextTrackFromOwners(state, owners)) return false;
103
+ return true;
104
+ }
105
+ /**
106
+ * Load text track cues orchestration.
107
+ *
108
+ * Triggers when:
109
+ * - Text track is selected
110
+ * - Track is resolved (has segments)
111
+ * - Track element exists
112
+ *
113
+ * Fetches and parses VTT segments within the forward buffer window, then adds
114
+ * cues to the track incrementally. Continues on segment errors to provide
115
+ * partial subtitles.
116
+ *
117
+ * @example
118
+ * const cleanup = loadTextTrackCues({ state, owners });
119
+ */
120
+ function loadTextTrackCues({ state, owners }) {
121
+ let currentTask = null;
122
+ let abortController = null;
123
+ let lastTrackId;
124
+ const cleanup = combineLatest([state, owners]).subscribe(async ([currentState, currentOwners]) => {
125
+ if (currentState.selectedTextTrackId !== lastTrackId) {
126
+ lastTrackId = currentState.selectedTextTrackId;
127
+ abortController?.abort();
128
+ currentTask = null;
129
+ }
130
+ if (currentTask) return;
131
+ if (!shouldLoadTextTrackCues(currentState, currentOwners)) return;
132
+ const textTrack = getSelectedTextTrackFromOwners(currentState, currentOwners);
133
+ if (!textTrack) return;
134
+ abortController = new AbortController();
135
+ currentTask = loadTextTrackCuesTask({ currentState }, {
136
+ signal: abortController.signal,
137
+ textTrack,
138
+ state
139
+ }).finally(() => {
140
+ currentTask = null;
141
+ });
142
+ });
143
+ return () => {
144
+ abortController?.abort();
145
+ cleanup();
146
+ };
147
+ }
148
+
149
+ //#endregion
150
+ export { loadTextTrackCues };
151
+ //# sourceMappingURL=load-text-track-cues.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load-text-track-cues.js","names":[],"sources":["../../../../src/dom/features/load-text-track-cues.ts"],"sourcesContent":["import { getSegmentsToLoad } from '../../core/buffer/forward-buffer';\nimport { combineLatest } from '../../core/reactive/combine-latest';\nimport type { WritableState } from '../../core/state/create-state';\nimport type { Presentation, Segment, TextTrack } from '../../core/types';\nimport { isResolvedTrack } from '../../core/types';\nimport { parseVttSegment } from '../text/parse-vtt-segment';\n\nfunction isDuplicateCue(cue: VTTCue, textTrack: globalThis.TextTrack): boolean {\n const { cues } = textTrack;\n if (!cues) return false;\n for (let i = 0; i < cues.length; i++) {\n const existing = cues[i] as VTTCue;\n if (existing.startTime === cue.startTime && existing.endTime === cue.endTime && existing.text === cue.text) {\n return true;\n }\n }\n return false;\n}\n\nconst loadVttSegmentTask = async (\n { segment }: { segment: Segment },\n context: { textTrack: globalThis.TextTrack }\n): Promise<void> => {\n const cues = await parseVttSegment(segment.url);\n for (const cue of cues) {\n if (!isDuplicateCue(cue, context.textTrack)) {\n context.textTrack.addCue(cue);\n }\n }\n};\n\n// ============================================================================\n// MAIN TASK (composite - orchestrates subtasks)\n// ============================================================================\n\n/**\n * Load text track cues task (composite - orchestrates VTT segment subtasks).\n */\nconst loadTextTrackCuesTask = async (\n { currentState }: { currentState: TextTrackCueLoadingState },\n context: {\n signal: AbortSignal;\n textTrack: globalThis.TextTrack;\n state: WritableState<TextTrackCueLoadingState>;\n }\n): Promise<void> => {\n const track = findSelectedTextTrack(currentState);\n if (!track || !isResolvedTrack(track)) return;\n\n const { segments } = track;\n if (segments.length === 0) return;\n\n const trackId = track.id;\n\n // Resolve segments already recorded in the state model for this track.\n // Keyed by track ID so multiple text tracks don't interfere with each other.\n const loadedIds = new Set((currentState.textBufferState?.[trackId]?.segments ?? []).map((s) => s.id));\n const alreadyLoaded = segments.filter((s) => loadedIds.has(s.id));\n\n // Apply the same forward buffer window as audio/video segment loading.\n const currentTime = currentState.currentTime ?? 0;\n const segmentsToLoad = getSegmentsToLoad(segments, alreadyLoaded, currentTime).filter((s) => !loadedIds.has(s.id));\n\n if (segmentsToLoad.length === 0) return;\n\n // Execute subtasks sequentially, recording each loaded segment in state.\n for (const segment of segmentsToLoad) {\n if (context.signal.aborted) break;\n\n try {\n await loadVttSegmentTask({ segment }, { textTrack: context.textTrack });\n\n // Record the loaded segment in shared state — mirrors bufferState for\n // audio/video and supports N text tracks keyed by track ID.\n const latest = context.state.current.textBufferState ?? {};\n const trackState = latest[trackId] ?? { segments: [] };\n context.state.patch({\n textBufferState: {\n ...latest,\n [trackId]: { segments: [...trackState.segments, { id: segment.id }] },\n },\n });\n } catch (error) {\n if (error instanceof Error && error.name === 'AbortError') break;\n console.error('Failed to load VTT segment:', error);\n // Continue to next segment (graceful degradation)\n }\n }\n\n // Chrome bug: after a track goes through mode='disabled' (which clears cues) and back\n // to 'showing', cues added to the track aren't activated. Re-adding all cues forces\n // Chrome to re-process them. Safe no-op in other browsers.\n // Mirrors the workaround in HlsMediaTextTracksMixin (packages/core/src/dom/media/hls/text-tracks.ts).\n if (context.textTrack.mode === 'showing' && context.textTrack.cues) {\n Array.from(context.textTrack.cues).forEach((cue) => {\n context.textTrack.addCue(cue);\n });\n }\n\n // Wait a frame before completing to allow state updates to flush\n await new Promise((resolve) => requestAnimationFrame(resolve));\n};\n\n// ============================================================================\n// STATE & OWNERS\n// ============================================================================\n\n/**\n * Loaded-segment record for a single text track.\n */\nexport interface TextTrackSegmentState {\n segments: Array<{ id: string }>;\n}\n\n/**\n * Buffer model for text track cues — keyed by track ID.\n *\n * Using a per-track-ID map (rather than fixed 'video'/'audio' keys) because\n * there can be N text tracks — one per language/subtitle variant.\n */\nexport type TextTrackBufferState = Record<string, TextTrackSegmentState>;\n\n/**\n * State shape for text track cue loading.\n */\nexport interface TextTrackCueLoadingState {\n selectedTextTrackId?: string;\n presentation?: Presentation;\n /** Current playback position — used to gate VTT segment fetching to the forward buffer window. */\n currentTime?: number;\n /** Loaded-segment model for text tracks, keyed by track ID. */\n textBufferState?: TextTrackBufferState;\n}\n\n/**\n * Owners shape for text track cue loading.\n */\nexport interface TextTrackCueLoadingOwners {\n textTracks?: Map<string, HTMLTrackElement>;\n}\n\n/**\n * Find the selected text track in the presentation.\n */\nfunction findSelectedTextTrack(state: TextTrackCueLoadingState): TextTrack | undefined {\n if (!state.presentation || !state.selectedTextTrackId) {\n return undefined;\n }\n\n const textSet = state.presentation.selectionSets.find((set) => set.type === 'text');\n if (!textSet?.switchingSets?.[0]?.tracks) {\n return undefined;\n }\n\n const track = textSet.switchingSets[0].tracks.find((t) => t.id === state.selectedTextTrackId);\n\n return track as TextTrack | undefined;\n}\n\n/**\n * Get the browser's TextTrack object for the selected text track.\n *\n * Retrieves the live TextTrack interface from the track element in owners,\n * which is used for adding cues, checking mode, and managing track state.\n *\n * Note: Returns the DOM TextTrack interface (HTMLTrackElement.track),\n * not the presentation Track metadata type.\n *\n * @param state - Current playback state (track selection)\n * @param owners - DOM owners containing track elements map\n * @returns DOM TextTrack interface or undefined if not found\n */\nfunction getSelectedTextTrackFromOwners(\n state: TextTrackCueLoadingState,\n owners: TextTrackCueLoadingOwners\n): globalThis.TextTrack | undefined {\n const trackId = state.selectedTextTrackId;\n if (!trackId || !owners.textTracks) {\n return undefined;\n }\n\n const trackElement = owners.textTracks.get(trackId);\n return trackElement?.track;\n}\n\n/**\n * Check if we can load text track cues.\n *\n * Requires:\n * - Selected text track ID exists\n * - Track elements map exists\n * - Track element exists for selected track\n */\nexport function canLoadTextTrackCues(state: TextTrackCueLoadingState, owners: TextTrackCueLoadingOwners): boolean {\n return !!state.selectedTextTrackId && !!owners.textTracks && owners.textTracks.has(state.selectedTextTrackId);\n}\n\n/**\n * Check if we should load text track cues.\n *\n * Only load if:\n * - Track is resolved (has segments)\n * - Track has at least one segment\n * - Track element exists\n */\nexport function shouldLoadTextTrackCues(state: TextTrackCueLoadingState, owners: TextTrackCueLoadingOwners): boolean {\n if (!canLoadTextTrackCues(state, owners)) {\n return false;\n }\n\n const track = findSelectedTextTrack(state);\n if (!track || !isResolvedTrack(track) || track.segments.length === 0) {\n return false;\n }\n\n const textTrack = getSelectedTextTrackFromOwners(state, owners);\n if (!textTrack) {\n return false;\n }\n\n return true;\n}\n\n/**\n * Load text track cues orchestration.\n *\n * Triggers when:\n * - Text track is selected\n * - Track is resolved (has segments)\n * - Track element exists\n *\n * Fetches and parses VTT segments within the forward buffer window, then adds\n * cues to the track incrementally. Continues on segment errors to provide\n * partial subtitles.\n *\n * @example\n * const cleanup = loadTextTrackCues({ state, owners });\n */\nexport function loadTextTrackCues({\n state,\n owners,\n}: {\n state: WritableState<TextTrackCueLoadingState>;\n owners: WritableState<TextTrackCueLoadingOwners>;\n}): () => void {\n let currentTask: Promise<void> | null = null;\n let abortController: AbortController | null = null;\n let lastTrackId: string | undefined;\n\n const cleanup = combineLatest([state, owners]).subscribe(\n async ([currentState, currentOwners]: [TextTrackCueLoadingState, TextTrackCueLoadingOwners]) => {\n // Abort any in-progress task when the selected track changes.\n // The new track's textBufferState entry will be empty, so the task\n // naturally starts fresh without needing any explicit reset.\n if (currentState.selectedTextTrackId !== lastTrackId) {\n lastTrackId = currentState.selectedTextTrackId;\n abortController?.abort();\n currentTask = null;\n }\n\n if (currentTask) return; // Task already in progress\n if (!shouldLoadTextTrackCues(currentState, currentOwners)) return;\n\n const textTrack = getSelectedTextTrackFromOwners(currentState, currentOwners);\n if (!textTrack) return;\n\n // Create abort controller and invoke task\n abortController = new AbortController();\n currentTask = loadTextTrackCuesTask(\n { currentState },\n { signal: abortController.signal, textTrack, state }\n ).finally(() => {\n currentTask = null;\n });\n }\n );\n\n return () => {\n abortController?.abort();\n cleanup();\n };\n}\n"],"mappings":"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"}