@videojs/spf 10.0.0-beta.26 → 10.0.0-beta.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (238) hide show
  1. package/dist/default/core/signals/when.js +38 -0
  2. package/dist/default/core/signals/when.js.map +1 -0
  3. package/dist/default/core/tasks/delayed-reschedule.js +31 -0
  4. package/dist/default/core/tasks/delayed-reschedule.js.map +1 -0
  5. package/dist/default/core/tasks/task.js +133 -3
  6. package/dist/default/core/tasks/task.js.map +1 -1
  7. package/dist/default/hls-audio.js +3 -0
  8. package/dist/default/hls-background-video.js +3 -0
  9. package/dist/default/hls-video.js +4 -0
  10. package/dist/default/hls.js +6 -5
  11. package/dist/default/media/dom/capabilities.js +2 -0
  12. package/dist/default/media/dom/capabilities.js.map +1 -1
  13. package/dist/default/media/errors.js +62 -0
  14. package/dist/default/media/errors.js.map +1 -0
  15. package/dist/default/media/hls/parse-media-playlist.js +167 -16
  16. package/dist/default/media/hls/parse-media-playlist.js.map +1 -1
  17. package/dist/default/media/hls/parse-multivariant.js +14 -3
  18. package/dist/default/media/hls/parse-multivariant.js.map +1 -1
  19. package/dist/default/media/hls/reload-policy.js +71 -0
  20. package/dist/default/media/hls/reload-policy.js.map +1 -0
  21. package/dist/default/media/live-window.js +36 -0
  22. package/dist/default/media/live-window.js.map +1 -0
  23. package/dist/default/media/primitives/select-tracks.js +1 -24
  24. package/dist/default/media/primitives/select-tracks.js.map +1 -1
  25. package/dist/default/media/types/index.js +15 -1
  26. package/dist/default/media/types/index.js.map +1 -1
  27. package/dist/default/media/utils/tracks.js +14 -0
  28. package/dist/default/media/utils/tracks.js.map +1 -1
  29. package/dist/default/mux-audio.js +3 -0
  30. package/dist/default/mux-background-video.js +3 -0
  31. package/dist/default/mux-video.js +3 -0
  32. package/dist/default/playback/adapters/hls-audio/adapter.js +186 -0
  33. package/dist/default/playback/adapters/hls-audio/adapter.js.map +1 -0
  34. package/dist/default/playback/adapters/hls-audio/media.js +9 -0
  35. package/dist/default/playback/adapters/hls-audio/media.js.map +1 -0
  36. package/dist/default/playback/adapters/hls-background-video/adapter.js +123 -0
  37. package/dist/default/playback/adapters/hls-background-video/adapter.js.map +1 -0
  38. package/dist/default/playback/adapters/hls-background-video/host.js +60 -0
  39. package/dist/default/playback/adapters/hls-background-video/host.js.map +1 -0
  40. package/dist/default/playback/adapters/hls-background-video/media.js +21 -0
  41. package/dist/default/playback/adapters/hls-background-video/media.js.map +1 -0
  42. package/dist/default/playback/adapters/hls-video/adapter.js +316 -0
  43. package/dist/default/playback/adapters/hls-video/adapter.js.map +1 -0
  44. package/dist/default/playback/adapters/hls-video/error-surface.js +51 -0
  45. package/dist/default/playback/adapters/hls-video/error-surface.js.map +1 -0
  46. package/dist/default/playback/adapters/hls-video/media-tracks.js +126 -0
  47. package/dist/default/playback/adapters/hls-video/media-tracks.js.map +1 -0
  48. package/dist/default/playback/adapters/hls-video/media.js +11 -0
  49. package/dist/default/playback/adapters/hls-video/media.js.map +1 -0
  50. package/dist/default/playback/adapters/mux-audio/media.js +26 -0
  51. package/dist/default/playback/adapters/mux-audio/media.js.map +1 -0
  52. package/dist/default/playback/adapters/mux-video/adapter.js +92 -0
  53. package/dist/default/playback/adapters/mux-video/adapter.js.map +1 -0
  54. package/dist/default/playback/adapters/mux-video/media.js +23 -0
  55. package/dist/default/playback/adapters/mux-video/media.js.map +1 -0
  56. package/dist/default/playback/behaviors/collect-errors.js +77 -0
  57. package/dist/default/playback/behaviors/collect-errors.js.map +1 -0
  58. package/dist/default/playback/behaviors/dom/end-of-stream.js +1 -0
  59. package/dist/default/playback/behaviors/dom/end-of-stream.js.map +1 -1
  60. package/dist/default/playback/behaviors/dom/seek-to-live-edge.js +132 -0
  61. package/dist/default/playback/behaviors/dom/seek-to-live-edge.js.map +1 -0
  62. package/dist/default/playback/behaviors/dom/sync-live-seekable-range.js +26 -0
  63. package/dist/default/playback/behaviors/dom/sync-live-seekable-range.js.map +1 -0
  64. package/dist/default/playback/behaviors/dom/update-mediasource-duration.js +29 -7
  65. package/dist/default/playback/behaviors/dom/update-mediasource-duration.js.map +1 -1
  66. package/dist/default/playback/behaviors/establish-start-media-time.js +78 -4
  67. package/dist/default/playback/behaviors/establish-start-media-time.js.map +1 -1
  68. package/dist/default/playback/behaviors/resolve-track.js +29 -8
  69. package/dist/default/playback/behaviors/resolve-track.js.map +1 -1
  70. package/dist/default/playback/behaviors/track-switching.js +18 -9
  71. package/dist/default/playback/behaviors/track-switching.js.map +1 -1
  72. package/dist/default/playback/engines/hls/engine-audio-only.js +9 -5
  73. package/dist/default/playback/engines/hls/engine-audio-only.js.map +1 -1
  74. package/dist/{dev/playback/engines/background-video/engine.js → default/playback/engines/hls/engine-background-video.js} +3 -3
  75. package/dist/default/playback/engines/hls/engine-background-video.js.map +1 -0
  76. package/dist/default/playback/engines/hls/engine.js +19 -6
  77. package/dist/default/playback/engines/hls/engine.js.map +1 -1
  78. package/dist/default/playback/primitives/error-messages.js +39 -0
  79. package/dist/default/playback/primitives/error-messages.js.map +1 -0
  80. package/dist/default/playback/primitives/live-window.js +66 -0
  81. package/dist/default/playback/primitives/live-window.js.map +1 -0
  82. package/dist/default/playback/primitives/report-track-conditions.js +86 -0
  83. package/dist/default/playback/primitives/report-track-conditions.js.map +1 -0
  84. package/dist/dev/core/signals/when.js +38 -0
  85. package/dist/dev/core/signals/when.js.map +1 -0
  86. package/dist/dev/core/tasks/delayed-reschedule.js +31 -0
  87. package/dist/dev/core/tasks/delayed-reschedule.js.map +1 -0
  88. package/dist/dev/core/tasks/task.d.ts +52 -1
  89. package/dist/dev/core/tasks/task.d.ts.map +1 -1
  90. package/dist/dev/core/tasks/task.js +133 -3
  91. package/dist/dev/core/tasks/task.js.map +1 -1
  92. package/dist/dev/hls-audio.d.ts +3 -0
  93. package/dist/dev/hls-audio.js +3 -0
  94. package/dist/dev/hls-background-video.d.ts +3 -0
  95. package/dist/dev/hls-background-video.js +3 -0
  96. package/dist/dev/hls-video.d.ts +5 -0
  97. package/dist/dev/hls-video.js +4 -0
  98. package/dist/dev/hls.d.ts +6 -5
  99. package/dist/dev/hls.js +6 -5
  100. package/dist/dev/media/dom/capabilities.js +2 -0
  101. package/dist/dev/media/dom/capabilities.js.map +1 -1
  102. package/dist/dev/media/errors.d.ts +82 -0
  103. package/dist/dev/media/errors.d.ts.map +1 -0
  104. package/dist/dev/media/errors.js +62 -0
  105. package/dist/dev/media/errors.js.map +1 -0
  106. package/dist/dev/media/hls/parse-media-playlist.js +167 -16
  107. package/dist/dev/media/hls/parse-media-playlist.js.map +1 -1
  108. package/dist/dev/media/hls/parse-multivariant.js +14 -3
  109. package/dist/dev/media/hls/parse-multivariant.js.map +1 -1
  110. package/dist/dev/media/hls/reload-policy.js +71 -0
  111. package/dist/dev/media/hls/reload-policy.js.map +1 -0
  112. package/dist/dev/media/live-window.js +36 -0
  113. package/dist/dev/media/live-window.js.map +1 -0
  114. package/dist/dev/media/primitives/select-tracks.js +1 -24
  115. package/dist/dev/media/primitives/select-tracks.js.map +1 -1
  116. package/dist/dev/media/types/index.d.ts +125 -9
  117. package/dist/dev/media/types/index.d.ts.map +1 -1
  118. package/dist/dev/media/types/index.js +15 -1
  119. package/dist/dev/media/types/index.js.map +1 -1
  120. package/dist/dev/media/utils/tracks.js +14 -0
  121. package/dist/dev/media/utils/tracks.js.map +1 -1
  122. package/dist/dev/mux-audio.d.ts +4 -0
  123. package/dist/dev/mux-audio.js +3 -0
  124. package/dist/dev/mux-background-video.d.ts +3 -0
  125. package/dist/dev/mux-background-video.js +3 -0
  126. package/dist/dev/mux-video.d.ts +4 -0
  127. package/dist/dev/mux-video.js +3 -0
  128. package/dist/dev/playback/adapters/hls-audio/adapter.d.ts +54 -0
  129. package/dist/dev/playback/adapters/hls-audio/adapter.d.ts.map +1 -0
  130. package/dist/dev/playback/adapters/hls-audio/adapter.js +186 -0
  131. package/dist/dev/playback/adapters/hls-audio/adapter.js.map +1 -0
  132. package/dist/dev/playback/adapters/hls-audio/media.d.ts +10 -0
  133. package/dist/dev/playback/adapters/hls-audio/media.d.ts.map +1 -0
  134. package/dist/dev/playback/adapters/hls-audio/media.js +9 -0
  135. package/dist/dev/playback/adapters/hls-audio/media.js.map +1 -0
  136. package/dist/dev/playback/adapters/hls-background-video/adapter.d.ts +59 -0
  137. package/dist/dev/playback/adapters/hls-background-video/adapter.d.ts.map +1 -0
  138. package/dist/dev/playback/adapters/hls-background-video/adapter.js +123 -0
  139. package/dist/dev/playback/adapters/hls-background-video/adapter.js.map +1 -0
  140. package/dist/dev/playback/adapters/hls-background-video/host.d.ts +38 -0
  141. package/dist/dev/playback/adapters/hls-background-video/host.d.ts.map +1 -0
  142. package/dist/dev/playback/adapters/hls-background-video/host.js +60 -0
  143. package/dist/dev/playback/adapters/hls-background-video/host.js.map +1 -0
  144. package/dist/dev/playback/adapters/hls-background-video/media.d.ts +20 -0
  145. package/dist/dev/playback/adapters/hls-background-video/media.d.ts.map +1 -0
  146. package/dist/dev/playback/adapters/hls-background-video/media.js +21 -0
  147. package/dist/dev/playback/adapters/hls-background-video/media.js.map +1 -0
  148. package/dist/dev/playback/adapters/hls-video/adapter.d.ts +63 -0
  149. package/dist/dev/playback/adapters/hls-video/adapter.d.ts.map +1 -0
  150. package/dist/dev/playback/adapters/hls-video/adapter.js +316 -0
  151. package/dist/dev/playback/adapters/hls-video/adapter.js.map +1 -0
  152. package/dist/dev/playback/adapters/hls-video/error-surface.d.ts +18 -0
  153. package/dist/dev/playback/adapters/hls-video/error-surface.d.ts.map +1 -0
  154. package/dist/dev/playback/adapters/hls-video/error-surface.js +51 -0
  155. package/dist/dev/playback/adapters/hls-video/error-surface.js.map +1 -0
  156. package/dist/dev/playback/adapters/hls-video/media-tracks.d.ts +22 -0
  157. package/dist/dev/playback/adapters/hls-video/media-tracks.d.ts.map +1 -0
  158. package/dist/dev/playback/adapters/hls-video/media-tracks.js +126 -0
  159. package/dist/dev/playback/adapters/hls-video/media-tracks.js.map +1 -0
  160. package/dist/dev/playback/adapters/hls-video/media.d.ts +10 -0
  161. package/dist/dev/playback/adapters/hls-video/media.d.ts.map +1 -0
  162. package/dist/dev/playback/adapters/hls-video/media.js +11 -0
  163. package/dist/dev/playback/adapters/hls-video/media.js.map +1 -0
  164. package/dist/dev/playback/adapters/mux-audio/media.d.ts +28 -0
  165. package/dist/dev/playback/adapters/mux-audio/media.d.ts.map +1 -0
  166. package/dist/dev/playback/adapters/mux-audio/media.js +26 -0
  167. package/dist/dev/playback/adapters/mux-audio/media.js.map +1 -0
  168. package/dist/dev/playback/adapters/mux-video/adapter.d.ts +33 -0
  169. package/dist/dev/playback/adapters/mux-video/adapter.d.ts.map +1 -0
  170. package/dist/dev/playback/adapters/mux-video/adapter.js +92 -0
  171. package/dist/dev/playback/adapters/mux-video/adapter.js.map +1 -0
  172. package/dist/dev/playback/adapters/mux-video/media.d.ts +24 -0
  173. package/dist/dev/playback/adapters/mux-video/media.d.ts.map +1 -0
  174. package/dist/dev/playback/adapters/mux-video/media.js +23 -0
  175. package/dist/dev/playback/adapters/mux-video/media.js.map +1 -0
  176. package/dist/dev/playback/behaviors/collect-errors.js +77 -0
  177. package/dist/dev/playback/behaviors/collect-errors.js.map +1 -0
  178. package/dist/dev/playback/behaviors/dom/end-of-stream.js +1 -0
  179. package/dist/dev/playback/behaviors/dom/end-of-stream.js.map +1 -1
  180. package/dist/dev/playback/behaviors/dom/seek-to-live-edge.js +132 -0
  181. package/dist/dev/playback/behaviors/dom/seek-to-live-edge.js.map +1 -0
  182. package/dist/dev/playback/behaviors/dom/sync-live-seekable-range.js +26 -0
  183. package/dist/dev/playback/behaviors/dom/sync-live-seekable-range.js.map +1 -0
  184. package/dist/dev/playback/behaviors/dom/update-mediasource-duration.js +29 -7
  185. package/dist/dev/playback/behaviors/dom/update-mediasource-duration.js.map +1 -1
  186. package/dist/dev/playback/behaviors/establish-start-media-time.d.ts +5 -0
  187. package/dist/dev/playback/behaviors/establish-start-media-time.d.ts.map +1 -1
  188. package/dist/dev/playback/behaviors/establish-start-media-time.js +78 -4
  189. package/dist/dev/playback/behaviors/establish-start-media-time.js.map +1 -1
  190. package/dist/dev/playback/behaviors/resolve-track.js +29 -8
  191. package/dist/dev/playback/behaviors/resolve-track.js.map +1 -1
  192. package/dist/dev/playback/behaviors/track-switching.js +18 -9
  193. package/dist/dev/playback/behaviors/track-switching.js.map +1 -1
  194. package/dist/dev/playback/engines/hls/engine-audio-only.d.ts +34 -17
  195. package/dist/dev/playback/engines/hls/engine-audio-only.d.ts.map +1 -1
  196. package/dist/dev/playback/engines/hls/engine-audio-only.js +9 -5
  197. package/dist/dev/playback/engines/hls/engine-audio-only.js.map +1 -1
  198. package/dist/dev/playback/engines/{background-video/engine.d.ts → hls/engine-background-video.d.ts} +7 -7
  199. package/dist/dev/playback/engines/hls/engine-background-video.d.ts.map +1 -0
  200. package/dist/{default/playback/engines/background-video/engine.js → dev/playback/engines/hls/engine-background-video.js} +3 -3
  201. package/dist/dev/playback/engines/hls/engine-background-video.js.map +1 -0
  202. package/dist/dev/playback/engines/hls/engine.d.ts +43 -15
  203. package/dist/dev/playback/engines/hls/engine.d.ts.map +1 -1
  204. package/dist/dev/playback/engines/hls/engine.js +19 -6
  205. package/dist/dev/playback/engines/hls/engine.js.map +1 -1
  206. package/dist/dev/playback/primitives/error-messages.js +39 -0
  207. package/dist/dev/playback/primitives/error-messages.js.map +1 -0
  208. package/dist/dev/playback/primitives/live-window.js +66 -0
  209. package/dist/dev/playback/primitives/live-window.js.map +1 -0
  210. package/dist/dev/playback/primitives/report-track-conditions.d.ts +11 -0
  211. package/dist/dev/playback/primitives/report-track-conditions.d.ts.map +1 -0
  212. package/dist/dev/playback/primitives/report-track-conditions.js +86 -0
  213. package/dist/dev/playback/primitives/report-track-conditions.js.map +1 -0
  214. package/package.json +32 -6
  215. package/dist/default/background-video.js +0 -3
  216. package/dist/default/playback/engines/background-video/adapter.js +0 -158
  217. package/dist/default/playback/engines/background-video/adapter.js.map +0 -1
  218. package/dist/default/playback/engines/background-video/engine.js.map +0 -1
  219. package/dist/default/playback/engines/hls/adapter-audio-only.js +0 -124
  220. package/dist/default/playback/engines/hls/adapter-audio-only.js.map +0 -1
  221. package/dist/default/playback/engines/hls/adapter.js +0 -120
  222. package/dist/default/playback/engines/hls/adapter.js.map +0 -1
  223. package/dist/dev/background-video.d.ts +0 -3
  224. package/dist/dev/background-video.js +0 -3
  225. package/dist/dev/playback/engines/background-video/adapter.d.ts +0 -59
  226. package/dist/dev/playback/engines/background-video/adapter.d.ts.map +0 -1
  227. package/dist/dev/playback/engines/background-video/adapter.js +0 -158
  228. package/dist/dev/playback/engines/background-video/adapter.js.map +0 -1
  229. package/dist/dev/playback/engines/background-video/engine.d.ts.map +0 -1
  230. package/dist/dev/playback/engines/background-video/engine.js.map +0 -1
  231. package/dist/dev/playback/engines/hls/adapter-audio-only.d.ts +0 -46
  232. package/dist/dev/playback/engines/hls/adapter-audio-only.d.ts.map +0 -1
  233. package/dist/dev/playback/engines/hls/adapter-audio-only.js +0 -124
  234. package/dist/dev/playback/engines/hls/adapter-audio-only.js.map +0 -1
  235. package/dist/dev/playback/engines/hls/adapter.d.ts +0 -42
  236. package/dist/dev/playback/engines/hls/adapter.d.ts.map +0 -1
  237. package/dist/dev/playback/engines/hls/adapter.js +0 -120
  238. package/dist/dev/playback/engines/hls/adapter.js.map +0 -1
@@ -0,0 +1,186 @@
1
+ import { effect } from "../../../core/signals/effect.js";
2
+ import { SVTA_NO_SUPPORTED_AUDIO_TRACK, SVTA_UNSUPPORTED_PLAYBACK_FEATURE } from "../../../media/errors.js";
3
+ import { createHlsAudioEngine } from "../../engines/hls/engine-audio-only.js";
4
+ import { UNSUPPORTED_PLAYBACK_FEATURE_MESSAGE } from "../../primitives/error-messages.js";
5
+ import { firstFatal, hasUnsupportedFeatureCause, withAlternativeMediaSuggestion } from "../hls-video/error-surface.js";
6
+ //#region src/playback/adapters/hls-audio/adapter.ts
7
+ const hlsAudioMediaDefaultProps = {
8
+ src: "",
9
+ preload: "",
10
+ disableRemotePlayback: false
11
+ };
12
+ /**
13
+ * Which reported conditions this composition treats as fatal. Only the audio
14
+ * verdict: an audio-only engine composes no video selection, so
15
+ * `SVTA_NO_SUPPORTED_VIDEO_TRACK` is never reported and surfacing it would
16
+ * describe a track type this media doesn't have.
17
+ */
18
+ const FATAL_SVTA_CODES = /* @__PURE__ */ new Set([SVTA_NO_SUPPORTED_AUDIO_TRACK]);
19
+ /**
20
+ * Mixin that adds SPF audio-only HLS playback to any base class.
21
+ *
22
+ * @fires error - Fired when a fatal condition is reported. Read `error` for it.
23
+ *
24
+ * Parallel to `HlsVideoMediaMixin` with one substantive difference: the
25
+ * underlying engine is the audio-only variant (`createHlsAudioEngine`),
26
+ * which omits video and text-track behaviors. The src / preload /
27
+ * disableRemotePlayback / play() contract per the WHATWG HTML spec is identical
28
+ * to the default adapter.
29
+ *
30
+ * Selecting this adapter is the variant decision: instantiating
31
+ * `HlsAudioMediaElement` opts the consumer into audio-only
32
+ * delivery even when the source is a mixed-AV HLS manifest.
33
+ *
34
+ * @example
35
+ * class HlsAudioMedia extends HlsAudioMediaMixin(HTMLVideoElementHost) {}
36
+ *
37
+ * const media = new HlsAudioMedia();
38
+ * media.attach(document.querySelector('video'));
39
+ * media.src = 'https://stream.mux.com/abc123.m3u8';
40
+ */
41
+ function HlsAudioMediaMixin(BaseClass) {
42
+ class HlsAudioMediaImpl extends BaseClass {
43
+ /**
44
+ * A complete sentence naming the Media to reach for when this one can't play
45
+ * a source. Appended to the copy this adapter logs.
46
+ *
47
+ * Empty here, and overridden the same way as on the video adapter — see its
48
+ * note. `hls-audio` has no better-equipped sibling of its own; the
49
+ * Mux audio Media built on this engine does, and points at the hls.js-backed
50
+ * one.
51
+ */
52
+ static get alternativeMediaSuggestion() {}
53
+ #engine;
54
+ #config;
55
+ #signals;
56
+ #preload = hlsAudioMediaDefaultProps.preload;
57
+ #disableRemotePlayback = hlsAudioMediaDefaultProps.disableRemotePlayback;
58
+ #error = null;
59
+ /** Reported condition currently surfaced — see the video adapter's note. */
60
+ #reportedCode = null;
61
+ #stopErrorSync;
62
+ /** Pending loadstart listener from a deferred play() retry, if any. */
63
+ #loadstartListener = null;
64
+ constructor(...args) {
65
+ super(...args);
66
+ const { config } = args?.[0] ?? {};
67
+ this.#config = config;
68
+ this.#engine = this.#createEngine();
69
+ this.#stopErrorSync = effect(() => {
70
+ const errors = this.#signals.state.errors.get();
71
+ this.#setError(firstFatal(errors, FATAL_SVTA_CODES), errors);
72
+ });
73
+ }
74
+ /**
75
+ * The current fatal error, or `null`. Only *fatal* conditions appear here —
76
+ * the engine reports non-fatal ones too, which stay in `engine.state.errors`.
77
+ * Resets per source. Fires `'error'` when set.
78
+ */
79
+ get error() {
80
+ return this.#error;
81
+ }
82
+ #setError(reported, errors) {
83
+ if (!reported) {
84
+ this.#error = null;
85
+ this.#reportedCode = null;
86
+ return;
87
+ }
88
+ if (this.#reportedCode === reported.code) return;
89
+ this.#reportedCode = reported.code;
90
+ const unsupported = hasUnsupportedFeatureCause(errors);
91
+ if (unsupported) console.error(this.#withSuggestion(UNSUPPORTED_PLAYBACK_FEATURE_MESSAGE), { conditions: errors });
92
+ this.#error = {
93
+ code: unsupported ? SVTA_UNSUPPORTED_PLAYBACK_FEATURE : reported.code,
94
+ message: reported.message ?? "",
95
+ ...reported.data === void 0 ? {} : { data: reported.data }
96
+ };
97
+ this.dispatchEvent?.(new Event("error"));
98
+ }
99
+ /**
100
+ * Underlying playback engine — the low-level SPF reactive composition that
101
+ * drives playback. An advanced escape hatch for direct engine access;
102
+ * normal playback is driven through this element's own properties and
103
+ * methods.
104
+ */
105
+ get engine() {
106
+ return this.#engine;
107
+ }
108
+ attach(mediaElement) {
109
+ super.attach?.(mediaElement);
110
+ this.#signals.context.mediaElement.set(mediaElement);
111
+ }
112
+ detach() {
113
+ this.#cancelPendingPlay();
114
+ this.#signals.context.mediaElement.set(void 0);
115
+ super.detach?.();
116
+ }
117
+ destroy() {
118
+ this.#cancelPendingPlay();
119
+ this.#stopErrorSync();
120
+ this.#engine.destroy();
121
+ }
122
+ /** Preload type (`'none'` / `'metadata'` / `'auto'`). */
123
+ get preload() {
124
+ return this.#preload;
125
+ }
126
+ set preload(value) {
127
+ this.#preload = value;
128
+ if (value) this.#signals.state.preload.set(value);
129
+ }
130
+ get disableRemotePlayback() {
131
+ return this.#disableRemotePlayback;
132
+ }
133
+ set disableRemotePlayback(value) {
134
+ this.#disableRemotePlayback = value;
135
+ this.#signals.state.disableRemotePlayback.set(value);
136
+ }
137
+ get src() {
138
+ return this.#signals.state.presentation.get()?.url ?? "";
139
+ }
140
+ set src(value) {
141
+ if (value === this.src) return;
142
+ this.#cancelPendingPlay();
143
+ this.#signals.state.presentation.set(value ? { url: value } : void 0);
144
+ }
145
+ play() {
146
+ const mediaElement = this.#signals.context.mediaElement.get();
147
+ if (!mediaElement) return Promise.reject(/* @__PURE__ */ new Error("HlsAudioMediaElement: no media element attached"));
148
+ this.#signals.state.loadActivated.set(true);
149
+ return mediaElement.play().catch((err) => {
150
+ if (this.src) return new Promise((resolve, reject) => {
151
+ const listener = () => {
152
+ this.#loadstartListener = null;
153
+ mediaElement.play().then(resolve, reject);
154
+ };
155
+ this.#loadstartListener = listener;
156
+ mediaElement.addEventListener("loadstart", listener, { once: true });
157
+ });
158
+ throw err;
159
+ });
160
+ }
161
+ /** `message`, plus the alternative-Media sentence when this class names one. */
162
+ #withSuggestion(message) {
163
+ return withAlternativeMediaSuggestion(message, this);
164
+ }
165
+ #createEngine() {
166
+ return createHlsAudioEngine({
167
+ ...this.#config,
168
+ onSignalsReady: (signals) => {
169
+ this.#signals = signals;
170
+ }
171
+ });
172
+ }
173
+ #cancelPendingPlay() {
174
+ if (!this.#loadstartListener) return;
175
+ this.#signals.context.mediaElement.get()?.removeEventListener("loadstart", this.#loadstartListener);
176
+ this.#loadstartListener = null;
177
+ }
178
+ }
179
+ return HlsAudioMediaImpl;
180
+ }
181
+ /** Standalone SPF audio-only media adapter with no base class. */
182
+ var HlsAudioMediaElement = class extends HlsAudioMediaMixin(class {}) {};
183
+ //#endregion
184
+ export { HlsAudioMediaElement, HlsAudioMediaMixin, hlsAudioMediaDefaultProps };
185
+
186
+ //# sourceMappingURL=adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.js","names":["#engine","#config","#createEngine","#stopErrorSync","#signals","#setError","#error","#reportedCode","#withSuggestion","#cancelPendingPlay","#preload","#disableRemotePlayback","#loadstartListener"],"sources":["../../../../../src/playback/adapters/hls-audio/adapter.ts"],"sourcesContent":["import type { Constructor, MixinReturn } from '@videojs/utils/types';\nimport type { Composition } from '../../../core/composition/create-composition';\nimport { effect } from '../../../core/signals/effect';\nimport {\n SVTA_NO_SUPPORTED_AUDIO_TRACK,\n SVTA_UNSUPPORTED_PLAYBACK_FEATURE,\n type SvtaError,\n} from '../../../media/errors';\nimport {\n createHlsAudioEngine,\n type HlsAudioEngineConfig,\n type HlsAudioEngineContext,\n type HlsAudioEngineSignals,\n type HlsAudioEngineState,\n} from '../../engines/hls/engine-audio-only';\nimport { UNSUPPORTED_PLAYBACK_FEATURE_MESSAGE } from '../../primitives/error-messages';\nimport {\n firstFatal,\n type HlsVideoMediaError,\n hasUnsupportedFeatureCause,\n withAlternativeMediaSuggestion,\n} from '../hls-video/error-surface';\n\nexport interface HlsAudioMediaProps {\n src: string;\n preload: '' | 'none' | 'metadata' | 'auto';\n disableRemotePlayback: boolean;\n}\n\nexport const hlsAudioMediaDefaultProps: HlsAudioMediaProps = {\n src: '',\n preload: '',\n disableRemotePlayback: false,\n};\n\nexport interface HlsAudioMediaAPI extends HlsAudioMediaProps {\n readonly engine: Composition<HlsAudioEngineState, HlsAudioEngineContext>;\n readonly error: HlsVideoMediaError | null;\n attach(mediaElement: HTMLMediaElement): void;\n detach(): void;\n destroy(): void;\n play(): Promise<void>;\n}\n\n/**\n * Which reported conditions this composition treats as fatal. Only the audio\n * verdict: an audio-only engine composes no video selection, so\n * `SVTA_NO_SUPPORTED_VIDEO_TRACK` is never reported and surfacing it would\n * describe a track type this media doesn't have.\n */\nconst FATAL_SVTA_CODES: ReadonlySet<number> = new Set<number>([SVTA_NO_SUPPORTED_AUDIO_TRACK]);\n\n/**\n * Mixin that adds SPF audio-only HLS playback to any base class.\n *\n * @fires error - Fired when a fatal condition is reported. Read `error` for it.\n *\n * Parallel to `HlsVideoMediaMixin` with one substantive difference: the\n * underlying engine is the audio-only variant (`createHlsAudioEngine`),\n * which omits video and text-track behaviors. The src / preload /\n * disableRemotePlayback / play() contract per the WHATWG HTML spec is identical\n * to the default adapter.\n *\n * Selecting this adapter is the variant decision: instantiating\n * `HlsAudioMediaElement` opts the consumer into audio-only\n * delivery even when the source is a mixed-AV HLS manifest.\n *\n * @example\n * class HlsAudioMedia extends HlsAudioMediaMixin(HTMLVideoElementHost) {}\n *\n * const media = new HlsAudioMedia();\n * media.attach(document.querySelector('video'));\n * media.src = 'https://stream.mux.com/abc123.m3u8';\n */\nexport function HlsAudioMediaMixin<Base extends Constructor<any>>(BaseClass: Base) {\n class HlsAudioMediaImpl extends BaseClass {\n /**\n * A complete sentence naming the Media to reach for when this one can't play\n * a source. Appended to the copy this adapter logs.\n *\n * Empty here, and overridden the same way as on the video adapter — see its\n * note. `hls-audio` has no better-equipped sibling of its own; the\n * Mux audio Media built on this engine does, and points at the hls.js-backed\n * one.\n */\n static get alternativeMediaSuggestion(): string | undefined {\n return undefined;\n }\n\n readonly #engine: Composition<HlsAudioEngineState, HlsAudioEngineContext>;\n #config: HlsAudioEngineConfig;\n #signals!: HlsAudioEngineSignals;\n #preload: '' | 'none' | 'metadata' | 'auto' = hlsAudioMediaDefaultProps.preload;\n #disableRemotePlayback: boolean = hlsAudioMediaDefaultProps.disableRemotePlayback;\n #error: HlsVideoMediaError | null = null;\n /** Reported condition currently surfaced — see the video adapter's note. */\n #reportedCode: number | null = null;\n #stopErrorSync: () => void;\n\n /** Pending loadstart listener from a deferred play() retry, if any. */\n #loadstartListener: (() => void) | null = null;\n\n constructor(...args: any[]) {\n super(...args);\n\n const { config } = args?.[0] ?? {};\n this.#config = config;\n this.#engine = this.#createEngine();\n\n // Promote the first fatal condition out of the engine's reported sequence\n // onto the media surface. Clearing rides the same signal: `collectErrors`\n // resets the slot per source, so a new source starts with no error without\n // this needing its own source-change hook.\n this.#stopErrorSync = effect(() => {\n const errors = this.#signals.state.errors.get();\n this.#setError(firstFatal(errors, FATAL_SVTA_CODES), errors);\n });\n }\n\n /**\n * The current fatal error, or `null`. Only *fatal* conditions appear here —\n * the engine reports non-fatal ones too, which stay in `engine.state.errors`.\n * Resets per source. Fires `'error'` when set.\n */\n get error(): HlsVideoMediaError | null {\n return this.#error;\n }\n\n #setError(reported: SvtaError | undefined, errors: readonly SvtaError[] | undefined): void {\n if (!reported) {\n // Cleared (new source). No event: `'error'` announces a failure, and\n // consumers reset their own copy on source change.\n this.#error = null;\n this.#reportedCode = null;\n return;\n }\n // Keyed on the code, not the object: a later append re-runs this effect\n // with an equal-but-new array, and re-firing `'error'` for a condition\n // already surfaced would look like a second failure.\n if (this.#reportedCode === reported.code) return;\n this.#reportedCode = reported.code;\n\n // See the video adapter: a cause this engine can't implement is what the\n // consumer needs, so it replaces the verdict's code on the surface.\n const unsupported = hasUnsupportedFeatureCause(errors);\n if (unsupported) {\n console.error(this.#withSuggestion(UNSUPPORTED_PLAYBACK_FEATURE_MESSAGE), { conditions: errors });\n }\n\n this.#error = {\n code: unsupported ? SVTA_UNSUPPORTED_PLAYBACK_FEATURE : reported.code,\n message: reported.message ?? '',\n ...(reported.data === undefined ? {} : { data: reported.data }),\n };\n this.dispatchEvent?.(new Event('error'));\n }\n\n /**\n * Underlying playback engine — the low-level SPF reactive composition that\n * drives playback. An advanced escape hatch for direct engine access;\n * normal playback is driven through this element's own properties and\n * methods.\n */\n get engine(): Composition<HlsAudioEngineState, HlsAudioEngineContext> {\n return this.#engine;\n }\n\n // -------------------------------------------------------------------------\n // Media element lifecycle\n // -------------------------------------------------------------------------\n\n attach(mediaElement: HTMLMediaElement): void {\n super.attach?.(mediaElement);\n this.#signals.context.mediaElement.set(mediaElement);\n }\n\n detach(): void {\n this.#cancelPendingPlay();\n this.#signals.context.mediaElement.set(undefined);\n super.detach?.();\n }\n\n destroy(): void {\n this.#cancelPendingPlay();\n this.#stopErrorSync();\n this.#engine.destroy();\n }\n\n // -------------------------------------------------------------------------\n // preload — synchronous IDL attribute (WHATWG §4.8.11.2)\n // -------------------------------------------------------------------------\n\n /** Preload type (`'none'` / `'metadata'` / `'auto'`). */\n get preload(): '' | 'none' | 'metadata' | 'auto' {\n return this.#preload;\n }\n\n set preload(value: '' | 'none' | 'metadata' | 'auto') {\n this.#preload = value;\n if (value) {\n this.#signals.state.preload.set(value);\n }\n }\n\n // -------------------------------------------------------------------------\n // disableRemotePlayback — synchronous IDL attribute (WHATWG Remote Playback)\n // Author intent for whether the AirPlay/remote picker is offered. Mirrors\n // the DOM attribute name; the value flows to `state.disableRemotePlayback`,\n // which `setupAirPlay` reads to honor an explicit opt-out. The underlying\n // media element's own `disableRemotePlayback` stays programmatically managed\n // (ManagedMediaSource needs it `true` to open; AirPlay flips it `false` once\n // the source is open), so author intent and the effective flag stay distinct.\n // -------------------------------------------------------------------------\n\n get disableRemotePlayback(): boolean {\n return this.#disableRemotePlayback;\n }\n\n set disableRemotePlayback(value: boolean) {\n this.#disableRemotePlayback = value;\n this.#signals.state.disableRemotePlayback.set(value);\n }\n\n // -------------------------------------------------------------------------\n // src — synchronous IDL attribute (WHATWG §4.8.11.2)\n // Each assignment overwrites the engine's presentation state in place. The\n // resolver FSM routes back through teardown → rebuild on the same engine,\n // mirroring how the browser's load algorithm resets media state on src change\n // — without recreating the engine or re-capturing its signals. Setting an\n // empty src un-resolves the presentation, tearing the current source down to\n // the engine's fresh-but-attached \"no source\" state.\n // -------------------------------------------------------------------------\n\n get src(): string {\n return this.#signals.state.presentation.get()?.url ?? '';\n }\n\n set src(value: string) {\n // Unchanged URL, no reload — see the video adapter's note.\n if (value === this.src) return;\n\n this.#cancelPendingPlay();\n this.#signals.state.presentation.set(value ? { url: value } : undefined);\n }\n\n // -------------------------------------------------------------------------\n // play() — WHATWG §4.8.11.8\n // -------------------------------------------------------------------------\n\n play(): Promise<void> {\n const mediaElement = this.#signals.context.mediaElement.get();\n if (!mediaElement) {\n return Promise.reject(new Error('HlsAudioMediaElement: no media element attached'));\n }\n\n this.#signals.state.loadActivated.set(true);\n\n return mediaElement.play().catch((err: unknown) => {\n if (this.src) {\n return new Promise<void>((resolve, reject) => {\n const listener = () => {\n this.#loadstartListener = null;\n mediaElement.play().then(resolve, reject);\n };\n this.#loadstartListener = listener;\n mediaElement.addEventListener('loadstart', listener, { once: true });\n });\n }\n throw err;\n });\n }\n\n // -------------------------------------------------------------------------\n // Private\n // -------------------------------------------------------------------------\n\n /** `message`, plus the alternative-Media sentence when this class names one. */\n #withSuggestion(message: string): string {\n return withAlternativeMediaSuggestion(message, this);\n }\n\n #createEngine(): Composition<HlsAudioEngineState, HlsAudioEngineContext> {\n return createHlsAudioEngine({\n ...this.#config,\n onSignalsReady: (signals) => {\n this.#signals = signals;\n },\n });\n }\n\n #cancelPendingPlay(): void {\n if (!this.#loadstartListener) return;\n const mediaElement = this.#signals.context.mediaElement.get();\n mediaElement?.removeEventListener('loadstart', this.#loadstartListener);\n this.#loadstartListener = null;\n }\n }\n\n // `MixinReturn` sources statics from `Base`, so the adapter's own static needs\n // adding back to the type or callers can't read it.\n return HlsAudioMediaImpl as unknown as MixinReturn<Base, HlsAudioMediaAPI> & {\n readonly alternativeMediaSuggestion: string | undefined;\n };\n}\n\n/** Standalone SPF audio-only media adapter with no base class. */\nexport class HlsAudioMediaElement extends HlsAudioMediaMixin(class {}) {}\n"],"mappings":";;;;;;AA6BA,MAAa,4BAAgD;CAC3D,KAAK;CACL,SAAS;CACT,uBAAuB;AACzB;;;;;;;AAiBA,MAAM,mCAAwC,IAAI,IAAY,CAAC,6BAA6B,CAAC;;;;;;;;;;;;;;;;;;;;;;;AAwB7F,SAAgB,mBAAkD,WAAiB;CACjF,MAAM,0BAA0B,UAAU;;;;;;;;;;EAUxC,WAAW,6BAAiD,CAE5D;EAEA;EACA;EACA;EACA,WAA8C,0BAA0B;EACxE,yBAAkC,0BAA0B;EAC5D,SAAoC;;EAEpC,gBAA+B;EAC/B;;EAGA,qBAA0C;EAE1C,YAAY,GAAG,MAAa;GAC1B,MAAM,GAAG,IAAI;GAEb,MAAM,EAAE,WAAW,OAAO,MAAM,CAAC;GACjC,KAAKC,UAAU;GACf,KAAKD,UAAU,KAAKE,cAAc;GAMlC,KAAKC,iBAAiB,aAAa;IACjC,MAAM,SAAS,KAAKC,SAAS,MAAM,OAAO,IAAI;IAC9C,KAAKC,UAAU,WAAW,QAAQ,gBAAgB,GAAG,MAAM;GAC7D,CAAC;EACH;;;;;;EAOA,IAAI,QAAmC;GACrC,OAAO,KAAKC;EACd;EAEA,UAAU,UAAiC,QAAgD;GACzF,IAAI,CAAC,UAAU;IAGb,KAAKA,SAAS;IACd,KAAKC,gBAAgB;IACrB;GACF;GAIA,IAAI,KAAKA,kBAAkB,SAAS,MAAM;GAC1C,KAAKA,gBAAgB,SAAS;GAI9B,MAAM,cAAc,2BAA2B,MAAM;GACrD,IAAI,aACF,QAAQ,MAAM,KAAKC,gBAAgB,oCAAoC,GAAG,EAAE,YAAY,OAAO,CAAC;GAGlG,KAAKF,SAAS;IACZ,MAAM,cAAc,oCAAoC,SAAS;IACjE,SAAS,SAAS,WAAW;IAC7B,GAAI,SAAS,SAAS,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM,SAAS,KAAK;GAC/D;GACA,KAAK,gBAAgB,IAAI,MAAM,OAAO,CAAC;EACzC;;;;;;;EAQA,IAAI,SAAkE;GACpE,OAAO,KAAKN;EACd;EAMA,OAAO,cAAsC;GAC3C,MAAM,SAAS,YAAY;GAC3B,KAAKI,SAAS,QAAQ,aAAa,IAAI,YAAY;EACrD;EAEA,SAAe;GACb,KAAKK,mBAAmB;GACxB,KAAKL,SAAS,QAAQ,aAAa,IAAI,KAAA,CAAS;GAChD,MAAM,SAAS;EACjB;EAEA,UAAgB;GACd,KAAKK,mBAAmB;GACxB,KAAKN,eAAe;GACpB,KAAKH,QAAQ,QAAQ;EACvB;;EAOA,IAAI,UAA6C;GAC/C,OAAO,KAAKU;EACd;EAEA,IAAI,QAAQ,OAA0C;GACpD,KAAKA,WAAW;GAChB,IAAI,OACF,KAAKN,SAAS,MAAM,QAAQ,IAAI,KAAK;EAEzC;EAYA,IAAI,wBAAiC;GACnC,OAAO,KAAKO;EACd;EAEA,IAAI,sBAAsB,OAAgB;GACxC,KAAKA,yBAAyB;GAC9B,KAAKP,SAAS,MAAM,sBAAsB,IAAI,KAAK;EACrD;EAYA,IAAI,MAAc;GAChB,OAAO,KAAKA,SAAS,MAAM,aAAa,IAAI,CAAC,EAAE,OAAO;EACxD;EAEA,IAAI,IAAI,OAAe;GAErB,IAAI,UAAU,KAAK,KAAK;GAExB,KAAKK,mBAAmB;GACxB,KAAKL,SAAS,MAAM,aAAa,IAAI,QAAQ,EAAE,KAAK,MAAM,IAAI,KAAA,CAAS;EACzE;EAMA,OAAsB;GACpB,MAAM,eAAe,KAAKA,SAAS,QAAQ,aAAa,IAAI;GAC5D,IAAI,CAAC,cACH,OAAO,QAAQ,uBAAO,IAAI,MAAM,iDAAiD,CAAC;GAGpF,KAAKA,SAAS,MAAM,cAAc,IAAI,IAAI;GAE1C,OAAO,aAAa,KAAK,CAAC,CAAC,OAAO,QAAiB;IACjD,IAAI,KAAK,KACP,OAAO,IAAI,SAAe,SAAS,WAAW;KAC5C,MAAM,iBAAiB;MACrB,KAAKQ,qBAAqB;MAC1B,aAAa,KAAK,CAAC,CAAC,KAAK,SAAS,MAAM;KAC1C;KACA,KAAKA,qBAAqB;KAC1B,aAAa,iBAAiB,aAAa,UAAU,EAAE,MAAM,KAAK,CAAC;IACrE,CAAC;IAEH,MAAM;GACR,CAAC;EACH;;EAOA,gBAAgB,SAAyB;GACvC,OAAO,+BAA+B,SAAS,IAAI;EACrD;EAEA,gBAAyE;GACvE,OAAO,qBAAqB;IAC1B,GAAG,KAAKX;IACR,iBAAiB,YAAY;KAC3B,KAAKG,WAAW;IAClB;GACF,CAAC;EACH;EAEA,qBAA2B;GACzB,IAAI,CAAC,KAAKQ,oBAAoB;GAE9B,KAD0BR,SAAS,QAAQ,aAAa,IAC7C,CAAC,EAAE,oBAAoB,aAAa,KAAKQ,kBAAkB;GACtE,KAAKA,qBAAqB;EAC5B;CACF;CAIA,OAAO;AAGT;;AAGA,IAAa,uBAAb,cAA0C,mBAAmB,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { HlsAudioMediaAPI } from "./adapter.js";
2
+ import { HTMLAudioElementHost } from "@videojs/media/dom/audio-host";
3
+ //#region src/playback/adapters/hls-audio/media.d.ts
4
+ declare const HlsAudioMediaBase: import("@videojs/utils/types").Constructor<HTMLAudioElementHost & HlsAudioMediaAPI, any[]> & Omit<typeof HTMLAudioElementHost, "prototype"> & {
5
+ readonly alternativeMediaSuggestion: string | undefined;
6
+ };
7
+ declare class HlsAudioMedia extends HlsAudioMediaBase {}
8
+ //#endregion
9
+ export { HlsAudioMedia };
10
+ //# sourceMappingURL=media.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media.d.ts","names":[],"sources":["../../../../../src/playback/adapters/hls-audio/media.ts"],"mappings":";;;cAGM,kDAAiB,YAAA,uBAAA,2BAAA,YAAA;;;cAEV,sBAAsB"}
@@ -0,0 +1,9 @@
1
+ import { HlsAudioMediaMixin } from "./adapter.js";
2
+ import { HTMLAudioElementHost } from "@videojs/media/dom/audio-host";
3
+ //#region src/playback/adapters/hls-audio/media.ts
4
+ const HlsAudioMediaBase = HlsAudioMediaMixin(HTMLAudioElementHost);
5
+ var HlsAudioMedia = class extends HlsAudioMediaBase {};
6
+ //#endregion
7
+ export { HlsAudioMedia };
8
+
9
+ //# sourceMappingURL=media.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media.js","names":[],"sources":["../../../../../src/playback/adapters/hls-audio/media.ts"],"sourcesContent":["import { HTMLAudioElementHost } from '@videojs/media/dom/audio-host';\nimport { HlsAudioMediaMixin } from './adapter';\n\nconst HlsAudioMediaBase = HlsAudioMediaMixin(HTMLAudioElementHost);\n\nexport class HlsAudioMedia extends HlsAudioMediaBase {}\n"],"mappings":";;;AAGA,MAAM,oBAAoB,mBAAmB,oBAAoB;AAEjE,IAAa,gBAAb,cAAmC,kBAAkB,CAAC"}
@@ -0,0 +1,59 @@
1
+ import { Composition } from "../../../core/composition/create-composition.js";
2
+ import { BackgroundVideoEngineContext, BackgroundVideoEngineState } from "../../engines/hls/engine-background-video.js";
3
+ import { Constructor, MixinReturn } from "@videojs/utils/types";
4
+ //#region src/playback/adapters/hls-background-video/adapter.d.ts
5
+ interface HlsBackgroundVideoMediaProps {
6
+ src: string;
7
+ }
8
+ declare const hlsBackgroundVideoMediaDefaultProps: HlsBackgroundVideoMediaProps;
9
+ interface HlsBackgroundVideoMediaAPI extends HlsBackgroundVideoMediaProps {
10
+ readonly engine: Composition<BackgroundVideoEngineState, BackgroundVideoEngineContext>;
11
+ attach(mediaElement: HTMLMediaElement): void;
12
+ detach(): void;
13
+ destroy(): void;
14
+ play(): Promise<void>;
15
+ }
16
+ /**
17
+ * Mixin that adds the background-video SPF playback engine to any base class,
18
+ * for an HLS URL.
19
+ *
20
+ * `src` is the whole surface, and the picker always pins the top rendition on
21
+ * offer. There is no cap of its own because the manifest is the better place to
22
+ * narrow one: a delivery param — `?max_resolution=720p` on a Mux stream URL, for
23
+ * one — keeps the renditions it excludes out of the manifest entirely, rather
24
+ * than fetched-then-unpicked. Deriving a cap from the screen instead is on the
25
+ * roadmap, and lands here when it does.
26
+ *
27
+ * `@videojs/spf/mux-background-video` is this same Media under a Mux-flavored
28
+ * name — an alias, not a variant. Nothing about the surface changes with the
29
+ * import path.
30
+ *
31
+ * Everything else the use case fixes rather than exposes: video-only, looping,
32
+ * muted, autoplaying, loading as soon as there is a source. `attach` writes that
33
+ * onto the element and nothing here declares `loop` / `muted` / `autoplay` /
34
+ * `preload` of its own — a host-bound Media inherits all four from the host
35
+ * already, and shadowing them with fixed values would only make reads describe
36
+ * an intention rather than what the element is doing.
37
+ *
38
+ * A new src re-resolves the presentation, tearing down the state, SourceBuffers,
39
+ * and in-flight requests the previous one built before the next begins. The
40
+ * engine instance and the attached media element are both kept, so neither has to
41
+ * be rewired.
42
+ *
43
+ * @example
44
+ * class HlsBackgroundVideoMedia extends HlsBackgroundVideoMediaMixin(BackgroundVideoHost) {}
45
+ *
46
+ * const media = new HlsBackgroundVideoMedia();
47
+ * media.attach(document.querySelector('video'));
48
+ * media.src = 'https://stream.mux.com/PLAYBACK_ID.m3u8?max_resolution=720p';
49
+ * media.play();
50
+ */
51
+ declare function HlsBackgroundVideoMediaMixin<Base extends Constructor<any>>(BaseClass: Base): MixinReturn<Base, HlsBackgroundVideoMediaAPI>;
52
+ declare const HlsBackgroundVideoMediaElement_base: MixinReturn<{
53
+ new (): {};
54
+ }, HlsBackgroundVideoMediaAPI>;
55
+ /** Standalone SPF background-video adapter with no base class. */
56
+ declare class HlsBackgroundVideoMediaElement extends HlsBackgroundVideoMediaElement_base {}
57
+ //#endregion
58
+ export { HlsBackgroundVideoMediaAPI, HlsBackgroundVideoMediaElement, HlsBackgroundVideoMediaMixin, HlsBackgroundVideoMediaProps, hlsBackgroundVideoMediaDefaultProps };
59
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.d.ts","names":[],"sources":["../../../../../src/playback/adapters/hls-background-video/adapter.ts"],"mappings":";;;;UAYiB;EACf;;cAGW,qCAAqC;UAIjC,mCAAmC;WACzC,QAAQ,YAAY,4BAA4B;EACzD,OAAO,cAAc;EACrB;EACA;EACA,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAsCM,6BAA6B,aAAa,kBAAkB,WAAW,OAiIpC,YAAY,MAAM;;;;;cAIxD,uCAAuC"}
@@ -0,0 +1,123 @@
1
+ import { pickTrackUnderPixelArea } from "../../../media/primitives/select-tracks.js";
2
+ import { createBackgroundVideoEngine } from "../../engines/hls/engine-background-video.js";
3
+ //#region src/playback/adapters/hls-background-video/adapter.ts
4
+ const hlsBackgroundVideoMediaDefaultProps = { src: "" };
5
+ /**
6
+ * Mixin that adds the background-video SPF playback engine to any base class,
7
+ * for an HLS URL.
8
+ *
9
+ * `src` is the whole surface, and the picker always pins the top rendition on
10
+ * offer. There is no cap of its own because the manifest is the better place to
11
+ * narrow one: a delivery param — `?max_resolution=720p` on a Mux stream URL, for
12
+ * one — keeps the renditions it excludes out of the manifest entirely, rather
13
+ * than fetched-then-unpicked. Deriving a cap from the screen instead is on the
14
+ * roadmap, and lands here when it does.
15
+ *
16
+ * `@videojs/spf/mux-background-video` is this same Media under a Mux-flavored
17
+ * name — an alias, not a variant. Nothing about the surface changes with the
18
+ * import path.
19
+ *
20
+ * Everything else the use case fixes rather than exposes: video-only, looping,
21
+ * muted, autoplaying, loading as soon as there is a source. `attach` writes that
22
+ * onto the element and nothing here declares `loop` / `muted` / `autoplay` /
23
+ * `preload` of its own — a host-bound Media inherits all four from the host
24
+ * already, and shadowing them with fixed values would only make reads describe
25
+ * an intention rather than what the element is doing.
26
+ *
27
+ * A new src re-resolves the presentation, tearing down the state, SourceBuffers,
28
+ * and in-flight requests the previous one built before the next begins. The
29
+ * engine instance and the attached media element are both kept, so neither has to
30
+ * be rewired.
31
+ *
32
+ * @example
33
+ * class HlsBackgroundVideoMedia extends HlsBackgroundVideoMediaMixin(BackgroundVideoHost) {}
34
+ *
35
+ * const media = new HlsBackgroundVideoMedia();
36
+ * media.attach(document.querySelector('video'));
37
+ * media.src = 'https://stream.mux.com/PLAYBACK_ID.m3u8?max_resolution=720p';
38
+ * media.play();
39
+ */
40
+ function HlsBackgroundVideoMediaMixin(BaseClass) {
41
+ class HlsBackgroundVideoMediaImpl extends BaseClass {
42
+ #engine;
43
+ #config;
44
+ #signals;
45
+ /** Pending loadstart listener from a deferred play() retry, if any. */
46
+ #loadstartListener = null;
47
+ constructor(...args) {
48
+ super(...args);
49
+ const { config } = args?.[0] ?? {};
50
+ this.#config = config;
51
+ this.#engine = this.#createEngine();
52
+ }
53
+ get engine() {
54
+ return this.#engine;
55
+ }
56
+ attach(mediaElement) {
57
+ super.attach?.(mediaElement);
58
+ mediaElement.loop = true;
59
+ mediaElement.muted = true;
60
+ mediaElement.autoplay = true;
61
+ mediaElement.preload = "auto";
62
+ this.#signals.context.mediaElement.set(mediaElement);
63
+ }
64
+ detach() {
65
+ this.#cancelPendingPlay();
66
+ this.#signals.context.mediaElement.set(void 0);
67
+ super.detach?.();
68
+ }
69
+ destroy() {
70
+ this.#cancelPendingPlay();
71
+ this.#engine.destroy();
72
+ }
73
+ get src() {
74
+ return this.#signals.state.presentation.get()?.url ?? "";
75
+ }
76
+ set src(value) {
77
+ if (value === this.src) return;
78
+ this.#cancelPendingPlay();
79
+ this.#signals.state.presentation.set(value ? { url: value } : void 0);
80
+ }
81
+ async play() {
82
+ const mediaElement = this.#signals.context.mediaElement.get();
83
+ if (!mediaElement) return Promise.reject(/* @__PURE__ */ new Error("HlsBackgroundVideoMediaElement: no media element attached"));
84
+ try {
85
+ return await mediaElement.play();
86
+ } catch (err) {
87
+ if (this.src) return new Promise((resolve, reject) => {
88
+ const listener = () => {
89
+ this.#loadstartListener = null;
90
+ mediaElement.play().then(resolve, reject);
91
+ };
92
+ this.#loadstartListener = listener;
93
+ mediaElement.addEventListener("loadstart", listener, { once: true });
94
+ });
95
+ throw err;
96
+ }
97
+ }
98
+ #createEngine() {
99
+ const adapterPicker = (presentation) => {
100
+ return pickTrackUnderPixelArea((presentation.selectionSets?.find((s) => s.type === "video"))?.switchingSets[0]?.tracks ?? [])?.id;
101
+ };
102
+ return createBackgroundVideoEngine({
103
+ picker: adapterPicker,
104
+ ...this.#config,
105
+ onSignalsReady: (signals) => {
106
+ this.#signals = signals;
107
+ }
108
+ });
109
+ }
110
+ #cancelPendingPlay() {
111
+ if (!this.#loadstartListener) return;
112
+ this.#signals.context.mediaElement.get()?.removeEventListener("loadstart", this.#loadstartListener);
113
+ this.#loadstartListener = null;
114
+ }
115
+ }
116
+ return HlsBackgroundVideoMediaImpl;
117
+ }
118
+ /** Standalone SPF background-video adapter with no base class. */
119
+ var HlsBackgroundVideoMediaElement = class extends HlsBackgroundVideoMediaMixin(class {}) {};
120
+ //#endregion
121
+ export { HlsBackgroundVideoMediaElement, HlsBackgroundVideoMediaMixin, hlsBackgroundVideoMediaDefaultProps };
122
+
123
+ //# sourceMappingURL=adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.js","names":["#config","#engine","#createEngine","#signals","#cancelPendingPlay","#loadstartListener"],"sources":["../../../../../src/playback/adapters/hls-background-video/adapter.ts"],"sourcesContent":["import type { Constructor, MixinReturn } from '@videojs/utils/types';\nimport type { Composition } from '../../../core/composition/create-composition';\nimport { pickTrackUnderPixelArea, type TrackPicker } from '../../../media/primitives/select-tracks';\nimport type { VideoSelectionSet } from '../../../media/types';\nimport {\n type BackgroundVideoEngineConfig,\n type BackgroundVideoEngineContext,\n type BackgroundVideoEngineSignals,\n type BackgroundVideoEngineState,\n createBackgroundVideoEngine,\n} from '../../engines/hls/engine-background-video';\n\nexport interface HlsBackgroundVideoMediaProps {\n src: string;\n}\n\nexport const hlsBackgroundVideoMediaDefaultProps: HlsBackgroundVideoMediaProps = {\n src: '',\n};\n\nexport interface HlsBackgroundVideoMediaAPI extends HlsBackgroundVideoMediaProps {\n readonly engine: Composition<BackgroundVideoEngineState, BackgroundVideoEngineContext>;\n attach(mediaElement: HTMLMediaElement): void;\n detach(): void;\n destroy(): void;\n play(): Promise<void>;\n}\n\n/**\n * Mixin that adds the background-video SPF playback engine to any base class,\n * for an HLS URL.\n *\n * `src` is the whole surface, and the picker always pins the top rendition on\n * offer. There is no cap of its own because the manifest is the better place to\n * narrow one: a delivery param — `?max_resolution=720p` on a Mux stream URL, for\n * one — keeps the renditions it excludes out of the manifest entirely, rather\n * than fetched-then-unpicked. Deriving a cap from the screen instead is on the\n * roadmap, and lands here when it does.\n *\n * `@videojs/spf/mux-background-video` is this same Media under a Mux-flavored\n * name — an alias, not a variant. Nothing about the surface changes with the\n * import path.\n *\n * Everything else the use case fixes rather than exposes: video-only, looping,\n * muted, autoplaying, loading as soon as there is a source. `attach` writes that\n * onto the element and nothing here declares `loop` / `muted` / `autoplay` /\n * `preload` of its own — a host-bound Media inherits all four from the host\n * already, and shadowing them with fixed values would only make reads describe\n * an intention rather than what the element is doing.\n *\n * A new src re-resolves the presentation, tearing down the state, SourceBuffers,\n * and in-flight requests the previous one built before the next begins. The\n * engine instance and the attached media element are both kept, so neither has to\n * be rewired.\n *\n * @example\n * class HlsBackgroundVideoMedia extends HlsBackgroundVideoMediaMixin(BackgroundVideoHost) {}\n *\n * const media = new HlsBackgroundVideoMedia();\n * media.attach(document.querySelector('video'));\n * media.src = 'https://stream.mux.com/PLAYBACK_ID.m3u8?max_resolution=720p';\n * media.play();\n */\nexport function HlsBackgroundVideoMediaMixin<Base extends Constructor<any>>(BaseClass: Base) {\n class HlsBackgroundVideoMediaImpl extends BaseClass {\n #engine: Composition<BackgroundVideoEngineState, BackgroundVideoEngineContext>;\n #config: BackgroundVideoEngineConfig;\n #signals!: BackgroundVideoEngineSignals;\n\n /** Pending loadstart listener from a deferred play() retry, if any. */\n #loadstartListener: (() => void) | null = null;\n\n constructor(...args: any[]) {\n super(...args);\n\n const { config } = args?.[0] ?? {};\n this.#config = config;\n this.#engine = this.#createEngine();\n }\n\n get engine(): Composition<BackgroundVideoEngineState, BackgroundVideoEngineContext> {\n return this.#engine;\n }\n\n // -------------------------------------------------------------------------\n // Media element lifecycle\n // -------------------------------------------------------------------------\n\n attach(mediaElement: HTMLMediaElement): void {\n super.attach?.(mediaElement);\n // The one place the fixed behavior is stated. Muted and autoplay are what\n // let it start without a gesture, loop is the defining behavior, and\n // `preload` says out loud what the engine does regardless — it subtracts\n // preload monitoring and loads from the moment it has a source.\n mediaElement.loop = true;\n mediaElement.muted = true;\n mediaElement.autoplay = true;\n mediaElement.preload = 'auto';\n\n this.#signals.context.mediaElement.set(mediaElement);\n }\n\n detach(): void {\n this.#cancelPendingPlay();\n this.#signals.context.mediaElement.set(undefined);\n super.detach?.();\n }\n\n destroy(): void {\n this.#cancelPendingPlay();\n this.#engine.destroy();\n }\n\n // -------------------------------------------------------------------------\n // src — synchronous IDL attribute (WHATWG §4.8.11.2)\n // -------------------------------------------------------------------------\n\n get src(): string {\n return this.#signals.state.presentation.get()?.url ?? '';\n }\n\n set src(value: string) {\n // Same line the HLS Medias draw: the presentation is set from a fresh\n // object every time, so re-resolving a URL already playing would restart\n // it for no reason.\n if (value === this.src) return;\n\n this.#cancelPendingPlay();\n this.#signals.state.presentation.set(value ? { url: value } : undefined);\n }\n\n // -------------------------------------------------------------------------\n // play() — WHATWG §4.8.11.8\n // Delegates to the attached media element's native play().\n // -------------------------------------------------------------------------\n\n async play(): Promise<void> {\n const mediaElement = this.#signals.context.mediaElement.get();\n if (!mediaElement) {\n return Promise.reject(new Error('HlsBackgroundVideoMediaElement: no media element attached'));\n }\n\n try {\n return await mediaElement.play();\n } catch (err) {\n // If we have a pending HLS source, the rejection may be because MSE\n // hasn't attached a blob URL yet. Wait for loadstart (src assigned by\n // MSE setup) and retry once.\n if (this.src) {\n return new Promise<void>((resolve, reject) => {\n const listener = () => {\n this.#loadstartListener = null;\n mediaElement.play().then(resolve, reject);\n };\n this.#loadstartListener = listener;\n mediaElement.addEventListener('loadstart', listener, { once: true });\n });\n }\n throw err;\n }\n }\n\n // -------------------------------------------------------------------------\n // Private\n // -------------------------------------------------------------------------\n\n #createEngine(): Composition<BackgroundVideoEngineState, BackgroundVideoEngineContext> {\n // No cap to apply, so the pick is whichever rendition is largest. Passing\n // no maximum is what makes that the answer, rather than a rule of its own.\n const adapterPicker: TrackPicker = (presentation) => {\n const videoSet = presentation.selectionSets?.find((s) => s.type === 'video') as VideoSelectionSet | undefined;\n const tracks = videoSet?.switchingSets[0]?.tracks ?? [];\n return pickTrackUnderPixelArea(tracks)?.id;\n };\n\n return createBackgroundVideoEngine({\n picker: adapterPicker,\n ...this.#config,\n onSignalsReady: (signals) => {\n this.#signals = signals;\n },\n });\n }\n\n #cancelPendingPlay(): void {\n if (!this.#loadstartListener) return;\n const mediaElement = this.#signals.context.mediaElement.get();\n mediaElement?.removeEventListener('loadstart', this.#loadstartListener);\n this.#loadstartListener = null;\n }\n }\n\n return HlsBackgroundVideoMediaImpl as unknown as MixinReturn<Base, HlsBackgroundVideoMediaAPI>;\n}\n\n/** Standalone SPF background-video adapter with no base class. */\nexport class HlsBackgroundVideoMediaElement extends HlsBackgroundVideoMediaMixin(class {}) {}\n"],"mappings":";;;AAgBA,MAAa,sCAAoE,EAC/E,KAAK,GACP;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6CA,SAAgB,6BAA4D,WAAiB;CAC3F,MAAM,oCAAoC,UAAU;EAClD;EACA;EACA;;EAGA,qBAA0C;EAE1C,YAAY,GAAG,MAAa;GAC1B,MAAM,GAAG,IAAI;GAEb,MAAM,EAAE,WAAW,OAAO,MAAM,CAAC;GACjC,KAAKA,UAAU;GACf,KAAKC,UAAU,KAAKC,cAAc;EACpC;EAEA,IAAI,SAAgF;GAClF,OAAO,KAAKD;EACd;EAMA,OAAO,cAAsC;GAC3C,MAAM,SAAS,YAAY;GAK3B,aAAa,OAAO;GACpB,aAAa,QAAQ;GACrB,aAAa,WAAW;GACxB,aAAa,UAAU;GAEvB,KAAKE,SAAS,QAAQ,aAAa,IAAI,YAAY;EACrD;EAEA,SAAe;GACb,KAAKC,mBAAmB;GACxB,KAAKD,SAAS,QAAQ,aAAa,IAAI,KAAA,CAAS;GAChD,MAAM,SAAS;EACjB;EAEA,UAAgB;GACd,KAAKC,mBAAmB;GACxB,KAAKH,QAAQ,QAAQ;EACvB;EAMA,IAAI,MAAc;GAChB,OAAO,KAAKE,SAAS,MAAM,aAAa,IAAI,CAAC,EAAE,OAAO;EACxD;EAEA,IAAI,IAAI,OAAe;GAIrB,IAAI,UAAU,KAAK,KAAK;GAExB,KAAKC,mBAAmB;GACxB,KAAKD,SAAS,MAAM,aAAa,IAAI,QAAQ,EAAE,KAAK,MAAM,IAAI,KAAA,CAAS;EACzE;EAOA,MAAM,OAAsB;GAC1B,MAAM,eAAe,KAAKA,SAAS,QAAQ,aAAa,IAAI;GAC5D,IAAI,CAAC,cACH,OAAO,QAAQ,uBAAO,IAAI,MAAM,2DAA2D,CAAC;GAG9F,IAAI;IACF,OAAO,MAAM,aAAa,KAAK;GACjC,SAAS,KAAK;IAIZ,IAAI,KAAK,KACP,OAAO,IAAI,SAAe,SAAS,WAAW;KAC5C,MAAM,iBAAiB;MACrB,KAAKE,qBAAqB;MAC1B,aAAa,KAAK,CAAC,CAAC,KAAK,SAAS,MAAM;KAC1C;KACA,KAAKA,qBAAqB;KAC1B,aAAa,iBAAiB,aAAa,UAAU,EAAE,MAAM,KAAK,CAAC;IACrE,CAAC;IAEH,MAAM;GACR;EACF;EAMA,gBAAuF;GAGrF,MAAM,iBAA8B,iBAAiB;IAGnD,OAAO,yBAFU,aAAa,eAAe,MAAM,MAAM,EAAE,SAAS,OAAO,EAAA,EAClD,cAAc,EAAE,EAAE,UAAU,CAAC,CACjB,CAAC,EAAE;GAC1C;GAEA,OAAO,4BAA4B;IACjC,QAAQ;IACR,GAAG,KAAKL;IACR,iBAAiB,YAAY;KAC3B,KAAKG,WAAW;IAClB;GACF,CAAC;EACH;EAEA,qBAA2B;GACzB,IAAI,CAAC,KAAKE,oBAAoB;GAE9B,KAD0BF,SAAS,QAAQ,aAAa,IAC7C,CAAC,EAAE,oBAAoB,aAAa,KAAKE,kBAAkB;GACtE,KAAKA,qBAAqB;EAC5B;CACF;CAEA,OAAO;AACT;;AAGA,IAAa,iCAAb,cAAoD,6BAA6B,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC"}
@@ -0,0 +1,38 @@
1
+ //#region src/playback/adapters/hls-background-video/host.d.ts
2
+ /**
3
+ * The `<video>` binding a background video uses: somewhere to keep the attached
4
+ * element, and the four properties the Media fixes on it.
5
+ *
6
+ * That is the whole surface anything here reaches. The engine drives playback,
7
+ * the element exposes `src` and nothing else, and the background player
8
+ * subscribes to no store features, so nothing asks a background video for
9
+ * `currentTime`, `play()`, picture-in-picture, fullscreen, forwarded events, or
10
+ * media components. Holding the host to what is reachable is what keeps this
11
+ * entry free of `@videojs/media`, and small enough to suit a component whose
12
+ * whole pitch is its size.
13
+ *
14
+ * An `EventTarget`, because a Media is one: the store and the element layers
15
+ * take anything they attach as an event source, and inheriting the three methods
16
+ * satisfies that for free rather than by stubbing them.
17
+ *
18
+ * ⚠️ It still implements too little to carry the rest of the media surface, which
19
+ * bounds what may consume it: a store feature reading a property outside this set
20
+ * gets `undefined` rather than an error. A Media needing more of that surface
21
+ * belongs on a host that provides it.
22
+ */
23
+ declare class BackgroundVideoHost extends EventTarget {
24
+ #private;
25
+ attach(target: HTMLVideoElement): void;
26
+ detach(): void;
27
+ get loop(): boolean;
28
+ set loop(value: boolean);
29
+ get muted(): boolean;
30
+ set muted(value: boolean);
31
+ get autoplay(): boolean;
32
+ set autoplay(value: boolean);
33
+ get preload(): HTMLVideoElement['preload'];
34
+ set preload(value: HTMLVideoElement['preload']);
35
+ }
36
+ //#endregion
37
+ export { BackgroundVideoHost };
38
+ //# sourceMappingURL=host.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host.d.ts","names":[],"sources":["../../../../../src/playback/adapters/hls-background-video/host.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;cAqBa,4BAA4B;;EAGvC,OAAO,QAAQ;EAKf;MAOI;MAIA,KAAK;MAIL;MAIA,MAAM;MAIN;MAIA,SAAS;MAIT,WAAW;MAIX,QAAQ,OAAO"}
@@ -0,0 +1,60 @@
1
+ //#region src/playback/adapters/hls-background-video/host.ts
2
+ /**
3
+ * The `<video>` binding a background video uses: somewhere to keep the attached
4
+ * element, and the four properties the Media fixes on it.
5
+ *
6
+ * That is the whole surface anything here reaches. The engine drives playback,
7
+ * the element exposes `src` and nothing else, and the background player
8
+ * subscribes to no store features, so nothing asks a background video for
9
+ * `currentTime`, `play()`, picture-in-picture, fullscreen, forwarded events, or
10
+ * media components. Holding the host to what is reachable is what keeps this
11
+ * entry free of `@videojs/media`, and small enough to suit a component whose
12
+ * whole pitch is its size.
13
+ *
14
+ * An `EventTarget`, because a Media is one: the store and the element layers
15
+ * take anything they attach as an event source, and inheriting the three methods
16
+ * satisfies that for free rather than by stubbing them.
17
+ *
18
+ * ⚠️ It still implements too little to carry the rest of the media surface, which
19
+ * bounds what may consume it: a store feature reading a property outside this set
20
+ * gets `undefined` rather than an error. A Media needing more of that surface
21
+ * belongs on a host that provides it.
22
+ */
23
+ var BackgroundVideoHost = class extends EventTarget {
24
+ #target = null;
25
+ attach(target) {
26
+ if (!target || this.#target === target) return;
27
+ this.#target = target;
28
+ }
29
+ detach() {
30
+ this.#target = null;
31
+ }
32
+ get loop() {
33
+ return this.#target?.loop ?? false;
34
+ }
35
+ set loop(value) {
36
+ if (this.#target) this.#target.loop = value;
37
+ }
38
+ get muted() {
39
+ return this.#target?.muted ?? false;
40
+ }
41
+ set muted(value) {
42
+ if (this.#target) this.#target.muted = value;
43
+ }
44
+ get autoplay() {
45
+ return this.#target?.autoplay ?? false;
46
+ }
47
+ set autoplay(value) {
48
+ if (this.#target) this.#target.autoplay = value;
49
+ }
50
+ get preload() {
51
+ return this.#target?.preload ?? "metadata";
52
+ }
53
+ set preload(value) {
54
+ if (this.#target) this.#target.preload = value;
55
+ }
56
+ };
57
+ //#endregion
58
+ export { BackgroundVideoHost };
59
+
60
+ //# sourceMappingURL=host.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host.js","names":["#target"],"sources":["../../../../../src/playback/adapters/hls-background-video/host.ts"],"sourcesContent":["/**\n * The `<video>` binding a background video uses: somewhere to keep the attached\n * element, and the four properties the Media fixes on it.\n *\n * That is the whole surface anything here reaches. The engine drives playback,\n * the element exposes `src` and nothing else, and the background player\n * subscribes to no store features, so nothing asks a background video for\n * `currentTime`, `play()`, picture-in-picture, fullscreen, forwarded events, or\n * media components. Holding the host to what is reachable is what keeps this\n * entry free of `@videojs/media`, and small enough to suit a component whose\n * whole pitch is its size.\n *\n * An `EventTarget`, because a Media is one: the store and the element layers\n * take anything they attach as an event source, and inheriting the three methods\n * satisfies that for free rather than by stubbing them.\n *\n * ⚠️ It still implements too little to carry the rest of the media surface, which\n * bounds what may consume it: a store feature reading a property outside this set\n * gets `undefined` rather than an error. A Media needing more of that surface\n * belongs on a host that provides it.\n */\nexport class BackgroundVideoHost extends EventTarget {\n #target: HTMLVideoElement | null = null;\n\n attach(target: HTMLVideoElement): void {\n if (!target || this.#target === target) return;\n this.#target = target;\n }\n\n detach(): void {\n this.#target = null;\n }\n\n // Read back off the element rather than stored, so they describe what is\n // playing instead of what was asked for — see the adapter's note on why the\n // Media declares none of these itself.\n get loop(): boolean {\n return this.#target?.loop ?? false;\n }\n\n set loop(value: boolean) {\n if (this.#target) this.#target.loop = value;\n }\n\n get muted(): boolean {\n return this.#target?.muted ?? false;\n }\n\n set muted(value: boolean) {\n if (this.#target) this.#target.muted = value;\n }\n\n get autoplay(): boolean {\n return this.#target?.autoplay ?? false;\n }\n\n set autoplay(value: boolean) {\n if (this.#target) this.#target.autoplay = value;\n }\n\n get preload(): HTMLVideoElement['preload'] {\n return this.#target?.preload ?? 'metadata';\n }\n\n set preload(value: HTMLVideoElement['preload']) {\n if (this.#target) this.#target.preload = value;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAqBA,IAAa,sBAAb,cAAyC,YAAY;CACnD,UAAmC;CAEnC,OAAO,QAAgC;EACrC,IAAI,CAAC,UAAU,KAAKA,YAAY,QAAQ;EACxC,KAAKA,UAAU;CACjB;CAEA,SAAe;EACb,KAAKA,UAAU;CACjB;CAKA,IAAI,OAAgB;EAClB,OAAO,KAAKA,SAAS,QAAQ;CAC/B;CAEA,IAAI,KAAK,OAAgB;EACvB,IAAI,KAAKA,SAAS,KAAKA,QAAQ,OAAO;CACxC;CAEA,IAAI,QAAiB;EACnB,OAAO,KAAKA,SAAS,SAAS;CAChC;CAEA,IAAI,MAAM,OAAgB;EACxB,IAAI,KAAKA,SAAS,KAAKA,QAAQ,QAAQ;CACzC;CAEA,IAAI,WAAoB;EACtB,OAAO,KAAKA,SAAS,YAAY;CACnC;CAEA,IAAI,SAAS,OAAgB;EAC3B,IAAI,KAAKA,SAAS,KAAKA,QAAQ,WAAW;CAC5C;CAEA,IAAI,UAAuC;EACzC,OAAO,KAAKA,SAAS,WAAW;CAClC;CAEA,IAAI,QAAQ,OAAoC;EAC9C,IAAI,KAAKA,SAAS,KAAKA,QAAQ,UAAU;CAC3C;AACF"}
@@ -0,0 +1,20 @@
1
+ import { HlsBackgroundVideoMediaAPI } from "./adapter.js";
2
+ import { BackgroundVideoHost } from "./host.js";
3
+ //#region src/playback/adapters/hls-background-video/media.d.ts
4
+ declare const HlsBackgroundVideoMediaBase: import("@videojs/utils/types").MixinReturn<typeof BackgroundVideoHost, HlsBackgroundVideoMediaAPI>;
5
+ /**
6
+ * The background-video Media, bound to a `<video>` through
7
+ * {@link BackgroundVideoHost}.
8
+ *
9
+ * That host carries the attached element and the four properties `attach` fixes,
10
+ * which is all this Media's surface needs, and it is local — so this entry has
11
+ * no `@videojs/media` dependency.
12
+ *
13
+ * No `MediaTracksMixin`, unlike `HlsVideoMedia`: the engine subtracts audio and
14
+ * text entirely and pins one video rendition for the session, so there are no
15
+ * track lists for a consumer to project or switch between.
16
+ */
17
+ declare class HlsBackgroundVideoMedia extends HlsBackgroundVideoMediaBase {}
18
+ //#endregion
19
+ export { HlsBackgroundVideoMedia };
20
+ //# sourceMappingURL=media.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media.d.ts","names":[],"sources":["../../../../../src/playback/adapters/hls-background-video/media.ts"],"mappings":";;;cAGM,4DAA2B,mBAAA,qBAAA;;;;;;;;;;;;;cAcpB,gCAAgC"}
@@ -0,0 +1,21 @@
1
+ import { HlsBackgroundVideoMediaMixin } from "./adapter.js";
2
+ import { BackgroundVideoHost } from "./host.js";
3
+ //#region src/playback/adapters/hls-background-video/media.ts
4
+ const HlsBackgroundVideoMediaBase = HlsBackgroundVideoMediaMixin(BackgroundVideoHost);
5
+ /**
6
+ * The background-video Media, bound to a `<video>` through
7
+ * {@link BackgroundVideoHost}.
8
+ *
9
+ * That host carries the attached element and the four properties `attach` fixes,
10
+ * which is all this Media's surface needs, and it is local — so this entry has
11
+ * no `@videojs/media` dependency.
12
+ *
13
+ * No `MediaTracksMixin`, unlike `HlsVideoMedia`: the engine subtracts audio and
14
+ * text entirely and pins one video rendition for the session, so there are no
15
+ * track lists for a consumer to project or switch between.
16
+ */
17
+ var HlsBackgroundVideoMedia = class extends HlsBackgroundVideoMediaBase {};
18
+ //#endregion
19
+ export { HlsBackgroundVideoMedia };
20
+
21
+ //# sourceMappingURL=media.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media.js","names":[],"sources":["../../../../../src/playback/adapters/hls-background-video/media.ts"],"sourcesContent":["import { HlsBackgroundVideoMediaMixin } from './adapter';\nimport { BackgroundVideoHost } from './host';\n\nconst HlsBackgroundVideoMediaBase = HlsBackgroundVideoMediaMixin(BackgroundVideoHost);\n\n/**\n * The background-video Media, bound to a `<video>` through\n * {@link BackgroundVideoHost}.\n *\n * That host carries the attached element and the four properties `attach` fixes,\n * which is all this Media's surface needs, and it is local — so this entry has\n * no `@videojs/media` dependency.\n *\n * No `MediaTracksMixin`, unlike `HlsVideoMedia`: the engine subtracts audio and\n * text entirely and pins one video rendition for the session, so there are no\n * track lists for a consumer to project or switch between.\n */\nexport class HlsBackgroundVideoMedia extends HlsBackgroundVideoMediaBase {}\n"],"mappings":";;;AAGA,MAAM,8BAA8B,6BAA6B,mBAAmB;;;;;;;;;;;;;AAcpF,IAAa,0BAAb,cAA6C,4BAA4B,CAAC"}