@videojs/spf 10.0.0-beta.23 → 10.0.0-beta.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/default/background-video.js +3 -0
- package/dist/{dev/core → default/core/actors}/create-machine-actor.js +3 -3
- package/dist/default/core/actors/create-machine-actor.js.map +1 -0
- package/dist/default/core/{create-transition-actor.js → actors/create-transition-actor.js} +3 -3
- package/dist/default/core/actors/create-transition-actor.js.map +1 -0
- package/dist/default/core/composition/create-composition.js +85 -0
- package/dist/default/core/composition/create-composition.js.map +1 -0
- package/dist/default/core/composition/share-signals.js +39 -0
- package/dist/default/core/composition/share-signals.js.map +1 -0
- package/dist/{dev/core → default/core/reactors}/create-machine-reactor.js +4 -4
- package/dist/default/core/reactors/create-machine-reactor.js.map +1 -0
- package/dist/default/core/signals/primitives.js +33 -11
- package/dist/default/core/signals/primitives.js.map +1 -1
- package/dist/default/core/{task.js → tasks/task.js} +2 -2
- package/dist/default/core/tasks/task.js.map +1 -0
- package/dist/default/dom.js +9 -10
- package/dist/default/hls.js +5 -0
- package/dist/default/index.js +12 -3
- package/dist/default/index.js.map +1 -1
- package/dist/default/media/abr/quality-selection.js +21 -0
- package/dist/default/media/abr/quality-selection.js.map +1 -0
- package/dist/default/{core → media}/buffer/back-buffer.js +2 -2
- package/dist/default/media/buffer/back-buffer.js.map +1 -0
- package/dist/{dev/core → default/media}/buffer/forward-buffer.js +14 -2
- package/dist/default/media/buffer/forward-buffer.js.map +1 -0
- package/dist/default/media/dom/capabilities.js +70 -0
- package/dist/default/media/dom/capabilities.js.map +1 -0
- package/dist/{dev/dom/media → default/media/dom/mse}/append-segment.js +1 -1
- package/dist/default/media/dom/mse/append-segment.js.map +1 -0
- package/dist/default/{dom/media → media/dom/mse}/buffer-flusher.js +1 -1
- package/dist/default/media/dom/mse/buffer-flusher.js.map +1 -0
- package/dist/default/media/dom/mse/duration.js +78 -0
- package/dist/default/media/dom/mse/duration.js.map +1 -0
- package/dist/default/media/dom/mse/end-of-stream.js +20 -0
- package/dist/default/media/dom/mse/end-of-stream.js.map +1 -0
- package/dist/default/{dom/media → media/dom/mse}/mediasource-setup.js +63 -18
- package/dist/default/media/dom/mse/mediasource-setup.js.map +1 -0
- package/dist/default/{dom/text/parse-vtt-segment.js → media/dom/text/resolve-vtt-segment.js} +5 -5
- package/dist/default/media/dom/text/resolve-vtt-segment.js.map +1 -0
- package/dist/default/media/dom/text/text-track-slots.js +69 -0
- package/dist/default/media/dom/text/text-track-slots.js.map +1 -0
- package/dist/{dev/core → default/media}/hls/parse-attributes.js +17 -3
- package/dist/default/media/hls/parse-attributes.js.map +1 -0
- package/dist/{dev/core → default/media}/hls/parse-media-playlist.js +25 -2
- package/dist/default/media/hls/parse-media-playlist.js.map +1 -0
- package/dist/{dev/core → default/media}/hls/parse-multivariant.js +17 -8
- package/dist/default/media/hls/parse-multivariant.js.map +1 -0
- package/dist/default/{core → media}/hls/resolve-url.js +1 -1
- package/dist/default/media/hls/resolve-url.js.map +1 -0
- package/dist/default/media/primitives/select-tracks.js +130 -0
- package/dist/default/media/primitives/select-tracks.js.map +1 -0
- package/dist/default/media/types/index.js +36 -0
- package/dist/default/media/types/index.js.map +1 -0
- package/dist/default/media/utils/cdn.js +56 -0
- package/dist/default/media/utils/cdn.js.map +1 -0
- package/dist/default/media/utils/preload.js +22 -0
- package/dist/default/media/utils/preload.js.map +1 -0
- package/dist/default/media/utils/track-selection.js +47 -0
- package/dist/default/media/utils/track-selection.js.map +1 -0
- package/dist/default/media/utils/tracks.js +130 -0
- package/dist/default/media/utils/tracks.js.map +1 -0
- package/dist/default/{core/abr → network}/bandwidth-estimator.js +6 -5
- package/dist/default/network/bandwidth-estimator.js.map +1 -0
- package/dist/{dev/dom → default}/network/chunked-stream-iterable.js +1 -1
- package/dist/default/network/chunked-stream-iterable.js.map +1 -0
- package/dist/{dev/core/abr → default/network}/ewma.js +1 -1
- package/dist/default/network/ewma.js.map +1 -0
- package/dist/default/network/fetch.js +115 -0
- package/dist/default/network/fetch.js.map +1 -0
- package/dist/default/{dom/features/segment-loader-actor.js → playback/actors/dom/segment-loader.js} +63 -23
- package/dist/default/playback/actors/dom/segment-loader.js.map +1 -0
- package/dist/default/{dom/media/source-buffer-actor.js → playback/actors/dom/source-buffer.js} +10 -9
- package/dist/default/playback/actors/dom/source-buffer.js.map +1 -0
- package/dist/{dev/dom/features/text-tracks-actor.js → default/playback/actors/dom/text-tracks.js} +7 -3
- package/dist/default/playback/actors/dom/text-tracks.js.map +1 -0
- package/dist/default/playback/actors/text-track-segment-loader.js +135 -0
- package/dist/default/playback/actors/text-track-segment-loader.js.map +1 -0
- package/dist/default/playback/behaviors/calculate-presentation-duration.js +36 -0
- package/dist/default/playback/behaviors/calculate-presentation-duration.js.map +1 -0
- package/dist/default/playback/behaviors/derive-cdn-priority.js +68 -0
- package/dist/default/playback/behaviors/derive-cdn-priority.js.map +1 -0
- package/dist/default/playback/behaviors/dom/end-of-stream.js +90 -0
- package/dist/default/playback/behaviors/dom/end-of-stream.js.map +1 -0
- package/dist/default/playback/behaviors/dom/load-segments.js +177 -0
- package/dist/default/playback/behaviors/dom/load-segments.js.map +1 -0
- package/dist/default/playback/behaviors/dom/setup-buffer-actors.js +192 -0
- package/dist/default/playback/behaviors/dom/setup-buffer-actors.js.map +1 -0
- package/dist/default/playback/behaviors/dom/setup-mediasource.js +89 -0
- package/dist/default/playback/behaviors/dom/setup-mediasource.js.map +1 -0
- package/dist/default/playback/behaviors/dom/setup-text-track-actors.js +54 -0
- package/dist/default/playback/behaviors/dom/setup-text-track-actors.js.map +1 -0
- package/dist/default/playback/behaviors/dom/sync-text-tracks.js +124 -0
- package/dist/default/playback/behaviors/dom/sync-text-tracks.js.map +1 -0
- package/dist/default/playback/behaviors/dom/track-current-time.js +52 -0
- package/dist/default/playback/behaviors/dom/track-current-time.js.map +1 -0
- package/dist/default/playback/behaviors/dom/track-load-triggers.js +94 -0
- package/dist/default/playback/behaviors/dom/track-load-triggers.js.map +1 -0
- package/dist/default/playback/behaviors/dom/track-playback-rate.js +37 -0
- package/dist/default/playback/behaviors/dom/track-playback-rate.js.map +1 -0
- package/dist/default/playback/behaviors/dom/update-mediasource-duration.js +90 -0
- package/dist/default/playback/behaviors/dom/update-mediasource-duration.js.map +1 -0
- package/dist/default/playback/behaviors/resolve-presentation.js +85 -0
- package/dist/default/playback/behaviors/resolve-presentation.js.map +1 -0
- package/dist/default/playback/behaviors/resolve-track.js +120 -0
- package/dist/default/playback/behaviors/resolve-track.js.map +1 -0
- package/dist/default/playback/behaviors/select-tracks.js +101 -0
- package/dist/default/playback/behaviors/select-tracks.js.map +1 -0
- package/dist/default/playback/behaviors/setup-failover-monitor.js +66 -0
- package/dist/default/playback/behaviors/setup-failover-monitor.js.map +1 -0
- package/dist/default/playback/behaviors/sync-preload.js +59 -0
- package/dist/default/playback/behaviors/sync-preload.js.map +1 -0
- package/dist/default/playback/behaviors/track-switching.js +341 -0
- package/dist/default/playback/behaviors/track-switching.js.map +1 -0
- package/dist/default/playback/engines/background-video/adapter.js +158 -0
- package/dist/default/playback/engines/background-video/adapter.js.map +1 -0
- package/dist/default/playback/engines/background-video/engine.js +75 -0
- package/dist/default/playback/engines/background-video/engine.js.map +1 -0
- package/dist/default/playback/engines/hls/adapter-audio-only.js +119 -0
- package/dist/default/playback/engines/hls/adapter-audio-only.js.map +1 -0
- package/dist/default/{dom/playback-engine → playback/engines/hls}/adapter.js +40 -26
- package/dist/default/playback/engines/hls/adapter.js.map +1 -0
- package/dist/default/playback/engines/hls/engine-audio-only.js +80 -0
- package/dist/default/playback/engines/hls/engine-audio-only.js.map +1 -0
- package/dist/default/playback/engines/hls/engine.js +115 -0
- package/dist/default/playback/engines/hls/engine.js.map +1 -0
- package/dist/default/playback/primitives/failover-fetch.js +42 -0
- package/dist/default/playback/primitives/failover-fetch.js.map +1 -0
- package/dist/default/playback/primitives/track-types.js +61 -0
- package/dist/default/playback/primitives/track-types.js.map +1 -0
- package/dist/dev/background-video.d.ts +3 -0
- package/dist/dev/background-video.js +3 -0
- package/dist/dev/core/{actor.d.ts → actors/actor.d.ts} +2 -2
- package/dist/dev/core/actors/actor.d.ts.map +1 -0
- package/dist/dev/core/actors/create-machine-actor.d.ts +125 -0
- package/dist/dev/core/actors/create-machine-actor.d.ts.map +1 -0
- package/dist/{default/core → dev/core/actors}/create-machine-actor.js +3 -3
- package/dist/dev/core/actors/create-machine-actor.js.map +1 -0
- package/dist/dev/core/actors/create-transition-actor.d.ts +42 -0
- package/dist/dev/core/actors/create-transition-actor.d.ts.map +1 -0
- package/dist/dev/core/{create-transition-actor.js → actors/create-transition-actor.js} +3 -3
- package/dist/dev/core/actors/create-transition-actor.js.map +1 -0
- package/dist/dev/core/composition/create-composition.d.ts +274 -0
- package/dist/dev/core/composition/create-composition.d.ts.map +1 -0
- package/dist/dev/core/composition/create-composition.js +85 -0
- package/dist/dev/core/composition/create-composition.js.map +1 -0
- package/dist/dev/core/composition/share-signals.d.ts +47 -0
- package/dist/dev/core/composition/share-signals.d.ts.map +1 -0
- package/dist/dev/core/composition/share-signals.js +39 -0
- package/dist/dev/core/composition/share-signals.js.map +1 -0
- package/dist/dev/core/machine.d.ts +0 -1
- package/dist/dev/core/machine.d.ts.map +1 -1
- package/dist/dev/core/reactors/create-machine-reactor.d.ts +93 -0
- package/dist/dev/core/reactors/create-machine-reactor.d.ts.map +1 -0
- package/dist/{default/core → dev/core/reactors}/create-machine-reactor.js +4 -4
- package/dist/dev/core/reactors/create-machine-reactor.js.map +1 -0
- package/dist/dev/core/signals/primitives.d.ts +38 -1
- package/dist/dev/core/signals/primitives.d.ts.map +1 -1
- package/dist/dev/core/signals/primitives.js +33 -11
- package/dist/dev/core/signals/primitives.js.map +1 -1
- package/dist/dev/core/tasks/task.d.ts +115 -0
- package/dist/dev/core/tasks/task.d.ts.map +1 -0
- package/dist/dev/core/{task.js → tasks/task.js} +2 -2
- package/dist/dev/core/tasks/task.js.map +1 -0
- package/dist/dev/dom.d.ts +9 -10
- package/dist/dev/dom.js +9 -10
- package/dist/dev/hls.d.ts +5 -0
- package/dist/dev/hls.js +5 -0
- package/dist/dev/index.d.ts +15 -3
- package/dist/dev/index.d.ts.map +1 -1
- package/dist/dev/index.js +12 -3
- package/dist/dev/index.js.map +1 -1
- package/dist/dev/media/abr/quality-selection.d.ts +22 -0
- package/dist/dev/media/abr/quality-selection.d.ts.map +1 -0
- package/dist/dev/media/abr/quality-selection.js +21 -0
- package/dist/dev/media/abr/quality-selection.js.map +1 -0
- package/dist/dev/media/buffer/back-buffer.d.ts +14 -0
- package/dist/dev/media/buffer/back-buffer.d.ts.map +1 -0
- package/dist/dev/{core → media}/buffer/back-buffer.js +2 -2
- package/dist/dev/media/buffer/back-buffer.js.map +1 -0
- package/dist/dev/media/buffer/forward-buffer.d.ts +14 -0
- package/dist/dev/media/buffer/forward-buffer.d.ts.map +1 -0
- package/dist/{default/core → dev/media}/buffer/forward-buffer.js +14 -2
- package/dist/dev/media/buffer/forward-buffer.js.map +1 -0
- package/dist/dev/media/dom/capabilities.js +70 -0
- package/dist/dev/media/dom/capabilities.js.map +1 -0
- package/dist/dev/{dom/media → media/dom/mse}/append-segment.d.ts +1 -1
- package/dist/dev/media/dom/mse/append-segment.d.ts.map +1 -0
- package/dist/{default/dom/media → dev/media/dom/mse}/append-segment.js +1 -1
- package/dist/dev/media/dom/mse/append-segment.js.map +1 -0
- package/dist/dev/{dom/media → media/dom/mse}/buffer-flusher.d.ts +1 -1
- package/dist/dev/media/dom/mse/buffer-flusher.d.ts.map +1 -0
- package/dist/dev/{dom/media → media/dom/mse}/buffer-flusher.js +1 -1
- package/dist/dev/media/dom/mse/buffer-flusher.js.map +1 -0
- package/dist/dev/media/dom/mse/duration.js +78 -0
- package/dist/dev/media/dom/mse/duration.js.map +1 -0
- package/dist/dev/media/dom/mse/end-of-stream.js +20 -0
- package/dist/dev/media/dom/mse/end-of-stream.js.map +1 -0
- package/dist/dev/{dom/media → media/dom/mse}/mediasource-setup.js +63 -18
- package/dist/dev/media/dom/mse/mediasource-setup.js.map +1 -0
- package/dist/dev/media/dom/text/resolve-vtt-segment.d.ts +12 -0
- package/dist/dev/media/dom/text/resolve-vtt-segment.d.ts.map +1 -0
- package/dist/dev/{dom/text/parse-vtt-segment.js → media/dom/text/resolve-vtt-segment.js} +5 -5
- package/dist/dev/media/dom/text/resolve-vtt-segment.js.map +1 -0
- package/dist/dev/media/dom/text/text-track-slots.d.ts +31 -0
- package/dist/dev/media/dom/text/text-track-slots.d.ts.map +1 -0
- package/dist/dev/media/dom/text/text-track-slots.js +69 -0
- package/dist/dev/media/dom/text/text-track-slots.js.map +1 -0
- package/dist/{default/core → dev/media}/hls/parse-attributes.js +17 -3
- package/dist/dev/media/hls/parse-attributes.js.map +1 -0
- package/dist/{default/core → dev/media}/hls/parse-media-playlist.js +25 -2
- package/dist/dev/media/hls/parse-media-playlist.js.map +1 -0
- package/dist/{default/core → dev/media}/hls/parse-multivariant.js +17 -8
- package/dist/dev/media/hls/parse-multivariant.js.map +1 -0
- package/dist/dev/{core → media}/hls/resolve-url.js +1 -1
- package/dist/dev/media/hls/resolve-url.js.map +1 -0
- package/dist/dev/media/primitives/select-tracks.d.ts +36 -0
- package/dist/dev/media/primitives/select-tracks.d.ts.map +1 -0
- package/dist/dev/media/primitives/select-tracks.js +130 -0
- package/dist/dev/media/primitives/select-tracks.js.map +1 -0
- package/dist/dev/{core → media}/types/index.d.ts +55 -3
- package/dist/dev/media/types/index.d.ts.map +1 -0
- package/dist/dev/media/types/index.js +36 -0
- package/dist/dev/media/types/index.js.map +1 -0
- package/dist/dev/media/utils/cdn.d.ts +13 -0
- package/dist/dev/media/utils/cdn.d.ts.map +1 -0
- package/dist/dev/media/utils/cdn.js +56 -0
- package/dist/dev/media/utils/cdn.js.map +1 -0
- package/dist/dev/media/utils/preload.js +22 -0
- package/dist/dev/media/utils/preload.js.map +1 -0
- package/dist/dev/media/utils/track-selection.js +47 -0
- package/dist/dev/media/utils/track-selection.js.map +1 -0
- package/dist/dev/media/utils/tracks.js +130 -0
- package/dist/dev/media/utils/tracks.js.map +1 -0
- package/dist/dev/{core/abr → network}/bandwidth-estimator.d.ts +17 -2
- package/dist/dev/network/bandwidth-estimator.d.ts.map +1 -0
- package/dist/dev/{core/abr → network}/bandwidth-estimator.js +6 -5
- package/dist/dev/network/bandwidth-estimator.js.map +1 -0
- package/dist/{default/dom → dev}/network/chunked-stream-iterable.js +1 -1
- package/dist/dev/network/chunked-stream-iterable.js.map +1 -0
- package/dist/{default/core/abr → dev/network}/ewma.js +1 -1
- package/dist/dev/network/ewma.js.map +1 -0
- package/dist/dev/network/fetch.js +115 -0
- package/dist/dev/network/fetch.js.map +1 -0
- package/dist/dev/playback/actors/dom/segment-loader.d.ts +36 -0
- package/dist/dev/playback/actors/dom/segment-loader.d.ts.map +1 -0
- package/dist/dev/{dom/features/segment-loader-actor.js → playback/actors/dom/segment-loader.js} +63 -23
- package/dist/dev/playback/actors/dom/segment-loader.js.map +1 -0
- package/dist/dev/{dom/media/source-buffer-actor.d.ts → playback/actors/dom/source-buffer.d.ts} +20 -5
- package/dist/dev/playback/actors/dom/source-buffer.d.ts.map +1 -0
- package/dist/dev/{dom/media/source-buffer-actor.js → playback/actors/dom/source-buffer.js} +10 -9
- package/dist/dev/playback/actors/dom/source-buffer.js.map +1 -0
- package/dist/dev/playback/actors/dom/text-tracks.d.ts +1 -0
- package/dist/{default/dom/features/text-tracks-actor.js → dev/playback/actors/dom/text-tracks.js} +7 -3
- package/dist/dev/playback/actors/dom/text-tracks.js.map +1 -0
- package/dist/dev/playback/actors/text-track-segment-loader.d.ts +59 -0
- package/dist/dev/playback/actors/text-track-segment-loader.d.ts.map +1 -0
- package/dist/dev/playback/actors/text-track-segment-loader.js +135 -0
- package/dist/dev/playback/actors/text-track-segment-loader.js.map +1 -0
- package/dist/dev/playback/actors/text-tracks.d.ts +42 -0
- package/dist/dev/playback/actors/text-tracks.d.ts.map +1 -0
- package/dist/dev/playback/behaviors/calculate-presentation-duration.d.ts +25 -0
- package/dist/dev/playback/behaviors/calculate-presentation-duration.d.ts.map +1 -0
- package/dist/dev/playback/behaviors/calculate-presentation-duration.js +36 -0
- package/dist/dev/playback/behaviors/calculate-presentation-duration.js.map +1 -0
- package/dist/dev/playback/behaviors/derive-cdn-priority.js +68 -0
- package/dist/dev/playback/behaviors/derive-cdn-priority.js.map +1 -0
- package/dist/dev/playback/behaviors/dom/end-of-stream.js +90 -0
- package/dist/dev/playback/behaviors/dom/end-of-stream.js.map +1 -0
- package/dist/dev/playback/behaviors/dom/load-segments.d.ts +62 -0
- package/dist/dev/playback/behaviors/dom/load-segments.d.ts.map +1 -0
- package/dist/dev/playback/behaviors/dom/load-segments.js +177 -0
- package/dist/dev/playback/behaviors/dom/load-segments.js.map +1 -0
- package/dist/dev/playback/behaviors/dom/setup-buffer-actors.js +192 -0
- package/dist/dev/playback/behaviors/dom/setup-buffer-actors.js.map +1 -0
- package/dist/dev/playback/behaviors/dom/setup-mediasource.js +89 -0
- package/dist/dev/playback/behaviors/dom/setup-mediasource.js.map +1 -0
- package/dist/dev/playback/behaviors/dom/setup-text-track-actors.d.ts +31 -0
- package/dist/dev/playback/behaviors/dom/setup-text-track-actors.d.ts.map +1 -0
- package/dist/dev/playback/behaviors/dom/setup-text-track-actors.js +54 -0
- package/dist/dev/playback/behaviors/dom/setup-text-track-actors.js.map +1 -0
- package/dist/dev/playback/behaviors/dom/sync-text-tracks.js +124 -0
- package/dist/dev/playback/behaviors/dom/sync-text-tracks.js.map +1 -0
- package/dist/dev/playback/behaviors/dom/track-current-time.d.ts +34 -0
- package/dist/dev/playback/behaviors/dom/track-current-time.d.ts.map +1 -0
- package/dist/dev/playback/behaviors/dom/track-current-time.js +52 -0
- package/dist/dev/playback/behaviors/dom/track-current-time.js.map +1 -0
- package/dist/dev/playback/behaviors/dom/track-load-triggers.d.ts +48 -0
- package/dist/dev/playback/behaviors/dom/track-load-triggers.d.ts.map +1 -0
- package/dist/dev/playback/behaviors/dom/track-load-triggers.js +94 -0
- package/dist/dev/playback/behaviors/dom/track-load-triggers.js.map +1 -0
- package/dist/dev/playback/behaviors/dom/track-playback-rate.d.ts +34 -0
- package/dist/dev/playback/behaviors/dom/track-playback-rate.d.ts.map +1 -0
- package/dist/dev/playback/behaviors/dom/track-playback-rate.js +37 -0
- package/dist/dev/playback/behaviors/dom/track-playback-rate.js.map +1 -0
- package/dist/dev/playback/behaviors/dom/update-mediasource-duration.js +90 -0
- package/dist/dev/playback/behaviors/dom/update-mediasource-duration.js.map +1 -0
- package/dist/dev/playback/behaviors/resolve-presentation.d.ts +7 -0
- package/dist/dev/playback/behaviors/resolve-presentation.d.ts.map +1 -0
- package/dist/dev/playback/behaviors/resolve-presentation.js +85 -0
- package/dist/dev/playback/behaviors/resolve-presentation.js.map +1 -0
- package/dist/dev/playback/behaviors/resolve-track.js +120 -0
- package/dist/dev/playback/behaviors/resolve-track.js.map +1 -0
- package/dist/dev/playback/behaviors/select-tracks.d.ts +13 -0
- package/dist/dev/playback/behaviors/select-tracks.d.ts.map +1 -0
- package/dist/dev/playback/behaviors/select-tracks.js +101 -0
- package/dist/dev/playback/behaviors/select-tracks.js.map +1 -0
- package/dist/dev/playback/behaviors/setup-failover-monitor.d.ts +12 -0
- package/dist/dev/playback/behaviors/setup-failover-monitor.d.ts.map +1 -0
- package/dist/dev/playback/behaviors/setup-failover-monitor.js +66 -0
- package/dist/dev/playback/behaviors/setup-failover-monitor.js.map +1 -0
- package/dist/dev/playback/behaviors/sync-preload.js +59 -0
- package/dist/dev/playback/behaviors/sync-preload.js.map +1 -0
- package/dist/dev/playback/behaviors/track-switching.js +341 -0
- package/dist/dev/playback/behaviors/track-switching.js.map +1 -0
- package/dist/dev/playback/engines/background-video/adapter.d.ts +60 -0
- package/dist/dev/playback/engines/background-video/adapter.d.ts.map +1 -0
- package/dist/dev/playback/engines/background-video/adapter.js +158 -0
- package/dist/dev/playback/engines/background-video/adapter.js.map +1 -0
- package/dist/dev/playback/engines/background-video/engine.d.ts +107 -0
- package/dist/dev/playback/engines/background-video/engine.d.ts.map +1 -0
- package/dist/dev/playback/engines/background-video/engine.js +75 -0
- package/dist/dev/playback/engines/background-video/engine.js.map +1 -0
- package/dist/dev/playback/engines/hls/adapter-audio-only.d.ts +45 -0
- package/dist/dev/playback/engines/hls/adapter-audio-only.d.ts.map +1 -0
- package/dist/dev/playback/engines/hls/adapter-audio-only.js +119 -0
- package/dist/dev/playback/engines/hls/adapter-audio-only.js.map +1 -0
- package/dist/dev/{dom/playback-engine → playback/engines/hls}/adapter.d.ts +13 -12
- package/dist/dev/playback/engines/hls/adapter.d.ts.map +1 -0
- package/dist/dev/{dom/playback-engine → playback/engines/hls}/adapter.js +40 -26
- package/dist/dev/playback/engines/hls/adapter.js.map +1 -0
- package/dist/dev/playback/engines/hls/engine-audio-only.d.ts +127 -0
- package/dist/dev/playback/engines/hls/engine-audio-only.d.ts.map +1 -0
- package/dist/dev/playback/engines/hls/engine-audio-only.js +80 -0
- package/dist/dev/playback/engines/hls/engine-audio-only.js.map +1 -0
- package/dist/dev/playback/engines/hls/engine.d.ts +236 -0
- package/dist/dev/playback/engines/hls/engine.d.ts.map +1 -0
- package/dist/dev/playback/engines/hls/engine.js +115 -0
- package/dist/dev/playback/engines/hls/engine.js.map +1 -0
- package/dist/dev/playback/primitives/failover-fetch.js +42 -0
- package/dist/dev/playback/primitives/failover-fetch.js.map +1 -0
- package/dist/dev/playback/primitives/track-types.js +61 -0
- package/dist/dev/playback/primitives/track-types.js.map +1 -0
- package/package.json +12 -7
- package/dist/default/core/abr/bandwidth-estimator.js.map +0 -1
- package/dist/default/core/abr/ewma.js.map +0 -1
- package/dist/default/core/abr/quality-selection.js +0 -53
- package/dist/default/core/abr/quality-selection.js.map +0 -1
- package/dist/default/core/buffer/back-buffer.js.map +0 -1
- package/dist/default/core/buffer/forward-buffer.js.map +0 -1
- package/dist/default/core/create-machine-actor.js.map +0 -1
- package/dist/default/core/create-machine-reactor.js.map +0 -1
- package/dist/default/core/create-transition-actor.js.map +0 -1
- package/dist/default/core/features/calculate-presentation-duration.js +0 -53
- package/dist/default/core/features/calculate-presentation-duration.js.map +0 -1
- package/dist/default/core/features/quality-switching.js +0 -69
- package/dist/default/core/features/quality-switching.js.map +0 -1
- package/dist/default/core/features/resolve-presentation.js +0 -73
- package/dist/default/core/features/resolve-presentation.js.map +0 -1
- package/dist/default/core/features/resolve-track.js +0 -66
- package/dist/default/core/features/resolve-track.js.map +0 -1
- package/dist/default/core/features/select-tracks.js +0 -130
- package/dist/default/core/features/select-tracks.js.map +0 -1
- package/dist/default/core/features/sync-preload-attribute.js +0 -26
- package/dist/default/core/features/sync-preload-attribute.js.map +0 -1
- package/dist/default/core/hls/parse-attributes.js.map +0 -1
- package/dist/default/core/hls/parse-media-playlist.js.map +0 -1
- package/dist/default/core/hls/parse-multivariant.js.map +0 -1
- package/dist/default/core/hls/resolve-url.js.map +0 -1
- package/dist/default/core/task.js.map +0 -1
- package/dist/default/core/types/index.js +0 -15
- package/dist/default/core/types/index.js.map +0 -1
- package/dist/default/core/utils/generate-id.js +0 -21
- package/dist/default/core/utils/generate-id.js.map +0 -1
- package/dist/default/core/utils/track-selection.js +0 -39
- package/dist/default/core/utils/track-selection.js.map +0 -1
- package/dist/default/dom/features/end-of-stream.js +0 -145
- package/dist/default/dom/features/end-of-stream.js.map +0 -1
- package/dist/default/dom/features/load-segments.js +0 -192
- package/dist/default/dom/features/load-segments.js.map +0 -1
- package/dist/default/dom/features/load-text-track-cues.js +0 -104
- package/dist/default/dom/features/load-text-track-cues.js.map +0 -1
- package/dist/default/dom/features/segment-loader-actor.js.map +0 -1
- package/dist/default/dom/features/setup-mediasource.js +0 -48
- package/dist/default/dom/features/setup-mediasource.js.map +0 -1
- package/dist/default/dom/features/setup-sourcebuffer.js +0 -78
- package/dist/default/dom/features/setup-sourcebuffer.js.map +0 -1
- package/dist/default/dom/features/sync-text-tracks.js +0 -95
- package/dist/default/dom/features/sync-text-tracks.js.map +0 -1
- package/dist/default/dom/features/text-track-segment-loader-actor.js +0 -55
- package/dist/default/dom/features/text-track-segment-loader-actor.js.map +0 -1
- package/dist/default/dom/features/text-tracks-actor.js.map +0 -1
- package/dist/default/dom/features/track-current-time.js +0 -49
- package/dist/default/dom/features/track-current-time.js.map +0 -1
- package/dist/default/dom/features/track-playback-initiated.js +0 -66
- package/dist/default/dom/features/track-playback-initiated.js.map +0 -1
- package/dist/default/dom/features/track-playback-rate.js +0 -32
- package/dist/default/dom/features/track-playback-rate.js.map +0 -1
- package/dist/default/dom/features/update-duration.js +0 -78
- package/dist/default/dom/features/update-duration.js.map +0 -1
- package/dist/default/dom/media/append-segment.js.map +0 -1
- package/dist/default/dom/media/buffer-flusher.js.map +0 -1
- package/dist/default/dom/media/mediasource-setup.js.map +0 -1
- package/dist/default/dom/media/source-buffer-actor.js.map +0 -1
- package/dist/default/dom/network/chunked-stream-iterable.js.map +0 -1
- package/dist/default/dom/network/fetch.js +0 -54
- package/dist/default/dom/network/fetch.js.map +0 -1
- package/dist/default/dom/playback-engine/adapter.js.map +0 -1
- package/dist/default/dom/playback-engine/engine.js +0 -142
- package/dist/default/dom/playback-engine/engine.js.map +0 -1
- package/dist/default/dom/playback-engine.js +0 -4
- package/dist/default/dom/text/parse-vtt-segment.js.map +0 -1
- package/dist/dev/core/abr/bandwidth-estimator.d.ts.map +0 -1
- package/dist/dev/core/abr/bandwidth-estimator.js.map +0 -1
- package/dist/dev/core/abr/ewma.js.map +0 -1
- package/dist/dev/core/abr/quality-selection.js +0 -53
- package/dist/dev/core/abr/quality-selection.js.map +0 -1
- package/dist/dev/core/actor.d.ts.map +0 -1
- package/dist/dev/core/buffer/back-buffer.js.map +0 -1
- package/dist/dev/core/buffer/forward-buffer.js.map +0 -1
- package/dist/dev/core/create-machine-actor.d.ts +0 -12
- package/dist/dev/core/create-machine-actor.d.ts.map +0 -1
- package/dist/dev/core/create-machine-actor.js.map +0 -1
- package/dist/dev/core/create-machine-reactor.d.ts +0 -8
- package/dist/dev/core/create-machine-reactor.d.ts.map +0 -1
- package/dist/dev/core/create-machine-reactor.js.map +0 -1
- package/dist/dev/core/create-transition-actor.d.ts +0 -23
- package/dist/dev/core/create-transition-actor.d.ts.map +0 -1
- package/dist/dev/core/create-transition-actor.js.map +0 -1
- package/dist/dev/core/features/calculate-presentation-duration.js +0 -53
- package/dist/dev/core/features/calculate-presentation-duration.js.map +0 -1
- package/dist/dev/core/features/quality-switching.js +0 -69
- package/dist/dev/core/features/quality-switching.js.map +0 -1
- package/dist/dev/core/features/resolve-presentation.js +0 -73
- package/dist/dev/core/features/resolve-presentation.js.map +0 -1
- package/dist/dev/core/features/resolve-track.js +0 -66
- package/dist/dev/core/features/resolve-track.js.map +0 -1
- package/dist/dev/core/features/select-tracks.js +0 -130
- package/dist/dev/core/features/select-tracks.js.map +0 -1
- package/dist/dev/core/features/sync-preload-attribute.js +0 -26
- package/dist/dev/core/features/sync-preload-attribute.js.map +0 -1
- package/dist/dev/core/hls/parse-attributes.js.map +0 -1
- package/dist/dev/core/hls/parse-media-playlist.js.map +0 -1
- package/dist/dev/core/hls/parse-multivariant.js.map +0 -1
- package/dist/dev/core/hls/resolve-url.js.map +0 -1
- package/dist/dev/core/task.js.map +0 -1
- package/dist/dev/core/types/index.d.ts.map +0 -1
- package/dist/dev/core/types/index.js +0 -15
- package/dist/dev/core/types/index.js.map +0 -1
- package/dist/dev/core/utils/generate-id.js +0 -21
- package/dist/dev/core/utils/generate-id.js.map +0 -1
- package/dist/dev/core/utils/track-selection.d.ts +0 -16
- package/dist/dev/core/utils/track-selection.d.ts.map +0 -1
- package/dist/dev/core/utils/track-selection.js +0 -39
- package/dist/dev/core/utils/track-selection.js.map +0 -1
- package/dist/dev/dom/features/end-of-stream.js +0 -145
- package/dist/dev/dom/features/end-of-stream.js.map +0 -1
- package/dist/dev/dom/features/load-segments.d.ts +0 -72
- package/dist/dev/dom/features/load-segments.d.ts.map +0 -1
- package/dist/dev/dom/features/load-segments.js +0 -192
- package/dist/dev/dom/features/load-segments.js.map +0 -1
- package/dist/dev/dom/features/load-text-track-cues.d.ts +0 -85
- package/dist/dev/dom/features/load-text-track-cues.d.ts.map +0 -1
- package/dist/dev/dom/features/load-text-track-cues.js +0 -104
- package/dist/dev/dom/features/load-text-track-cues.js.map +0 -1
- package/dist/dev/dom/features/segment-loader-actor.d.ts +0 -1
- package/dist/dev/dom/features/segment-loader-actor.js.map +0 -1
- package/dist/dev/dom/features/setup-mediasource.js +0 -48
- package/dist/dev/dom/features/setup-mediasource.js.map +0 -1
- package/dist/dev/dom/features/setup-sourcebuffer.d.ts +0 -9
- package/dist/dev/dom/features/setup-sourcebuffer.d.ts.map +0 -1
- package/dist/dev/dom/features/setup-sourcebuffer.js +0 -78
- package/dist/dev/dom/features/setup-sourcebuffer.js.map +0 -1
- package/dist/dev/dom/features/sync-text-tracks.js +0 -95
- package/dist/dev/dom/features/sync-text-tracks.js.map +0 -1
- package/dist/dev/dom/features/text-track-segment-loader-actor.d.ts +0 -12
- package/dist/dev/dom/features/text-track-segment-loader-actor.d.ts.map +0 -1
- package/dist/dev/dom/features/text-track-segment-loader-actor.js +0 -55
- package/dist/dev/dom/features/text-track-segment-loader-actor.js.map +0 -1
- package/dist/dev/dom/features/text-tracks-actor.d.ts +0 -31
- package/dist/dev/dom/features/text-tracks-actor.d.ts.map +0 -1
- package/dist/dev/dom/features/text-tracks-actor.js.map +0 -1
- package/dist/dev/dom/features/track-current-time.d.ts +0 -40
- package/dist/dev/dom/features/track-current-time.d.ts.map +0 -1
- package/dist/dev/dom/features/track-current-time.js +0 -49
- package/dist/dev/dom/features/track-current-time.js.map +0 -1
- package/dist/dev/dom/features/track-playback-initiated.d.ts +0 -46
- package/dist/dev/dom/features/track-playback-initiated.d.ts.map +0 -1
- package/dist/dev/dom/features/track-playback-initiated.js +0 -66
- package/dist/dev/dom/features/track-playback-initiated.js.map +0 -1
- package/dist/dev/dom/features/track-playback-rate.d.ts +0 -34
- package/dist/dev/dom/features/track-playback-rate.d.ts.map +0 -1
- package/dist/dev/dom/features/track-playback-rate.js +0 -32
- package/dist/dev/dom/features/track-playback-rate.js.map +0 -1
- package/dist/dev/dom/features/update-duration.js +0 -78
- package/dist/dev/dom/features/update-duration.js.map +0 -1
- package/dist/dev/dom/media/append-segment.d.ts.map +0 -1
- package/dist/dev/dom/media/append-segment.js.map +0 -1
- package/dist/dev/dom/media/buffer-flusher.d.ts.map +0 -1
- package/dist/dev/dom/media/buffer-flusher.js.map +0 -1
- package/dist/dev/dom/media/mediasource-setup.js.map +0 -1
- package/dist/dev/dom/media/source-buffer-actor.d.ts.map +0 -1
- package/dist/dev/dom/media/source-buffer-actor.js.map +0 -1
- package/dist/dev/dom/network/chunked-stream-iterable.js.map +0 -1
- package/dist/dev/dom/network/fetch.js +0 -54
- package/dist/dev/dom/network/fetch.js.map +0 -1
- package/dist/dev/dom/playback-engine/adapter.d.ts.map +0 -1
- package/dist/dev/dom/playback-engine/adapter.js.map +0 -1
- package/dist/dev/dom/playback-engine/engine.d.ts +0 -124
- package/dist/dev/dom/playback-engine/engine.d.ts.map +0 -1
- package/dist/dev/dom/playback-engine/engine.js +0 -142
- package/dist/dev/dom/playback-engine/engine.js.map +0 -1
- package/dist/dev/dom/playback-engine.d.ts +0 -4
- package/dist/dev/dom/playback-engine.js +0 -4
- package/dist/dev/dom/text/parse-vtt-segment.d.ts +0 -12
- package/dist/dev/dom/text/parse-vtt-segment.d.ts.map +0 -1
- package/dist/dev/dom/text/parse-vtt-segment.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"create-machine-actor.js","names":[],"sources":["../../../src/core/create-machine-actor.ts"],"sourcesContent":["import type { ActorSnapshot, SignalActor } from './actor';\nimport { createMachineCore } from './machine';\nimport { untrack, update } from './signals/primitives';\nimport type { TaskLike } from './task';\n\n// =============================================================================\n// Runner interfaces\n// =============================================================================\n\n/**\n * Minimal interface for any runner that can be used with createMachineActor.\n */\nexport interface RunnerLike {\n schedule<Value = void, Err = unknown>(task: TaskLike<Value, Err>): Promise<Value>;\n abortAll(): void;\n destroy(): void;\n whenSettled(callback: () => void): void;\n}\n\n// =============================================================================\n// Definition types\n// =============================================================================\n\n/**\n * Context passed to message handlers.\n * `runner` is present and typed as the exact runner instance only when the\n * definition includes a runner factory.\n */\nexport type HandlerContext<\n UserState extends string,\n Context extends object,\n RunnerFactory extends (() => RunnerLike) | undefined,\n> = {\n transition: (to: UserState) => void;\n /** Context snapshot captured at dispatch time. Stale after any `setContext` call. */\n context: Context;\n /**\n * Live untracked read of the current context. Use in async task closures that\n * execute after the handler returns — e.g. `getCtx: getContext` passed to tasks\n * scheduled on the runner, so each task reads the context committed by the\n * previous task rather than the stale snapshot from dispatch time.\n */\n getContext: () => Context;\n setContext: (next: Context) => void;\n} & (RunnerFactory extends () => infer R ? { runner: R } : object);\n\n/**\n * Definition for a single user-defined state.\n */\nexport type ActorStateDefinition<\n UserState extends string,\n Context extends object,\n Message extends { type: string },\n RunnerFactory extends (() => RunnerLike) | undefined,\n> = {\n /**\n * When the actor's runner settles while in this state, automatically\n * transition to this state. The framework owns the generation-token logic —\n * re-registering after each `runner.schedule()` call so that\n * `abortAll()` + reschedule correctly supersedes stale callbacks.\n */\n onSettled?: UserState;\n /** Message handlers active in this state. Messages with no handler are silently dropped. */\n on?: {\n [M in Message as M['type']]?: (\n message: Extract<Message, { type: M['type'] }>,\n ctx: HandlerContext<UserState, Context, RunnerFactory>\n ) => void;\n };\n};\n\n/**\n * Full actor definition passed to `createMachineActor`.\n *\n * `UserState` is the set of domain-meaningful states. `'destroyed'` is always\n * added by the framework as the implicit terminal state — do not include it here.\n */\nexport type ActorDefinition<\n UserState extends string,\n Context extends object,\n Message extends { type: string },\n RunnerFactory extends (() => RunnerLike) | undefined = undefined,\n> = {\n /**\n * Runner factory — called once at `createMachineActor()` time.\n * The runner lives for the full actor lifetime and is destroyed with it.\n *\n * @example\n * runner: () => new SerialRunner()\n */\n runner?: RunnerFactory;\n /** Initial state. */\n initial: UserState;\n /** Initial context. */\n context: Context;\n /**\n * Per-state definitions. States with no definition silently drop all messages.\n * All user-defined states must appear as keys in the `UserState` union.\n */\n states: Partial<Record<UserState, ActorStateDefinition<UserState, Context, Message, RunnerFactory>>>;\n};\n\n// =============================================================================\n// Live actor interface\n// =============================================================================\n\n/** Live actor instance returned by `createMachineActor`. */\nexport interface MessageActor<State extends string, Context extends object, Message extends { type: string }>\n extends SignalActor<State, Context> {\n send(message: Message): void;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\n/**\n * Creates a message-driven actor from a declarative definition.\n *\n * The actor owns a reactive snapshot signal (state + context), an optional\n * runner, and dispatches incoming messages to per-state handlers. `'destroyed'`\n * is always the implicit terminal state — `destroy()` transitions there\n * unconditionally and all subsequent `send()` calls are no-ops.\n *\n * When a state declares `onSettled`, the framework calls `runner.whenSettled()`\n * after the handler returns. The runner owns the generation-token logic — if\n * new tasks are scheduled before the current batch settles, the callback is\n * automatically superseded.\n *\n * @example\n * const actor = createMachineActor({\n * runner: () => new SerialRunner(),\n * initial: 'idle',\n * context: {},\n * states: {\n * idle: {\n * on: {\n * load: (msg, { transition, runner }) => {\n * segments.forEach(s => runner.schedule(new Task(...)));\n * transition('loading');\n * }\n * }\n * },\n * loading: {\n * onSettled: 'idle',\n * on: {\n * load: (msg, { runner }) => {\n * runner.abortAll();\n * segments.forEach(s => runner.schedule(new Task(...)));\n * }\n * }\n * }\n * }\n * });\n */\nexport function createMachineActor<\n UserState extends string,\n Context extends object,\n Message extends { type: string },\n RunnerFactory extends (() => RunnerLike) | undefined = undefined,\n>(\n def: ActorDefinition<UserState, Context, Message, RunnerFactory>\n): MessageActor<UserState | 'destroyed', Context, Message> {\n type FullState = UserState | 'destroyed';\n\n const runner = def.runner?.() as RunnerLike | undefined;\n const { snapshotSignal, getState, transition } = createMachineCore<FullState, ActorSnapshot<FullState, Context>>({\n value: def.initial as FullState,\n context: def.context,\n });\n\n const getContext = (): Context => untrack(() => snapshotSignal.get().context);\n\n const setContext = (context: Context): void => {\n update(snapshotSignal, { context });\n };\n\n return {\n get snapshot() {\n return snapshotSignal;\n },\n\n send(message: Message): void {\n const state = getState();\n if (state === 'destroyed') return;\n const stateDef = def.states[state as UserState];\n const handler = stateDef?.on?.[message.type as keyof typeof stateDef.on] as\n | ((msg: Message, ctx: HandlerContext<UserState, Context, RunnerFactory>) => void)\n | undefined;\n if (!handler) return;\n handler(message, {\n context: getContext(),\n getContext,\n transition: (to: UserState) => transition(to as FullState),\n setContext,\n ...(runner ? { runner } : {}),\n } as HandlerContext<UserState, Context, RunnerFactory>);\n // Register onSettled after the handler so we read the post-transition state.\n const newState = getState();\n if (newState !== 'destroyed') {\n const newStateDef = def.states[newState as UserState];\n if (newStateDef?.onSettled && runner) {\n const targetState = newStateDef.onSettled as FullState;\n runner.whenSettled(() => {\n if (getState() !== newState) return;\n transition(targetState);\n });\n }\n }\n },\n\n destroy(): void {\n if (getState() === 'destroyed') return;\n runner?.destroy();\n transition('destroyed');\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2JA,SAAgB,mBAMd,KACyD;CAGzD,MAAM,SAAS,IAAI,UAAU;CAC7B,MAAM,EAAE,gBAAgB,UAAU,eAAe,kBAAgE;EAC/G,OAAO,IAAI;EACX,SAAS,IAAI;EACd,CAAC;CAEF,MAAM,mBAA4B,cAAc,eAAe,KAAK,CAAC,QAAQ;CAE7E,MAAM,cAAc,YAA2B;AAC7C,SAAO,gBAAgB,EAAE,SAAS,CAAC;;AAGrC,QAAO;EACL,IAAI,WAAW;AACb,UAAO;;EAGT,KAAK,SAAwB;GAC3B,MAAM,QAAQ,UAAU;AACxB,OAAI,UAAU,YAAa;GAE3B,MAAM,UADW,IAAI,OAAO,QACF,KAAK,QAAQ;AAGvC,OAAI,CAAC,QAAS;AACd,WAAQ,SAAS;IACf,SAAS,YAAY;IACrB;IACA,aAAa,OAAkB,WAAW,GAAgB;IAC1D;IACA,GAAI,SAAS,EAAE,QAAQ,GAAG,EAAE;IAC7B,CAAsD;GAEvD,MAAM,WAAW,UAAU;AAC3B,OAAI,aAAa,aAAa;IAC5B,MAAM,cAAc,IAAI,OAAO;AAC/B,QAAI,aAAa,aAAa,QAAQ;KACpC,MAAM,cAAc,YAAY;AAChC,YAAO,kBAAkB;AACvB,UAAI,UAAU,KAAK,SAAU;AAC7B,iBAAW,YAAY;OACvB;;;;EAKR,UAAgB;AACd,OAAI,UAAU,KAAK,YAAa;AAChC,WAAQ,SAAS;AACjB,cAAW,YAAY;;EAE1B"}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { Machine, MachineSnapshot } from "./machine.js";
|
|
2
|
-
|
|
3
|
-
//#region src/core/create-machine-reactor.d.ts
|
|
4
|
-
/** Live reactor instance returned by `createMachineReactor`. */
|
|
5
|
-
type Reactor<State extends string> = Machine<MachineSnapshot<State>>;
|
|
6
|
-
//#endregion
|
|
7
|
-
export { Reactor };
|
|
8
|
-
//# sourceMappingURL=create-machine-reactor.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"create-machine-reactor.d.ts","names":[],"sources":["../../../src/core/create-machine-reactor.ts"],"mappings":";;;;KAyEY,OAAA,yBAAgC,OAAA,CAAQ,eAAA,CAAgB,KAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"create-machine-reactor.js","names":[],"sources":["../../../src/core/create-machine-reactor.ts"],"sourcesContent":["import type { Machine, MachineSnapshot } from './machine';\nimport { createMachineCore } from './machine';\nimport { effect } from './signals/effect';\nimport { untrack } from './signals/primitives';\n\n// =============================================================================\n// Definition types\n// =============================================================================\n\n/**\n * A reactive state-deriving function used in the `monitor` field.\n *\n * Returns the target state the reactor should be in. Any signals read inside\n * the fn body create reactive dependencies — the framework re-evaluates it when\n * those signals change and automatically calls `transition()` when the returned\n * state differs from the current one.\n */\nexport type ReactorDeriveFn<State extends string> = () => State;\n\n/**\n * An effect function used in reactor `entry` and `effects` blocks.\n *\n * May return a cleanup function that runs before each re-evaluation and on\n * state exit (including destroy).\n */\nexport type ReactorEffectFn = () => (() => void) | { abort(): void } | void;\n\n/**\n * Per-state effect grouping for a single reactor state.\n *\n * - `entry` effects run once on state entry. The fn body is automatically\n * untracked — no `untrack()` calls are needed inside. Use this for\n * one-time setup: reading current values, attaching event listeners, etc.\n * - `effects` run on state entry and re-run whenever a signal read inside\n * the fn body changes. Use `untrack()` for reads you do not want to track.\n * Use this for work that must stay in sync with reactive state.\n *\n * Both are optional; pass `{}` for states with no effects.\n */\nexport type ReactorStateDefinition = {\n entry?: ReactorEffectFn | ReactorEffectFn[];\n effects?: ReactorEffectFn | ReactorEffectFn[];\n};\n\n/**\n * Full reactor definition passed to `createMachineReactor`.\n *\n * `State` is the set of domain-meaningful states. `'destroying'` and\n * `'destroyed'` are always added by the framework as implicit terminal states —\n * do not include them here.\n */\nexport type ReactorDefinition<State extends string> = {\n /** Initial state. */\n initial: State;\n /**\n * Reactive state derivation. Registered before per-state effects — the\n * ordering guarantee ensures transitions fired here take effect before\n * per-state effects re-evaluate in the same flush.\n */\n monitor?: ReactorDeriveFn<State> | ReactorDeriveFn<State>[];\n /**\n * Per-state effect groupings. Every valid state must be declared — pass `{}`\n * for states with no effects. `entry` and `effects` each become independent\n * `effect()` calls gated on that state, with their own cleanup lifecycles.\n */\n states: Record<State, ReactorStateDefinition>;\n};\n\n// =============================================================================\n// Live reactor interface\n// =============================================================================\n\n/** Live reactor instance returned by `createMachineReactor`. */\nexport type Reactor<State extends string> = Machine<MachineSnapshot<State>>;\n\n// =============================================================================\n// Implementation helpers\n// =============================================================================\n\nconst toArray = <T>(x: T | T[] | undefined): T[] => (x === undefined ? [] : Array.isArray(x) ? x : [x]);\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\n/**\n * Creates a reactive Reactor from a declarative definition.\n *\n * A Reactor is driven by subscriptions to external signals rather than\n * imperative messages. Each state holds an array of effect functions —\n * every element becomes one independent `effect()` call gated on that state,\n * with its own dependency tracking and cleanup lifecycle.\n *\n * `'destroying'` and `'destroyed'` are always implicit terminal states.\n * `destroy()` transitions through both in sequence: `'destroying'` first (for\n * potential async teardown in a future extension), then immediately `'destroyed'`\n * for the synchronous base case. Active effect cleanups fire via disposal.\n *\n * @example\n * const reactor = createMachineReactor({\n * initial: 'waiting',\n * monitor: () => srcSignal.get() ? 'active' : 'waiting',\n * states: {\n * active: {\n * // entry: runs once on state entry; fn body is automatically untracked.\n * entry: () => listen(el, 'play', handler),\n * // effects: re-runs whenever tracked signals change.\n * effects: () => { currentTimeSignal.get(); return cleanup; },\n * },\n * waiting: {},\n * }\n * });\n */\nexport function createMachineReactor<State extends string>(\n def: ReactorDefinition<State>\n): Reactor<State | 'destroying' | 'destroyed'> {\n type FullState = State | 'destroying' | 'destroyed';\n\n const { snapshotSignal, getState, transition } = createMachineCore<FullState, MachineSnapshot<FullState>>({\n value: def.initial as FullState,\n });\n\n const effectDisposals: Array<() => void> = [];\n\n const wrapResult = (result: ReturnType<ReactorEffectFn>) => {\n if (!result) return undefined;\n if (typeof result === 'function') return result;\n return () => result.abort();\n };\n\n type EffectCall = () => ReturnType<ReactorEffectFn>;\n\n type EffectDescriptor = {\n fn: ReactorEffectFn;\n shouldSkip: (snapshot: { value: FullState }) => boolean;\n toFnCall?: (baseCall: EffectCall) => EffectCall;\n };\n\n const untracked: EffectDescriptor['toFnCall'] = (baseCall) => () => untrack(baseCall);\n\n const isTerminal = (snapshot: { value: FullState }) =>\n snapshot.value === 'destroying' || snapshot.value === 'destroyed';\n\n // `monitor` descriptors are built first — the ordering guarantee ensures\n // transitions they trigger take effect before per-state effects re-evaluate\n // in the same flush. See the comment on effect registration order in the\n // previous implementation for full details.\n const descriptors: EffectDescriptor[] = [\n ...toArray(def.monitor).map((fn) => ({\n fn: () => {\n const target = fn();\n if (target !== (getState() as State)) transition(target as FullState);\n },\n shouldSkip: isTerminal,\n })),\n ...(Object.entries(def.states) as Array<[State, ReactorStateDefinition]>).flatMap(([state, stateDef]) => {\n const isNotState = (snapshot: { value: FullState }) => snapshot.value !== state;\n return [\n ...toArray(stateDef.entry).map((fn) => ({ fn, shouldSkip: isNotState, toFnCall: untracked })),\n ...toArray(stateDef.effects).map((fn) => ({ fn, shouldSkip: isNotState })),\n ];\n }),\n ];\n\n const toEffect = ({ fn, shouldSkip, toFnCall = (baseCall) => baseCall }: EffectDescriptor) =>\n effect(() => {\n const snapshot = snapshotSignal.get();\n if (shouldSkip(snapshot)) return;\n const baseCall = () => fn();\n return wrapResult(toFnCall(baseCall)());\n });\n\n effectDisposals.push(...descriptors.map(toEffect));\n\n return {\n get snapshot() {\n return snapshotSignal;\n },\n\n destroy(): void {\n const state = getState();\n if (state === 'destroying' || state === 'destroyed') return;\n // Two-step teardown: transition through 'destroying' first to leave room\n // for async teardown in a future extension, then immediately 'destroyed'\n // for the synchronous base case. Active effect cleanups fire via disposal.\n transition('destroying');\n transition('destroyed');\n for (const dispose of effectDisposals) dispose();\n },\n };\n}\n"],"mappings":";;;;AA+EA,MAAM,WAAc,MAAiC,MAAM,KAAA,IAAY,EAAE,GAAG,MAAM,QAAQ,EAAE,GAAG,IAAI,CAAC,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCtG,SAAgB,qBACd,KAC6C;CAG7C,MAAM,EAAE,gBAAgB,UAAU,eAAe,kBAAyD,EACxG,OAAO,IAAI,SACZ,CAAC;CAEF,MAAM,kBAAqC,EAAE;CAE7C,MAAM,cAAc,WAAwC;AAC1D,MAAI,CAAC,OAAQ,QAAO,KAAA;AACpB,MAAI,OAAO,WAAW,WAAY,QAAO;AACzC,eAAa,OAAO,OAAO;;CAW7B,MAAM,aAA2C,mBAAmB,QAAQ,SAAS;CAErF,MAAM,cAAc,aAClB,SAAS,UAAU,gBAAgB,SAAS,UAAU;CAMxD,MAAM,cAAkC,CACtC,GAAG,QAAQ,IAAI,QAAQ,CAAC,KAAK,QAAQ;EACnC,UAAU;GACR,MAAM,SAAS,IAAI;AACnB,OAAI,WAAY,UAAU,CAAY,YAAW,OAAoB;;EAEvE,YAAY;EACb,EAAE,EACH,GAAI,OAAO,QAAQ,IAAI,OAAO,CAA4C,SAAS,CAAC,OAAO,cAAc;EACvG,MAAM,cAAc,aAAmC,SAAS,UAAU;AAC1E,SAAO,CACL,GAAG,QAAQ,SAAS,MAAM,CAAC,KAAK,QAAQ;GAAE;GAAI,YAAY;GAAY,UAAU;GAAW,EAAE,EAC7F,GAAG,QAAQ,SAAS,QAAQ,CAAC,KAAK,QAAQ;GAAE;GAAI,YAAY;GAAY,EAAE,CAC3E;GACD,CACH;CAED,MAAM,YAAY,EAAE,IAAI,YAAY,YAAY,aAAa,eAC3D,aAAa;AAEX,MAAI,WADa,eAAe,KAAK,CACb,CAAE;EAC1B,MAAM,iBAAiB,IAAI;AAC3B,SAAO,WAAW,SAAS,SAAS,EAAE,CAAC;GACvC;AAEJ,iBAAgB,KAAK,GAAG,YAAY,IAAI,SAAS,CAAC;AAElD,QAAO;EACL,IAAI,WAAW;AACb,UAAO;;EAGT,UAAgB;GACd,MAAM,QAAQ,UAAU;AACxB,OAAI,UAAU,gBAAgB,UAAU,YAAa;AAIrD,cAAW,aAAa;AACxB,cAAW,YAAY;AACvB,QAAK,MAAM,WAAW,gBAAiB,UAAS;;EAEnD"}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { Machine } from "./machine.js";
|
|
2
|
-
import { ActorSnapshot } from "./actor.js";
|
|
3
|
-
|
|
4
|
-
//#region src/core/create-transition-actor.d.ts
|
|
5
|
-
/**
|
|
6
|
-
* A reducer-shaped actor: `(context, message) => context`.
|
|
7
|
-
*
|
|
8
|
-
* No finite states — the snapshot carries `value: 'active' | 'destroyed'`
|
|
9
|
-
* as a universal lifecycle marker rather than domain state. The interesting
|
|
10
|
-
* state is entirely in the context, which is reactive via `snapshot`.
|
|
11
|
-
*
|
|
12
|
-
* Use this when the actor has context that needs to be reactive but no
|
|
13
|
-
* meaningful state machine (e.g., a message-driven model with DOM side
|
|
14
|
-
* effects). For actors that need per-state behavior, use `createMachineActor`.
|
|
15
|
-
*/
|
|
16
|
-
interface TransitionActor<Context extends object, Message extends {
|
|
17
|
-
type: string;
|
|
18
|
-
}> extends Machine<ActorSnapshot<'active' | 'destroyed', Context>> {
|
|
19
|
-
send(message: Message): void;
|
|
20
|
-
}
|
|
21
|
-
//#endregion
|
|
22
|
-
export { TransitionActor };
|
|
23
|
-
//# sourceMappingURL=create-transition-actor.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"create-transition-actor.d.ts","names":[],"sources":["../../../src/core/create-transition-actor.ts"],"mappings":";;;;;;AAoBA;;;;;;;;;UAAiB,eAAA;EAA0D,IAAA;AAAA,WACjE,OAAA,CAAQ,aAAA,yBAAsC,OAAA;EACtD,IAAA,CAAK,OAAA,EAAS,OAAA;AAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"create-transition-actor.js","names":[],"sources":["../../../src/core/create-transition-actor.ts"],"sourcesContent":["import type { ActorSnapshot } from './actor';\nimport type { Machine } from './machine';\nimport { createMachineCore } from './machine';\nimport { untrack, update } from './signals/primitives';\n\n// =============================================================================\n// Definition types\n// =============================================================================\n\n/**\n * A reducer-shaped actor: `(context, message) => context`.\n *\n * No finite states — the snapshot carries `value: 'active' | 'destroyed'`\n * as a universal lifecycle marker rather than domain state. The interesting\n * state is entirely in the context, which is reactive via `snapshot`.\n *\n * Use this when the actor has context that needs to be reactive but no\n * meaningful state machine (e.g., a message-driven model with DOM side\n * effects). For actors that need per-state behavior, use `createMachineActor`.\n */\nexport interface TransitionActor<Context extends object, Message extends { type: string }>\n extends Machine<ActorSnapshot<'active' | 'destroyed', Context>> {\n send(message: Message): void;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\n/**\n * Creates a reducer-shaped actor from an initial context and a reducer function.\n *\n * The reducer receives the current context and a message and returns the next\n * context. Returning the same reference (by identity) skips the signal update —\n * so early-returning `context` unchanged is both the no-op and the optimization.\n *\n * Side effects (e.g. DOM mutations) may be performed inside the reducer.\n * They run synchronously before the signal is updated.\n *\n * @example\n * const actor = createTransitionActor(\n * { count: 0 },\n * (context, message: { type: 'increment' }) => ({ count: context.count + 1 })\n * );\n */\nexport function createTransitionActor<Context extends object, Message extends { type: string }>(\n initialContext: Context,\n reducer: (context: Context, message: Message) => Context\n): TransitionActor<Context, Message> {\n const { snapshotSignal, getState, transition } = createMachineCore<\n 'active' | 'destroyed',\n ActorSnapshot<'active' | 'destroyed', Context>\n >({ value: 'active', context: initialContext });\n\n const getContext = (): Context => untrack(() => snapshotSignal.get().context);\n const setContext = (context: Context): void => update(snapshotSignal, { context });\n\n return {\n get snapshot() {\n return snapshotSignal;\n },\n\n send(message: Message): void {\n if (getState() === 'destroyed') return;\n const context = getContext();\n const newContext = reducer(context, message);\n if (newContext !== context) setContext(newContext);\n },\n\n destroy(): void {\n if (getState() === 'destroyed') return;\n transition('destroyed');\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA6CA,SAAgB,sBACd,gBACA,SACmC;CACnC,MAAM,EAAE,gBAAgB,UAAU,eAAe,kBAG/C;EAAE,OAAO;EAAU,SAAS;EAAgB,CAAC;CAE/C,MAAM,mBAA4B,cAAc,eAAe,KAAK,CAAC,QAAQ;CAC7E,MAAM,cAAc,YAA2B,OAAO,gBAAgB,EAAE,SAAS,CAAC;AAElF,QAAO;EACL,IAAI,WAAW;AACb,UAAO;;EAGT,KAAK,SAAwB;AAC3B,OAAI,UAAU,KAAK,YAAa;GAChC,MAAM,UAAU,YAAY;GAC5B,MAAM,aAAa,QAAQ,SAAS,QAAQ;AAC5C,OAAI,eAAe,QAAS,YAAW,WAAW;;EAGpD,UAAgB;AACd,OAAI,UAAU,KAAK,YAAa;AAChC,cAAW,YAAY;;EAE1B"}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { effect } from "../signals/effect.js";
|
|
2
|
-
import { isResolvedTrack } from "../types/index.js";
|
|
3
|
-
import { getSelectedTrack } from "../utils/track-selection.js";
|
|
4
|
-
//#region src/core/features/calculate-presentation-duration.ts
|
|
5
|
-
/**
|
|
6
|
-
* Check if we can calculate presentation duration (have required data).
|
|
7
|
-
*/
|
|
8
|
-
function canCalculateDuration(state) {
|
|
9
|
-
if (!state.presentation) return false;
|
|
10
|
-
return !!(state.selectedVideoTrackId || state.selectedAudioTrackId);
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* Check if we should calculate presentation duration (conditions met).
|
|
14
|
-
*/
|
|
15
|
-
function shouldCalculateDuration(state) {
|
|
16
|
-
if (!canCalculateDuration(state)) return false;
|
|
17
|
-
const { presentation } = state;
|
|
18
|
-
if (presentation.duration !== void 0) return false;
|
|
19
|
-
const videoTrack = state.selectedVideoTrackId ? getSelectedTrack(state, "video") : void 0;
|
|
20
|
-
const audioTrack = state.selectedAudioTrackId ? getSelectedTrack(state, "audio") : void 0;
|
|
21
|
-
return !!(videoTrack && isResolvedTrack(videoTrack) || audioTrack && isResolvedTrack(audioTrack));
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Get duration from the first resolved track (prefer video, fallback to audio).
|
|
25
|
-
*/
|
|
26
|
-
function getDurationFromResolvedTracks(state) {
|
|
27
|
-
const videoTrack = state.selectedVideoTrackId ? getSelectedTrack(state, "video") : void 0;
|
|
28
|
-
if (videoTrack && isResolvedTrack(videoTrack)) return videoTrack.duration;
|
|
29
|
-
const audioTrack = state.selectedAudioTrackId ? getSelectedTrack(state, "audio") : void 0;
|
|
30
|
-
if (audioTrack && isResolvedTrack(audioTrack)) return audioTrack.duration;
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Calculate and set presentation duration from resolved tracks.
|
|
34
|
-
*/
|
|
35
|
-
function calculatePresentationDuration({ state }) {
|
|
36
|
-
return effect(() => {
|
|
37
|
-
const currentState = state.get();
|
|
38
|
-
if (!shouldCalculateDuration(currentState)) return;
|
|
39
|
-
const duration = getDurationFromResolvedTracks(currentState);
|
|
40
|
-
if (duration === void 0 || !Number.isFinite(duration)) return;
|
|
41
|
-
state.set({
|
|
42
|
-
...currentState,
|
|
43
|
-
presentation: {
|
|
44
|
-
...currentState.presentation,
|
|
45
|
-
duration
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
//#endregion
|
|
51
|
-
export { calculatePresentationDuration };
|
|
52
|
-
|
|
53
|
-
//# sourceMappingURL=calculate-presentation-duration.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"calculate-presentation-duration.js","names":[],"sources":["../../../../src/core/features/calculate-presentation-duration.ts"],"sourcesContent":["import { effect } from '../signals/effect';\nimport type { Signal } from '../signals/primitives';\nimport type { AudioTrack, Presentation, VideoTrack } from '../types';\nimport { isResolvedTrack } from '../types';\nimport { getSelectedTrack } from '../utils/track-selection';\n\nexport interface PresentationDurationState {\n presentation?: Presentation;\n selectedVideoTrackId?: string;\n selectedAudioTrackId?: string;\n}\n\n/**\n * Check if we can calculate presentation duration (have required data).\n */\nexport function canCalculateDuration(state: PresentationDurationState): boolean {\n if (!state.presentation) return false;\n // Need at least one selected track\n return !!(state.selectedVideoTrackId || state.selectedAudioTrackId);\n}\n\n/**\n * Check if we should calculate presentation duration (conditions met).\n */\nexport function shouldCalculateDuration(state: PresentationDurationState): boolean {\n if (!canCalculateDuration(state)) return false;\n\n const { presentation } = state;\n\n // Don't recalculate if already set\n if (presentation!.duration !== undefined) return false;\n\n // Check if any selected track is resolved\n const videoTrack = state.selectedVideoTrackId ? getSelectedTrack(state, 'video') : undefined;\n const audioTrack = state.selectedAudioTrackId ? getSelectedTrack(state, 'audio') : undefined;\n\n // At least one track must be resolved (has segments and duration)\n return !!((videoTrack && isResolvedTrack(videoTrack)) || (audioTrack && isResolvedTrack(audioTrack)));\n}\n\n/**\n * Get duration from the first resolved track (prefer video, fallback to audio).\n */\nexport function getDurationFromResolvedTracks(state: PresentationDurationState): number | undefined {\n // Try video track first\n const videoTrack = state.selectedVideoTrackId\n ? (getSelectedTrack(state, 'video') as VideoTrack | undefined)\n : undefined;\n if (videoTrack && isResolvedTrack(videoTrack)) {\n return videoTrack.duration;\n }\n\n // Fallback to audio track\n const audioTrack = state.selectedAudioTrackId\n ? (getSelectedTrack(state, 'audio') as AudioTrack | undefined)\n : undefined;\n if (audioTrack && isResolvedTrack(audioTrack)) {\n return audioTrack.duration;\n }\n\n return undefined;\n}\n\n/**\n * Calculate and set presentation duration from resolved tracks.\n */\nexport function calculatePresentationDuration<S extends PresentationDurationState>({\n state,\n}: {\n state: Signal<S>;\n}): () => void {\n return effect(() => {\n const currentState = state.get();\n if (!shouldCalculateDuration(currentState)) return;\n\n const duration = getDurationFromResolvedTracks(currentState);\n if (duration === undefined || !Number.isFinite(duration)) return;\n\n state.set({\n ...currentState,\n presentation: {\n ...currentState.presentation!,\n duration,\n },\n } as S);\n });\n}\n"],"mappings":";;;;;;;AAeA,SAAgB,qBAAqB,OAA2C;AAC9E,KAAI,CAAC,MAAM,aAAc,QAAO;AAEhC,QAAO,CAAC,EAAE,MAAM,wBAAwB,MAAM;;;;;AAMhD,SAAgB,wBAAwB,OAA2C;AACjF,KAAI,CAAC,qBAAqB,MAAM,CAAE,QAAO;CAEzC,MAAM,EAAE,iBAAiB;AAGzB,KAAI,aAAc,aAAa,KAAA,EAAW,QAAO;CAGjD,MAAM,aAAa,MAAM,uBAAuB,iBAAiB,OAAO,QAAQ,GAAG,KAAA;CACnF,MAAM,aAAa,MAAM,uBAAuB,iBAAiB,OAAO,QAAQ,GAAG,KAAA;AAGnF,QAAO,CAAC,EAAG,cAAc,gBAAgB,WAAW,IAAM,cAAc,gBAAgB,WAAW;;;;;AAMrG,SAAgB,8BAA8B,OAAsD;CAElG,MAAM,aAAa,MAAM,uBACpB,iBAAiB,OAAO,QAAQ,GACjC,KAAA;AACJ,KAAI,cAAc,gBAAgB,WAAW,CAC3C,QAAO,WAAW;CAIpB,MAAM,aAAa,MAAM,uBACpB,iBAAiB,OAAO,QAAQ,GACjC,KAAA;AACJ,KAAI,cAAc,gBAAgB,WAAW,CAC3C,QAAO,WAAW;;;;;AAStB,SAAgB,8BAAmE,EACjF,SAGa;AACb,QAAO,aAAa;EAClB,MAAM,eAAe,MAAM,KAAK;AAChC,MAAI,CAAC,wBAAwB,aAAa,CAAE;EAE5C,MAAM,WAAW,8BAA8B,aAAa;AAC5D,MAAI,aAAa,KAAA,KAAa,CAAC,OAAO,SAAS,SAAS,CAAE;AAE1D,QAAM,IAAI;GACR,GAAG;GACH,cAAc;IACZ,GAAG,aAAa;IAChB;IACD;GACF,CAAM;GACP"}
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { getBandwidthEstimate } from "../abr/bandwidth-estimator.js";
|
|
2
|
-
import { effect } from "../signals/effect.js";
|
|
3
|
-
import { update } from "../signals/primitives.js";
|
|
4
|
-
import { selectQuality } from "../abr/quality-selection.js";
|
|
5
|
-
//#region src/core/features/quality-switching.ts
|
|
6
|
-
/**
|
|
7
|
-
* Default quality switching configuration.
|
|
8
|
-
*/
|
|
9
|
-
const DEFAULT_SWITCHING_CONFIG = {
|
|
10
|
-
safetyMargin: .85,
|
|
11
|
-
minUpgradeInterval: 8e3,
|
|
12
|
-
defaultBandwidth: 5e6
|
|
13
|
-
};
|
|
14
|
-
/**
|
|
15
|
-
* Get all video tracks from a presentation's first switching set.
|
|
16
|
-
* Returns [] when the presentation is still unresolved (no selectionSets yet).
|
|
17
|
-
*/
|
|
18
|
-
function getVideoTracks(presentation) {
|
|
19
|
-
return (presentation.selectionSets?.find((s) => s.type === "video"))?.switchingSets[0]?.tracks ?? [];
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Quality switching orchestration (F9).
|
|
23
|
-
*
|
|
24
|
-
* Reacts to bandwidth estimate changes and updates `selectedVideoTrackId`
|
|
25
|
-
* when a different quality is optimal:
|
|
26
|
-
*
|
|
27
|
-
* - **Downgrades** happen immediately to avoid buffering stalls.
|
|
28
|
-
* - **Upgrades** are gated by `minUpgradeInterval` to prevent oscillation.
|
|
29
|
-
* - The first switch (from any track, or no track) is always immediate.
|
|
30
|
-
*
|
|
31
|
-
* Smooth switching is handled downstream: when `selectedVideoTrackId` changes,
|
|
32
|
-
* `resolveTrack` fetches the new playlist and `loadSegments` reloads the init
|
|
33
|
-
* segment, then appends media segments from the current position in the new
|
|
34
|
-
* quality. The browser's SourceBuffer replaces the overlapping buffered range.
|
|
35
|
-
*
|
|
36
|
-
* @example
|
|
37
|
-
* const cleanup = switchQuality({ state });
|
|
38
|
-
* // Later, when done:
|
|
39
|
-
* cleanup();
|
|
40
|
-
*/
|
|
41
|
-
function switchQuality({ state }, config = {}) {
|
|
42
|
-
const safetyMargin = config.safetyMargin ?? DEFAULT_SWITCHING_CONFIG.safetyMargin;
|
|
43
|
-
const minUpgradeInterval = config.minUpgradeInterval ?? DEFAULT_SWITCHING_CONFIG.minUpgradeInterval;
|
|
44
|
-
const defaultBandwidth = config.defaultBandwidth ?? DEFAULT_SWITCHING_CONFIG.defaultBandwidth;
|
|
45
|
-
let lastUpgradeTime = Date.now();
|
|
46
|
-
let firstMeaningfulFire = true;
|
|
47
|
-
return effect(() => {
|
|
48
|
-
const { presentation, bandwidthState, selectedVideoTrackId, abrDisabled } = state.get();
|
|
49
|
-
if (abrDisabled === true) return;
|
|
50
|
-
if (!presentation || !bandwidthState) return;
|
|
51
|
-
const videoTracks = getVideoTracks(presentation);
|
|
52
|
-
if (videoTracks.length === 0) return;
|
|
53
|
-
const isFirst = firstMeaningfulFire;
|
|
54
|
-
firstMeaningfulFire = false;
|
|
55
|
-
const optimal = selectQuality(videoTracks, getBandwidthEstimate(bandwidthState, defaultBandwidth), { safetyMargin });
|
|
56
|
-
if (!optimal || optimal.id === selectedVideoTrackId) return;
|
|
57
|
-
const currentTrack = videoTracks.find((t) => t.id === selectedVideoTrackId);
|
|
58
|
-
if (!currentTrack || optimal.bandwidth > currentTrack.bandwidth) {
|
|
59
|
-
const now = Date.now();
|
|
60
|
-
if (!isFirst && now - lastUpgradeTime < minUpgradeInterval) return;
|
|
61
|
-
lastUpgradeTime = now;
|
|
62
|
-
}
|
|
63
|
-
update(state, { selectedVideoTrackId: optimal.id });
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
//#endregion
|
|
67
|
-
export { switchQuality };
|
|
68
|
-
|
|
69
|
-
//# sourceMappingURL=quality-switching.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"quality-switching.js","names":[],"sources":["../../../../src/core/features/quality-switching.ts"],"sourcesContent":["import type { BandwidthState } from '../abr/bandwidth-estimator';\nimport { getBandwidthEstimate } from '../abr/bandwidth-estimator';\nimport { selectQuality } from '../abr/quality-selection';\nimport { effect } from '../signals/effect';\nimport { type Signal, update } from '../signals/primitives';\nimport type { Presentation, VideoSelectionSet } from '../types';\n\n/**\n * State shape for quality switching.\n */\nexport interface QualitySwitchingState {\n presentation?: Presentation;\n bandwidthState?: BandwidthState;\n selectedVideoTrackId?: string;\n // TODO: abrDisabled is a blunt instrument — it requires callers to know they're\n // competing with ABR and explicitly opt out. A better long-term design would\n // separate ABR selection (abrVideoTrackId) from manual selection (manualVideoTrackId)\n // and derive the effective selectedVideoTrackId as manualVideoTrackId ?? abrVideoTrackId.\n // That way the two concerns never write to the same field and consumers are unchanged.\n /** When true, ABR quality switching is suppressed. Use for manual quality selection. */\n abrDisabled?: boolean;\n}\n\n/**\n * Configuration for quality switching behavior.\n */\nexport interface QualitySwitchingConfig {\n /**\n * Safety margin for quality selection (0–1).\n * Track is selected only when bandwidth >= track.bandwidth / safetyMargin.\n * Default: 0.85 (15% headroom).\n */\n safetyMargin?: number;\n\n /**\n * Minimum milliseconds between upgrades.\n * Prevents oscillation when bandwidth fluctuates around a quality threshold.\n * Downgrades are always immediate regardless of this setting.\n * Default: 8000 (8 seconds).\n */\n minUpgradeInterval?: number;\n\n /**\n * Bandwidth estimate in bps to use before enough samples have been collected.\n * Default: 5_000_000 (5 Mbps).\n */\n defaultBandwidth?: number;\n}\n\n/**\n * Default quality switching configuration.\n */\nexport const DEFAULT_SWITCHING_CONFIG: Required<QualitySwitchingConfig> = {\n safetyMargin: 0.85,\n minUpgradeInterval: 8000,\n defaultBandwidth: 5_000_000,\n};\n\n/**\n * Get all video tracks from a presentation's first switching set.\n * Returns [] when the presentation is still unresolved (no selectionSets yet).\n */\nfunction getVideoTracks(presentation: Presentation) {\n const videoSet = presentation.selectionSets?.find((s) => s.type === 'video') as VideoSelectionSet | undefined;\n return videoSet?.switchingSets[0]?.tracks ?? [];\n}\n\n/**\n * Quality switching orchestration (F9).\n *\n * Reacts to bandwidth estimate changes and updates `selectedVideoTrackId`\n * when a different quality is optimal:\n *\n * - **Downgrades** happen immediately to avoid buffering stalls.\n * - **Upgrades** are gated by `minUpgradeInterval` to prevent oscillation.\n * - The first switch (from any track, or no track) is always immediate.\n *\n * Smooth switching is handled downstream: when `selectedVideoTrackId` changes,\n * `resolveTrack` fetches the new playlist and `loadSegments` reloads the init\n * segment, then appends media segments from the current position in the new\n * quality. The browser's SourceBuffer replaces the overlapping buffered range.\n *\n * @example\n * const cleanup = switchQuality({ state });\n * // Later, when done:\n * cleanup();\n */\nexport function switchQuality<S extends QualitySwitchingState>(\n { state }: { state: Signal<S> },\n config: QualitySwitchingConfig = {}\n): () => void {\n const safetyMargin = config.safetyMargin ?? DEFAULT_SWITCHING_CONFIG.safetyMargin;\n const minUpgradeInterval = config.minUpgradeInterval ?? DEFAULT_SWITCHING_CONFIG.minUpgradeInterval;\n const defaultBandwidth = config.defaultBandwidth ?? DEFAULT_SWITCHING_CONFIG.defaultBandwidth;\n\n // Initialize to creation time so the interval starts counting immediately.\n // The first time we have enough data to make a meaningful quality decision\n // (presentation resolved + bandwidth available), the upgrade gate is skipped\n // so the initial ABR correction does not wait for minUpgradeInterval.\n let lastUpgradeTime = Date.now();\n let firstMeaningfulFire = true;\n\n return effect(() => {\n const currentState = state.get();\n const { presentation, bandwidthState, selectedVideoTrackId, abrDisabled } = currentState;\n\n if (abrDisabled === true) return;\n if (!presentation || !bandwidthState) return;\n\n const videoTracks = getVideoTracks(presentation);\n if (videoTracks.length === 0) return;\n\n // Consume the first-meaningful-fire flag now that we have all required data.\n const isFirst = firstMeaningfulFire;\n firstMeaningfulFire = false;\n\n const bandwidth = getBandwidthEstimate(bandwidthState, defaultBandwidth);\n const optimal = selectQuality(videoTracks as any, bandwidth, { safetyMargin });\n if (!optimal || optimal.id === selectedVideoTrackId) return;\n\n // Determine whether this is an upgrade or downgrade.\n const currentTrack = videoTracks.find((t) => t.id === selectedVideoTrackId);\n const isUpgrade = !currentTrack || optimal.bandwidth > currentTrack.bandwidth;\n\n if (isUpgrade) {\n const now = Date.now();\n // Gate upgrades with minUpgradeInterval to prevent oscillation.\n // Downgrades are always immediate; the first meaningful evaluation is always allowed.\n if (!isFirst && now - lastUpgradeTime < minUpgradeInterval) return;\n lastUpgradeTime = now;\n }\n\n const patch: Partial<QualitySwitchingState> = { selectedVideoTrackId: optimal.id };\n update(state, patch);\n });\n}\n"],"mappings":";;;;;;;;AAoDA,MAAa,2BAA6D;CACxE,cAAc;CACd,oBAAoB;CACpB,kBAAkB;CACnB;;;;;AAMD,SAAS,eAAe,cAA4B;AAElD,SADiB,aAAa,eAAe,MAAM,MAAM,EAAE,SAAS,QAAQ,GAC3D,cAAc,IAAI,UAAU,EAAE;;;;;;;;;;;;;;;;;;;;;;AAuBjD,SAAgB,cACd,EAAE,SACF,SAAiC,EAAE,EACvB;CACZ,MAAM,eAAe,OAAO,gBAAgB,yBAAyB;CACrE,MAAM,qBAAqB,OAAO,sBAAsB,yBAAyB;CACjF,MAAM,mBAAmB,OAAO,oBAAoB,yBAAyB;CAM7E,IAAI,kBAAkB,KAAK,KAAK;CAChC,IAAI,sBAAsB;AAE1B,QAAO,aAAa;EAElB,MAAM,EAAE,cAAc,gBAAgB,sBAAsB,gBADvC,MAAM,KAAK;AAGhC,MAAI,gBAAgB,KAAM;AAC1B,MAAI,CAAC,gBAAgB,CAAC,eAAgB;EAEtC,MAAM,cAAc,eAAe,aAAa;AAChD,MAAI,YAAY,WAAW,EAAG;EAG9B,MAAM,UAAU;AAChB,wBAAsB;EAGtB,MAAM,UAAU,cAAc,aADZ,qBAAqB,gBAAgB,iBAAiB,EACX,EAAE,cAAc,CAAC;AAC9E,MAAI,CAAC,WAAW,QAAQ,OAAO,qBAAsB;EAGrD,MAAM,eAAe,YAAY,MAAM,MAAM,EAAE,OAAO,qBAAqB;AAG3E,MAFkB,CAAC,gBAAgB,QAAQ,YAAY,aAAa,WAErD;GACb,MAAM,MAAM,KAAK,KAAK;AAGtB,OAAI,CAAC,WAAW,MAAM,kBAAkB,mBAAoB;AAC5D,qBAAkB;;AAIpB,SAAO,OADuC,EAAE,sBAAsB,QAAQ,IAAI,CAC9D;GACpB"}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { computed, update } from "../signals/primitives.js";
|
|
2
|
-
import { fetchResolvable, getResponseText } from "../../dom/network/fetch.js";
|
|
3
|
-
import { createMachineReactor } from "../create-machine-reactor.js";
|
|
4
|
-
import { parseMultivariantPlaylist } from "../hls/parse-multivariant.js";
|
|
5
|
-
//#region src/core/features/resolve-presentation.ts
|
|
6
|
-
/**
|
|
7
|
-
* Determines if resolution conditions are met based on preload policy and playback state.
|
|
8
|
-
*
|
|
9
|
-
* Resolution conditions:
|
|
10
|
-
* - State-driven: preload is 'auto' or 'metadata'
|
|
11
|
-
* - Playback-driven: playbackInitiated is true
|
|
12
|
-
*
|
|
13
|
-
* @param state - Current presentation state
|
|
14
|
-
* @returns true if resolution conditions are met
|
|
15
|
-
*/
|
|
16
|
-
function shouldResolve(state) {
|
|
17
|
-
const { preload, playbackInitiated } = state;
|
|
18
|
-
return ["auto", "metadata"].includes(preload) || !!playbackInitiated;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Derives the correct state from current state conditions.
|
|
22
|
-
*
|
|
23
|
-
* States are mutually exclusive and exhaustive:
|
|
24
|
-
* - `'preconditions-unmet'`: no presentation, or presentation has no URL
|
|
25
|
-
* - `'idle'`: URL present, unresolved (no id), shouldResolve not met
|
|
26
|
-
* - `'resolving'`: URL present, unresolved (no id), shouldResolve met
|
|
27
|
-
* - `'resolved'`: URL present, resolved (has id)
|
|
28
|
-
*/
|
|
29
|
-
function deriveState(state) {
|
|
30
|
-
const { presentation } = state;
|
|
31
|
-
if (!presentation || !("url" in presentation)) return "preconditions-unmet";
|
|
32
|
-
if ("id" in presentation) return "resolved";
|
|
33
|
-
return shouldResolve(state) ? "resolving" : "idle";
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Resolves unresolved presentations using reactive composition.
|
|
37
|
-
*
|
|
38
|
-
* FSM driven by `deriveState` — a single `always` monitor keeps the state in
|
|
39
|
-
* sync with conditions at all times. `'resolving'` additionally runs the fetch
|
|
40
|
-
* task and returns an AbortController so the framework aborts it on state exit.
|
|
41
|
-
*
|
|
42
|
-
* @example
|
|
43
|
-
* const reactor = resolvePresentation({ state });
|
|
44
|
-
* // later:
|
|
45
|
-
* reactor.destroy();
|
|
46
|
-
*/
|
|
47
|
-
function resolvePresentation({ state }) {
|
|
48
|
-
const derivedStateSignal = computed(() => deriveState(state.get()));
|
|
49
|
-
return createMachineReactor({
|
|
50
|
-
initial: "preconditions-unmet",
|
|
51
|
-
monitor: () => derivedStateSignal.get(),
|
|
52
|
-
states: {
|
|
53
|
-
"preconditions-unmet": {},
|
|
54
|
-
idle: {},
|
|
55
|
-
resolving: { entry: () => {
|
|
56
|
-
const presentation = state.get().presentation;
|
|
57
|
-
const ac = new AbortController();
|
|
58
|
-
fetchResolvable(presentation, { signal: ac.signal }).then((response) => getResponseText(response)).then((text) => {
|
|
59
|
-
update(state, { presentation: parseMultivariantPlaylist(text, presentation) });
|
|
60
|
-
}).catch((error) => {
|
|
61
|
-
if (error instanceof Error && error.name === "AbortError") return;
|
|
62
|
-
throw error;
|
|
63
|
-
});
|
|
64
|
-
return ac;
|
|
65
|
-
} },
|
|
66
|
-
resolved: {}
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
//#endregion
|
|
71
|
-
export { resolvePresentation };
|
|
72
|
-
|
|
73
|
-
//# sourceMappingURL=resolve-presentation.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"resolve-presentation.js","names":[],"sources":["../../../../src/core/features/resolve-presentation.ts"],"sourcesContent":["import { fetchResolvable, getResponseText } from '../../dom/network/fetch';\nimport type { Reactor } from '../create-machine-reactor';\nimport { createMachineReactor } from '../create-machine-reactor';\nimport { parseMultivariantPlaylist } from '../hls/parse-multivariant';\nimport { computed, type Signal, update } from '../signals/primitives';\nimport type { AddressableObject, Presentation } from '../types';\n\n/**\n * Unresolved presentation - has a URL but no data yet.\n * Identical to AddressableObject per user requirement.\n */\nexport type UnresolvedPresentation = AddressableObject;\n\n/**\n * State shape for presentation resolution.\n */\nexport interface PresentationState {\n presentation?: UnresolvedPresentation | Presentation | undefined;\n preload?: 'auto' | 'metadata' | 'none' | undefined;\n /** True once the user has initiated playback — enables resolution regardless of preload. */\n playbackInitiated?: boolean;\n}\n\n/**\n * Type guard to check if presentation is unresolved.\n */\nexport function isUnresolved(\n presentation: UnresolvedPresentation | Presentation | undefined\n): presentation is UnresolvedPresentation {\n return presentation !== undefined && 'url' in presentation && !('id' in presentation);\n}\n\nexport function canResolve(\n state: PresentationState\n): state is PresentationState & { presentation: UnresolvedPresentation } {\n return isUnresolved(state.presentation);\n}\n\n/**\n * Determines if resolution conditions are met based on preload policy and playback state.\n *\n * Resolution conditions:\n * - State-driven: preload is 'auto' or 'metadata'\n * - Playback-driven: playbackInitiated is true\n *\n * @param state - Current presentation state\n * @returns true if resolution conditions are met\n */\nexport function shouldResolve(state: PresentationState): boolean {\n const { preload, playbackInitiated } = state;\n return (\n // State-driven: preload allows (auto/metadata)\n ['auto', 'metadata'].includes(preload as any) ||\n // Playback-driven: user has initiated playback\n !!playbackInitiated\n );\n}\n\nexport type ResolvePresentationState = 'preconditions-unmet' | 'idle' | 'resolving' | 'resolved';\n\n/**\n * Derives the correct state from current state conditions.\n *\n * States are mutually exclusive and exhaustive:\n * - `'preconditions-unmet'`: no presentation, or presentation has no URL\n * - `'idle'`: URL present, unresolved (no id), shouldResolve not met\n * - `'resolving'`: URL present, unresolved (no id), shouldResolve met\n * - `'resolved'`: URL present, resolved (has id)\n */\nfunction deriveState(state: PresentationState): ResolvePresentationState {\n const { presentation } = state;\n if (!presentation || !('url' in presentation)) return 'preconditions-unmet';\n if ('id' in presentation) return 'resolved';\n return shouldResolve(state) ? 'resolving' : 'idle';\n}\n\n/**\n * Resolves unresolved presentations using reactive composition.\n *\n * FSM driven by `deriveState` — a single `always` monitor keeps the state in\n * sync with conditions at all times. `'resolving'` additionally runs the fetch\n * task and returns an AbortController so the framework aborts it on state exit.\n *\n * @example\n * const reactor = resolvePresentation({ state });\n * // later:\n * reactor.destroy();\n */\nexport function resolvePresentation<S extends PresentationState>({\n state,\n}: {\n state: Signal<S>;\n}): Reactor<ResolvePresentationState | 'destroying' | 'destroyed'> {\n const derivedStateSignal = computed(() => deriveState(state.get()));\n\n return createMachineReactor<ResolvePresentationState>({\n initial: 'preconditions-unmet',\n monitor: () => derivedStateSignal.get(),\n states: {\n 'preconditions-unmet': {},\n idle: {},\n resolving: {\n // Entry: start fetch on state entry; return AbortController so the\n // framework aborts the in-flight request on state exit.\n entry: () => {\n const presentation = state.get().presentation as UnresolvedPresentation;\n const ac = new AbortController();\n\n fetchResolvable(presentation, { signal: ac.signal })\n .then((response) => getResponseText(response))\n .then((text) => {\n const parsed = parseMultivariantPlaylist(text, presentation);\n update(state, { presentation: parsed } as Partial<S>);\n })\n .catch((error) => {\n if (error instanceof Error && error.name === 'AbortError') return;\n throw error;\n });\n\n return ac;\n },\n },\n resolved: {},\n },\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;AAgDA,SAAgB,cAAc,OAAmC;CAC/D,MAAM,EAAE,SAAS,sBAAsB;AACvC,QAEE,CAAC,QAAQ,WAAW,CAAC,SAAS,QAAe,IAE7C,CAAC,CAAC;;;;;;;;;;;AAeN,SAAS,YAAY,OAAoD;CACvE,MAAM,EAAE,iBAAiB;AACzB,KAAI,CAAC,gBAAgB,EAAE,SAAS,cAAe,QAAO;AACtD,KAAI,QAAQ,aAAc,QAAO;AACjC,QAAO,cAAc,MAAM,GAAG,cAAc;;;;;;;;;;;;;;AAe9C,SAAgB,oBAAiD,EAC/D,SAGiE;CACjE,MAAM,qBAAqB,eAAe,YAAY,MAAM,KAAK,CAAC,CAAC;AAEnE,QAAO,qBAA+C;EACpD,SAAS;EACT,eAAe,mBAAmB,KAAK;EACvC,QAAQ;GACN,uBAAuB,EAAE;GACzB,MAAM,EAAE;GACR,WAAW,EAGT,aAAa;IACX,MAAM,eAAe,MAAM,KAAK,CAAC;IACjC,MAAM,KAAK,IAAI,iBAAiB;AAEhC,oBAAgB,cAAc,EAAE,QAAQ,GAAG,QAAQ,CAAC,CACjD,MAAM,aAAa,gBAAgB,SAAS,CAAC,CAC7C,MAAM,SAAS;AAEd,YAAO,OAAO,EAAE,cADD,0BAA0B,MAAM,aAAa,EACtB,CAAe;MACrD,CACD,OAAO,UAAU;AAChB,SAAI,iBAAiB,SAAS,MAAM,SAAS,aAAc;AAC3D,WAAM;MACN;AAEJ,WAAO;MAEV;GACD,UAAU,EAAE;GACb;EACF,CAAC"}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { effect } from "../signals/effect.js";
|
|
2
|
-
import { isResolvedTrack } from "../types/index.js";
|
|
3
|
-
import { getSelectedTrack } from "../utils/track-selection.js";
|
|
4
|
-
import { fetchResolvable, getResponseText } from "../../dom/network/fetch.js";
|
|
5
|
-
import { ConcurrentRunner, Task } from "../task.js";
|
|
6
|
-
import { parseMediaPlaylist } from "../hls/parse-media-playlist.js";
|
|
7
|
-
//#region src/core/features/resolve-track.ts
|
|
8
|
-
function canResolve(state, config) {
|
|
9
|
-
const track = getSelectedTrack(state, config.type);
|
|
10
|
-
if (!track) return false;
|
|
11
|
-
return !isResolvedTrack(track);
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Updates a track within a presentation (immutably).
|
|
15
|
-
* Generic - works for video, audio, or text tracks.
|
|
16
|
-
*/
|
|
17
|
-
function updateTrackInPresentation(presentation, resolvedTrack) {
|
|
18
|
-
const trackId = resolvedTrack.id;
|
|
19
|
-
return {
|
|
20
|
-
...presentation,
|
|
21
|
-
selectionSets: presentation.selectionSets.map((selectionSet) => ({
|
|
22
|
-
...selectionSet,
|
|
23
|
-
switchingSets: selectionSet.switchingSets.map((switchingSet) => ({
|
|
24
|
-
...switchingSet,
|
|
25
|
-
tracks: switchingSet.tracks.map((track) => track.id === trackId ? resolvedTrack : track)
|
|
26
|
-
}))
|
|
27
|
-
}))
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Resolves unresolved tracks using reactive composition.
|
|
32
|
-
*
|
|
33
|
-
* Reacts to state changes and schedules fetch tasks via ConcurrentRunner when
|
|
34
|
-
* a selected track is unresolved. The ConcurrentRunner handles deduplication,
|
|
35
|
-
* parallel execution, and cleanup.
|
|
36
|
-
*
|
|
37
|
-
* Generic version that works for video, audio, or text tracks based on config.
|
|
38
|
-
* Type parameter T is inferred from config.type (use 'as const' for inference).
|
|
39
|
-
*/
|
|
40
|
-
function resolveTrack({ state }, config) {
|
|
41
|
-
const runner = new ConcurrentRunner();
|
|
42
|
-
const cleanup = effect(() => {
|
|
43
|
-
const currentState = state.get();
|
|
44
|
-
if (!canResolve(currentState, config)) return;
|
|
45
|
-
const track = getSelectedTrack(currentState, config.type);
|
|
46
|
-
if (!track) return;
|
|
47
|
-
const resolvedTrack = track;
|
|
48
|
-
runner.schedule(new Task(async (signal) => {
|
|
49
|
-
const mediaTrack = parseMediaPlaylist(await getResponseText(await fetchResolvable(resolvedTrack, { signal })), resolvedTrack);
|
|
50
|
-
const latest = state.get();
|
|
51
|
-
const updatedPresentation = updateTrackInPresentation(latest.presentation, mediaTrack);
|
|
52
|
-
state.set({
|
|
53
|
-
...latest,
|
|
54
|
-
presentation: updatedPresentation
|
|
55
|
-
});
|
|
56
|
-
}, { id: track.id }));
|
|
57
|
-
});
|
|
58
|
-
return () => {
|
|
59
|
-
runner.abortAll();
|
|
60
|
-
cleanup();
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
//#endregion
|
|
64
|
-
export { resolveTrack };
|
|
65
|
-
|
|
66
|
-
//# sourceMappingURL=resolve-track.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"resolve-track.js","names":[],"sources":["../../../../src/core/features/resolve-track.ts"],"sourcesContent":["import { fetchResolvable, getResponseText } from '../../dom/network/fetch';\nimport { parseMediaPlaylist } from '../hls/parse-media-playlist';\nimport { effect } from '../signals/effect';\nimport type { Signal } from '../signals/primitives';\nimport { ConcurrentRunner, Task } from '../task';\nimport type { Presentation, ResolvedTrack, TrackType } from '../types';\nimport { isResolvedTrack } from '../types';\nimport { getSelectedTrack, type TrackSelectionState } from '../utils/track-selection';\n\n/**\n * State shape for track resolution.\n */\nexport interface TrackResolutionState extends TrackSelectionState {\n presentation?: Presentation | undefined;\n selectedVideoTrackId?: string | undefined;\n selectedAudioTrackId?: string | undefined;\n selectedTextTrackId?: string | undefined;\n}\n\nexport function canResolve<T extends TrackType>(\n state: TrackResolutionState,\n config: TrackResolutionConfig<T>\n): boolean {\n const track = getSelectedTrack(state, config.type);\n if (!track) return false;\n\n return !isResolvedTrack(track);\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\n/**\n * Updates a track within a presentation (immutably).\n * Generic - works for video, audio, or text tracks.\n */\nexport function updateTrackInPresentation<T extends ResolvedTrack>(\n presentation: Presentation,\n resolvedTrack: T\n): Presentation {\n const trackId = resolvedTrack.id;\n return {\n ...presentation,\n selectionSets: presentation.selectionSets.map((selectionSet) => ({\n ...selectionSet,\n switchingSets: selectionSet.switchingSets.map((switchingSet) => ({\n ...switchingSet,\n tracks: switchingSet.tracks.map((track) => (track.id === trackId ? resolvedTrack : track)),\n })),\n })),\n } as Presentation;\n}\n\n/**\n * Configuration for track resolution.\n */\nexport interface TrackResolutionConfig<T extends TrackType = TrackType> {\n type: T;\n}\n\n/**\n * Resolves unresolved tracks using reactive composition.\n *\n * Reacts to state changes and schedules fetch tasks via ConcurrentRunner when\n * a selected track is unresolved. The ConcurrentRunner handles deduplication,\n * parallel execution, and cleanup.\n *\n * Generic version that works for video, audio, or text tracks based on config.\n * Type parameter T is inferred from config.type (use 'as const' for inference).\n */\nexport function resolveTrack<S extends TrackResolutionState, T extends TrackType>(\n { state }: { state: Signal<S> },\n config: TrackResolutionConfig<T>\n): () => void {\n // NOTE: This can/maybe will be pulled into a per-use case factory (e.g. something like createTaskRunner() with args TBD),\n // likely eventually passed down via config or a new \"definitions\" argument. This will allow us to decide if we want our task runner/scheduler\n // to e.g. run concurrently (like we currently are), serially with a queue, or abort the previous task and replace it with the newly scheduled one. (CJP).\n const runner = new ConcurrentRunner();\n\n const cleanup = effect(() => {\n const currentState = state.get();\n if (!canResolve(currentState, config)) return;\n\n const track = getSelectedTrack(currentState, config.type);\n if (!track) return;\n\n const resolvedTrack = track;\n\n runner.schedule(\n // NOTE: This can/maybe will be pulled into a per-use case factory (e.g. something like createResolveTrackTask(track, context, config)),\n // likely eventually passed down via config or a new \"definitions\" argument (CJP).\n new Task(\n async (signal) => {\n const response = await fetchResolvable(resolvedTrack, { signal });\n const text = await getResponseText(response);\n const mediaTrack = parseMediaPlaylist(text, resolvedTrack);\n\n // IMPORTANT: Read state.get().presentation at write time, not from the\n // captured currentState snapshot. Multiple Tasks may be running concurrently\n // (one per track being resolved), so the snapshot is likely already stale by\n // the time a task completes — a sibling task may have already written the\n // presentation with its own resolved track. Reading live state ensures each\n // task builds on top of whatever has been committed so far.\n const latest = state.get();\n const updatedPresentation = updateTrackInPresentation(latest.presentation!, mediaTrack);\n state.set({ ...latest, presentation: updatedPresentation } as S);\n },\n { id: track.id }\n )\n );\n });\n\n return () => {\n runner.abortAll();\n cleanup();\n };\n}\n"],"mappings":";;;;;;;AAmBA,SAAgB,WACd,OACA,QACS;CACT,MAAM,QAAQ,iBAAiB,OAAO,OAAO,KAAK;AAClD,KAAI,CAAC,MAAO,QAAO;AAEnB,QAAO,CAAC,gBAAgB,MAAM;;;;;;AAWhC,SAAgB,0BACd,cACA,eACc;CACd,MAAM,UAAU,cAAc;AAC9B,QAAO;EACL,GAAG;EACH,eAAe,aAAa,cAAc,KAAK,kBAAkB;GAC/D,GAAG;GACH,eAAe,aAAa,cAAc,KAAK,kBAAkB;IAC/D,GAAG;IACH,QAAQ,aAAa,OAAO,KAAK,UAAW,MAAM,OAAO,UAAU,gBAAgB,MAAO;IAC3F,EAAE;GACJ,EAAE;EACJ;;;;;;;;;;;;AAoBH,SAAgB,aACd,EAAE,SACF,QACY;CAIZ,MAAM,SAAS,IAAI,kBAAkB;CAErC,MAAM,UAAU,aAAa;EAC3B,MAAM,eAAe,MAAM,KAAK;AAChC,MAAI,CAAC,WAAW,cAAc,OAAO,CAAE;EAEvC,MAAM,QAAQ,iBAAiB,cAAc,OAAO,KAAK;AACzD,MAAI,CAAC,MAAO;EAEZ,MAAM,gBAAgB;AAEtB,SAAO,SAGL,IAAI,KACF,OAAO,WAAW;GAGhB,MAAM,aAAa,mBADN,MAAM,gBADF,MAAM,gBAAgB,eAAe,EAAE,QAAQ,CAAC,CACrB,EACA,cAAc;GAQ1D,MAAM,SAAS,MAAM,KAAK;GAC1B,MAAM,sBAAsB,0BAA0B,OAAO,cAAe,WAAW;AACvF,SAAM,IAAI;IAAE,GAAG;IAAQ,cAAc;IAAqB,CAAM;KAElE,EAAE,IAAI,MAAM,IAAI,CACjB,CACF;GACD;AAEF,cAAa;AACX,SAAO,UAAU;AACjB,WAAS"}
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
import { effect } from "../signals/effect.js";
|
|
2
|
-
import { update } from "../signals/primitives.js";
|
|
3
|
-
import { SelectedTrackIdKeyByType } from "../utils/track-selection.js";
|
|
4
|
-
//#region src/core/features/select-tracks.ts
|
|
5
|
-
/**
|
|
6
|
-
* Pick text track to activate.
|
|
7
|
-
*
|
|
8
|
-
* Selection priority (if enabled):
|
|
9
|
-
* 1. User preference (preferredSubtitleLanguage)
|
|
10
|
-
* 2. DEFAULT track (if enableDefaultTrack is true and track has DEFAULT=YES + AUTOSELECT=YES)
|
|
11
|
-
* 3. No auto-selection (user opt-in)
|
|
12
|
-
*
|
|
13
|
-
* By default, FORCED tracks are excluded per Apple's HLS spec.
|
|
14
|
-
*
|
|
15
|
-
* @param presentation - Presentation with text tracks
|
|
16
|
-
* @param config - Selection configuration
|
|
17
|
-
* @returns Track ID or undefined (no auto-selection)
|
|
18
|
-
*/
|
|
19
|
-
function pickTextTrack(presentation, config) {
|
|
20
|
-
const textSet = presentation.selectionSets.find((set) => set.type === "text");
|
|
21
|
-
if (!textSet?.switchingSets?.[0]?.tracks.length) return void 0;
|
|
22
|
-
const tracks = textSet.switchingSets[0].tracks;
|
|
23
|
-
const availableTracks = config.includeForcedTracks ? tracks : tracks.filter((track) => !track.forced);
|
|
24
|
-
if (availableTracks.length === 0) return void 0;
|
|
25
|
-
const { preferredSubtitleLanguage, enableDefaultTrack = false } = config;
|
|
26
|
-
if (preferredSubtitleLanguage) {
|
|
27
|
-
const languageMatch = availableTracks.find((track) => track.language === preferredSubtitleLanguage);
|
|
28
|
-
if (languageMatch) return languageMatch.id;
|
|
29
|
-
}
|
|
30
|
-
if (enableDefaultTrack) {
|
|
31
|
-
const defaultTrack = availableTracks.find((track) => track.default === true);
|
|
32
|
-
if (defaultTrack) return defaultTrack.id;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Check if we can select a track of the given type.
|
|
37
|
-
*
|
|
38
|
-
* Returns true when:
|
|
39
|
-
* - Presentation exists
|
|
40
|
-
* - Has tracks of the specified type
|
|
41
|
-
*
|
|
42
|
-
* Generic over track type - works for video, audio, or text.
|
|
43
|
-
*/
|
|
44
|
-
function canSelectTrack(state, config) {
|
|
45
|
-
return !!state?.presentation?.selectionSets?.find(({ type }) => type === config.type)?.switchingSets?.[0]?.tracks.length;
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Check if we should select a track of the given type.
|
|
49
|
-
*
|
|
50
|
-
* Returns true when:
|
|
51
|
-
* - Track of this type is not already selected
|
|
52
|
-
*
|
|
53
|
-
* Generic over track type - works for video, audio, or text.
|
|
54
|
-
*
|
|
55
|
-
* @TODO figure out reactive model for ABR cases - right now we're only selecting
|
|
56
|
-
* if we have nothing selected (CJP)
|
|
57
|
-
*/
|
|
58
|
-
function shouldSelectTrack(state, config) {
|
|
59
|
-
return !state[SelectedTrackIdKeyByType[config.type]];
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* Select video track orchestration.
|
|
63
|
-
*
|
|
64
|
-
* Selects video track when:
|
|
65
|
-
* - Presentation exists
|
|
66
|
-
* - No video track is selected yet
|
|
67
|
-
*
|
|
68
|
-
* Uses bandwidth-based quality selection algorithm.
|
|
69
|
-
*
|
|
70
|
-
* @example
|
|
71
|
-
* const cleanup = selectVideoTrack(
|
|
72
|
-
* { state, owners, events },
|
|
73
|
-
* { initialBandwidth: 2_000_000 }
|
|
74
|
-
* );
|
|
75
|
-
*/
|
|
76
|
-
function selectVideoTrack({ state }, config = { type: "video" }) {
|
|
77
|
-
return effect(() => {
|
|
78
|
-
const currentState = state.get();
|
|
79
|
-
if (!canSelectTrack(currentState, config) || !shouldSelectTrack(currentState, config)) return;
|
|
80
|
-
const selectedTrackId = currentState.presentation?.selectionSets.find(({ type }) => type === config.type)?.switchingSets[0]?.tracks[0]?.id;
|
|
81
|
-
if (selectedTrackId) update(state, { [SelectedTrackIdKeyByType[config.type]]: selectedTrackId });
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* Select audio track orchestration.
|
|
86
|
-
*
|
|
87
|
-
* Selects audio track when:
|
|
88
|
-
* - Presentation exists
|
|
89
|
-
* - No audio track is selected yet
|
|
90
|
-
*
|
|
91
|
-
* Uses language and preference-based selection.
|
|
92
|
-
*
|
|
93
|
-
* @example
|
|
94
|
-
* const cleanup = selectAudioTrack(
|
|
95
|
-
* { state, owners, events },
|
|
96
|
-
* { preferredAudioLanguage: 'en' }
|
|
97
|
-
* );
|
|
98
|
-
*/
|
|
99
|
-
function selectAudioTrack({ state }, config = { type: "audio" }) {
|
|
100
|
-
return effect(() => {
|
|
101
|
-
const currentState = state.get();
|
|
102
|
-
if (!canSelectTrack(currentState, config) || !shouldSelectTrack(currentState, config)) return;
|
|
103
|
-
const selectedTrackId = currentState.presentation?.selectionSets.find(({ type }) => type === "audio")?.switchingSets[0]?.tracks[0]?.id;
|
|
104
|
-
if (selectedTrackId) update(state, { selectedAudioTrackId: selectedTrackId });
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Select text track orchestration.
|
|
109
|
-
*
|
|
110
|
-
* Selects text track when:
|
|
111
|
-
* - Presentation exists
|
|
112
|
-
* - No text track is selected yet
|
|
113
|
-
*
|
|
114
|
-
* Note: Currently does not auto-select (user opt-in).
|
|
115
|
-
*
|
|
116
|
-
* @example
|
|
117
|
-
* const cleanup = selectTextTrack({ state, owners, events }, {});
|
|
118
|
-
*/
|
|
119
|
-
function selectTextTrack({ state }, config = { type: "text" }) {
|
|
120
|
-
return effect(() => {
|
|
121
|
-
const currentState = state.get();
|
|
122
|
-
if (!canSelectTrack(currentState, config) || !shouldSelectTrack(currentState, config)) return;
|
|
123
|
-
const selectedTextTrackId = pickTextTrack(currentState.presentation, config);
|
|
124
|
-
if (selectedTextTrackId) update(state, { selectedTextTrackId });
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
//#endregion
|
|
128
|
-
export { selectAudioTrack, selectTextTrack, selectVideoTrack };
|
|
129
|
-
|
|
130
|
-
//# sourceMappingURL=select-tracks.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"select-tracks.js","names":[],"sources":["../../../../src/core/features/select-tracks.ts"],"sourcesContent":["import { DEFAULT_QUALITY_CONFIG, selectQuality } from '../abr/quality-selection';\nimport { effect } from '../signals/effect';\nimport { type Signal, update } from '../signals/primitives';\nimport type { AudioSelectionSet, Presentation, TrackType, VideoSelectionSet } from '../types';\nimport { SelectedTrackIdKeyByType } from '../utils/track-selection';\n\n/**\n * Default initial bandwidth estimate for cold start (bits per second).\n * Conservative 1 Mbps to avoid over-selecting on slow connections.\n */\nexport const DEFAULT_INITIAL_BANDWIDTH = 1_000_000;\n\n/**\n * State shape for track selection.\n */\nexport interface TrackSelectionState {\n presentation?: Presentation;\n selectedVideoTrackId?: string;\n selectedAudioTrackId?: string;\n selectedTextTrackId?: string;\n}\n\n/**\n * Owners shape for track selection.\n * Currently empty - reserved for future use (e.g., bandwidth estimator).\n */\nexport type TrackSelectionOwners = Record<string, never>;\n\n/**\n * Action types for track selection.\n * Reserved for future event-driven selection triggers.\n */\nexport type TrackSelectionAction = { type: 'presentation-loaded' };\n\n/**\n * Base configuration for track selection.\n * Generic over track type with discriminant `type` field.\n */\nexport interface TrackSelectionConfig<T extends TrackType = TrackType> {\n type: T;\n}\n\n/**\n * Configuration for video track selection.\n * Generic with default to 'video' for convenience.\n */\nexport interface VideoSelectionConfig<T extends TrackType = 'video'> extends TrackSelectionConfig<T> {\n /**\n * Initial bandwidth estimate for cold start (bits per second).\n * Used to select video quality before we have real measurements.\n * Default: 1 Mbps (conservative).\n */\n initialBandwidth?: number;\n\n /**\n * Safety margin for quality selection (0-1).\n * Default: 0.85 (15% headroom).\n */\n safetyMargin?: number;\n}\n\n/**\n * Configuration for audio track selection.\n * Generic with default to 'audio' for convenience.\n */\nexport interface AudioSelectionConfig<T extends TrackType = 'audio'> extends TrackSelectionConfig<T> {\n /**\n * Preferred audio language (ISO 639 code, e.g., \"en\", \"es\").\n * If not specified, selects first audio track.\n */\n preferredAudioLanguage?: string;\n}\n\n/**\n * Configuration for text track selection.\n * Generic with default to 'text' for convenience.\n */\nexport interface TextSelectionConfig<T extends TrackType = 'text'> extends TrackSelectionConfig<T> {\n /**\n * Preferred subtitle language (ISO 639 code, e.g., \"en\", \"es\").\n * If specified, selects matching track if available.\n */\n preferredSubtitleLanguage?: string;\n\n /**\n * Include FORCED subtitle tracks in selection.\n * Default: false (follows hls.js/http-streaming pattern)\n *\n * Note: Per Apple's HLS spec, if content has forced and regular subtitles\n * in the same language, the regular track MUST contain both forced and\n * regular content. Therefore, forced-only tracks are redundant and excluded\n * by default.\n */\n includeForcedTracks?: boolean;\n\n /**\n * Auto-select DEFAULT track (requires DEFAULT=YES + AUTOSELECT=YES in HLS).\n * Default: false (user opt-in, matches hls.js/http-streaming)\n *\n * When enabled, tracks marked with both DEFAULT=YES and AUTOSELECT=YES\n * will be automatically selected if no user preference matches.\n */\n enableDefaultTrack?: boolean;\n}\n\n// =============================================================================\n// Helper Functions (Pure Selection Logic)\n// =============================================================================\n\n/**\n * Pick video track using quality selection algorithm.\n *\n * Uses bandwidth-based selection with safety margin to pick\n * the highest quality track that fits available bandwidth.\n *\n * @param presentation - Presentation with video tracks\n * @param config - Selection configuration (bandwidth, safety margin)\n * @returns Selected video track ID, or undefined if no video tracks\n */\nexport function pickVideoTrack(presentation: Presentation, config: VideoSelectionConfig): string | undefined {\n const videoSet = presentation.selectionSets.find((set) => set.type === 'video') as VideoSelectionSet | undefined;\n\n if (!videoSet || videoSet.switchingSets.length === 0) {\n return undefined;\n }\n\n // Get first switching set's tracks (HLS typically has one switching set per type)\n const switchingSet = videoSet.switchingSets[0];\n if (!switchingSet || switchingSet.tracks.length === 0) {\n return undefined;\n }\n\n const initialBandwidth = config.initialBandwidth ?? DEFAULT_INITIAL_BANDWIDTH;\n const safetyMargin = config.safetyMargin ?? DEFAULT_QUALITY_CONFIG.safetyMargin;\n\n // selectQuality works with both partially resolved and resolved tracks\n const selected = selectQuality(switchingSet.tracks as any, initialBandwidth, { safetyMargin });\n\n return selected?.id;\n}\n\n/**\n * Pick audio track.\n *\n * Selection priority:\n * 1. First track matching preferred language (if specified)\n * 2. First default track\n * 3. First audio track\n *\n * @param presentation - Presentation with audio tracks\n * @param config - Selection configuration (preferred language)\n * @returns Selected audio track ID, or undefined if no audio tracks\n */\nexport function pickAudioTrack(presentation: Presentation, config: AudioSelectionConfig): string | undefined {\n const audioSet = presentation.selectionSets.find((set) => set.type === 'audio') as AudioSelectionSet | undefined;\n\n if (!audioSet || audioSet.switchingSets.length === 0) {\n return undefined;\n }\n\n // Get first switching set's tracks\n const switchingSet = audioSet.switchingSets[0];\n if (!switchingSet || switchingSet.tracks.length === 0) {\n return undefined;\n }\n\n const tracks = switchingSet.tracks;\n\n // Try preferred language first\n if (config.preferredAudioLanguage) {\n const languageMatch = tracks.find((track) => track.language === config.preferredAudioLanguage);\n if (languageMatch) {\n return languageMatch.id;\n }\n }\n\n // Try default track\n const defaultTrack = tracks.find((track) => track.default === true);\n if (defaultTrack) {\n return defaultTrack.id;\n }\n\n // Fall back to first track\n return tracks[0]?.id;\n}\n\n/**\n * Pick text track to activate.\n *\n * Selection priority (if enabled):\n * 1. User preference (preferredSubtitleLanguage)\n * 2. DEFAULT track (if enableDefaultTrack is true and track has DEFAULT=YES + AUTOSELECT=YES)\n * 3. No auto-selection (user opt-in)\n *\n * By default, FORCED tracks are excluded per Apple's HLS spec.\n *\n * @param presentation - Presentation with text tracks\n * @param config - Selection configuration\n * @returns Track ID or undefined (no auto-selection)\n */\nexport function pickTextTrack(presentation: Presentation, config: TextSelectionConfig): string | undefined {\n const textSet = presentation.selectionSets.find((set) => set.type === 'text');\n if (!textSet?.switchingSets?.[0]?.tracks.length) return undefined;\n\n const tracks = textSet.switchingSets[0].tracks;\n\n // Filter out FORCED tracks by default (following hls.js/http-streaming pattern)\n // Per Apple spec: regular tracks MUST contain forced content when both exist\n const availableTracks = config.includeForcedTracks ? tracks : tracks.filter((track) => !track.forced);\n\n if (availableTracks.length === 0) return undefined;\n\n const { preferredSubtitleLanguage, enableDefaultTrack = false } = config;\n\n // 1. Preferred language match (if specified)\n if (preferredSubtitleLanguage) {\n const languageMatch = availableTracks.find((track) => track.language === preferredSubtitleLanguage);\n if (languageMatch) return languageMatch.id;\n }\n\n // 2. DEFAULT track (if enabled AND track has both DEFAULT=YES + AUTOSELECT=YES)\n // Note: Parser only sets default=true when BOTH attributes present\n if (enableDefaultTrack) {\n const defaultTrack = availableTracks.find((track) => track.default === true);\n if (defaultTrack) return defaultTrack.id;\n }\n\n // 3. User opt-in (no auto-selection)\n return undefined;\n}\n\n/**\n * Check if we can select a track of the given type.\n *\n * Returns true when:\n * - Presentation exists\n * - Has tracks of the specified type\n *\n * Generic over track type - works for video, audio, or text.\n */\nexport function canSelectTrack<T extends TrackType>(\n state: TrackSelectionState,\n config: TrackSelectionConfig<T>\n): boolean {\n return !!state?.presentation?.selectionSets?.find(({ type }) => type === config.type)?.switchingSets?.[0]?.tracks\n .length;\n}\n\n/**\n * Check if we should select a track of the given type.\n *\n * Returns true when:\n * - Track of this type is not already selected\n *\n * Generic over track type - works for video, audio, or text.\n *\n * @TODO figure out reactive model for ABR cases - right now we're only selecting\n * if we have nothing selected (CJP)\n */\nexport function shouldSelectTrack<T extends TrackType>(\n state: TrackSelectionState,\n config: TrackSelectionConfig<T>\n): boolean {\n return !state[SelectedTrackIdKeyByType[config.type]];\n}\n\n// =============================================================================\n// Orchestrations\n// =============================================================================\n\n/**\n * Select video track orchestration.\n *\n * Selects video track when:\n * - Presentation exists\n * - No video track is selected yet\n *\n * Uses bandwidth-based quality selection algorithm.\n *\n * @example\n * const cleanup = selectVideoTrack(\n * { state, owners, events },\n * { initialBandwidth: 2_000_000 }\n * );\n */\nexport function selectVideoTrack<S extends TrackSelectionState>(\n { state }: { state: Signal<S> },\n config: VideoSelectionConfig = { type: 'video' }\n): () => void {\n return effect(() => {\n const currentState = state.get();\n if (!canSelectTrack(currentState, config) || !shouldSelectTrack(currentState, config)) return;\n\n // Just to have basic functionality/POC, simply selecting the first track\n const selectedTrackId = currentState.presentation?.selectionSets.find(({ type }) => type === config.type)\n ?.switchingSets[0]?.tracks[0]?.id;\n\n if (selectedTrackId) {\n const selectedTrackKey = SelectedTrackIdKeyByType[config.type];\n const patch: Partial<TrackSelectionState> = { [selectedTrackKey]: selectedTrackId };\n update(state, patch);\n }\n });\n}\n\n/**\n * Select audio track orchestration.\n *\n * Selects audio track when:\n * - Presentation exists\n * - No audio track is selected yet\n *\n * Uses language and preference-based selection.\n *\n * @example\n * const cleanup = selectAudioTrack(\n * { state, owners, events },\n * { preferredAudioLanguage: 'en' }\n * );\n */\nexport function selectAudioTrack<S extends TrackSelectionState>(\n { state }: { state: Signal<S> },\n config: AudioSelectionConfig = { type: 'audio' }\n): () => void {\n return effect(() => {\n const currentState = state.get();\n if (!canSelectTrack(currentState, config) || !shouldSelectTrack(currentState, config)) return;\n\n // Just to have basic functionality/POC, simply selecting the first track\n const selectedTrackId = currentState.presentation?.selectionSets.find(({ type }) => type === 'audio')\n ?.switchingSets[0]?.tracks[0]?.id;\n\n if (selectedTrackId) {\n const patch: Partial<TrackSelectionState> = { selectedAudioTrackId: selectedTrackId };\n update(state, patch);\n }\n });\n}\n\n/**\n * Select text track orchestration.\n *\n * Selects text track when:\n * - Presentation exists\n * - No text track is selected yet\n *\n * Note: Currently does not auto-select (user opt-in).\n *\n * @example\n * const cleanup = selectTextTrack({ state, owners, events }, {});\n */\nexport function selectTextTrack<S extends TrackSelectionState>(\n { state }: { state: Signal<S> },\n config: TextSelectionConfig = { type: 'text' }\n): () => void {\n return effect(() => {\n const currentState = state.get();\n if (!canSelectTrack(currentState, config) || !shouldSelectTrack(currentState, config)) return;\n\n // Text tracks are user opt-in - don't auto-select\n const selectedTextTrackId = pickTextTrack(currentState.presentation!, config);\n\n if (selectedTextTrackId) {\n const patch: Partial<TrackSelectionState> = { selectedTextTrackId };\n update(state, patch);\n }\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAwMA,SAAgB,cAAc,cAA4B,QAAiD;CACzG,MAAM,UAAU,aAAa,cAAc,MAAM,QAAQ,IAAI,SAAS,OAAO;AAC7E,KAAI,CAAC,SAAS,gBAAgB,IAAI,OAAO,OAAQ,QAAO,KAAA;CAExD,MAAM,SAAS,QAAQ,cAAc,GAAG;CAIxC,MAAM,kBAAkB,OAAO,sBAAsB,SAAS,OAAO,QAAQ,UAAU,CAAC,MAAM,OAAO;AAErG,KAAI,gBAAgB,WAAW,EAAG,QAAO,KAAA;CAEzC,MAAM,EAAE,2BAA2B,qBAAqB,UAAU;AAGlE,KAAI,2BAA2B;EAC7B,MAAM,gBAAgB,gBAAgB,MAAM,UAAU,MAAM,aAAa,0BAA0B;AACnG,MAAI,cAAe,QAAO,cAAc;;AAK1C,KAAI,oBAAoB;EACtB,MAAM,eAAe,gBAAgB,MAAM,UAAU,MAAM,YAAY,KAAK;AAC5E,MAAI,aAAc,QAAO,aAAa;;;;;;;;;;;;AAgB1C,SAAgB,eACd,OACA,QACS;AACT,QAAO,CAAC,CAAC,OAAO,cAAc,eAAe,MAAM,EAAE,WAAW,SAAS,OAAO,KAAK,EAAE,gBAAgB,IAAI,OACxG;;;;;;;;;;;;;AAcL,SAAgB,kBACd,OACA,QACS;AACT,QAAO,CAAC,MAAM,yBAAyB,OAAO;;;;;;;;;;;;;;;;;AAsBhD,SAAgB,iBACd,EAAE,SACF,SAA+B,EAAE,MAAM,SAAS,EACpC;AACZ,QAAO,aAAa;EAClB,MAAM,eAAe,MAAM,KAAK;AAChC,MAAI,CAAC,eAAe,cAAc,OAAO,IAAI,CAAC,kBAAkB,cAAc,OAAO,CAAE;EAGvF,MAAM,kBAAkB,aAAa,cAAc,cAAc,MAAM,EAAE,WAAW,SAAS,OAAO,KAAK,EACrG,cAAc,IAAI,OAAO,IAAI;AAEjC,MAAI,gBAGF,QAAO,OADqC,GADnB,yBAAyB,OAAO,QACS,iBAAiB,CAC/D;GAEtB;;;;;;;;;;;;;;;;;AAkBJ,SAAgB,iBACd,EAAE,SACF,SAA+B,EAAE,MAAM,SAAS,EACpC;AACZ,QAAO,aAAa;EAClB,MAAM,eAAe,MAAM,KAAK;AAChC,MAAI,CAAC,eAAe,cAAc,OAAO,IAAI,CAAC,kBAAkB,cAAc,OAAO,CAAE;EAGvF,MAAM,kBAAkB,aAAa,cAAc,cAAc,MAAM,EAAE,WAAW,SAAS,QAAQ,EACjG,cAAc,IAAI,OAAO,IAAI;AAEjC,MAAI,gBAEF,QAAO,OADqC,EAAE,sBAAsB,iBAAiB,CACjE;GAEtB;;;;;;;;;;;;;;AAeJ,SAAgB,gBACd,EAAE,SACF,SAA8B,EAAE,MAAM,QAAQ,EAClC;AACZ,QAAO,aAAa;EAClB,MAAM,eAAe,MAAM,KAAK;AAChC,MAAI,CAAC,eAAe,cAAc,OAAO,IAAI,CAAC,kBAAkB,cAAc,OAAO,CAAE;EAGvF,MAAM,sBAAsB,cAAc,aAAa,cAAe,OAAO;AAE7E,MAAI,oBAEF,QAAO,OADqC,EAAE,qBAAqB,CAC/C;GAEtB"}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { effect } from "../signals/effect.js";
|
|
2
|
-
import { computed, update } from "../signals/primitives.js";
|
|
3
|
-
//#region src/core/features/sync-preload-attribute.ts
|
|
4
|
-
/**
|
|
5
|
-
* Syncs preload attribute from mediaElement to state.
|
|
6
|
-
*
|
|
7
|
-
* Watches the owners signal for mediaElement changes and copies the
|
|
8
|
-
* preload attribute to state when no explicit value has been set.
|
|
9
|
-
* An explicit value (set via SpfMedia.preload) always wins.
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
* const cleanup = syncPreloadAttribute({ state, owners });
|
|
13
|
-
*/
|
|
14
|
-
function syncPreloadAttribute({ state, owners }) {
|
|
15
|
-
const mediaElement = computed(() => owners.get().mediaElement);
|
|
16
|
-
return effect(() => {
|
|
17
|
-
if (state.get().preload !== void 0) return;
|
|
18
|
-
const preload = mediaElement.get()?.preload || void 0;
|
|
19
|
-
if (preload === void 0) return;
|
|
20
|
-
update(state, { preload });
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
//#endregion
|
|
24
|
-
export { syncPreloadAttribute };
|
|
25
|
-
|
|
26
|
-
//# sourceMappingURL=sync-preload-attribute.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sync-preload-attribute.js","names":[],"sources":["../../../../src/core/features/sync-preload-attribute.ts"],"sourcesContent":["import { effect } from '../signals/effect';\nimport { computed, type Signal, update } from '../signals/primitives';\nimport type { MediaElementLike } from '../types';\nimport type { PresentationState } from './resolve-presentation';\n\n/**\n * Owners shape for preload attribute sync.\n */\nexport interface PlatformOwners {\n mediaElement?: MediaElementLike | undefined;\n}\n\n/**\n * Syncs preload attribute from mediaElement to state.\n *\n * Watches the owners signal for mediaElement changes and copies the\n * preload attribute to state when no explicit value has been set.\n * An explicit value (set via SpfMedia.preload) always wins.\n *\n * @example\n * const cleanup = syncPreloadAttribute({ state, owners });\n */\nexport function syncPreloadAttribute<S extends PresentationState, O extends PlatformOwners>({\n state,\n owners,\n}: {\n state: Signal<S>;\n owners: Signal<O>;\n}): () => void {\n const mediaElement = computed(() => owners.get().mediaElement);\n return effect(() => {\n if (state.get().preload !== undefined) return;\n const preload = mediaElement.get()?.preload || undefined;\n if (preload === undefined) return;\n const patch: Partial<PresentationState> = { preload: preload as 'auto' | 'metadata' | 'none' };\n update(state, patch);\n });\n}\n"],"mappings":";;;;;;;;;;;;;AAsBA,SAAgB,qBAA4E,EAC1F,OACA,UAIa;CACb,MAAM,eAAe,eAAe,OAAO,KAAK,CAAC,aAAa;AAC9D,QAAO,aAAa;AAClB,MAAI,MAAM,KAAK,CAAC,YAAY,KAAA,EAAW;EACvC,MAAM,UAAU,aAAa,KAAK,EAAE,WAAW,KAAA;AAC/C,MAAI,YAAY,KAAA,EAAW;AAE3B,SAAO,OADmC,EAAW,SAAyC,CAC1E;GACpB"}
|