@mattebox/player 0.1.0

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 (164) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE +24 -0
  3. package/README.md +22 -0
  4. package/dist/cdn/mattebox-player.min.js +340 -0
  5. package/dist/controls/bar.d.ts +56 -0
  6. package/dist/controls/bar.d.ts.map +1 -0
  7. package/dist/controls/bar.js +245 -0
  8. package/dist/controls/bar.js.map +1 -0
  9. package/dist/controls/buttons.d.ts +13 -0
  10. package/dist/controls/buttons.d.ts.map +1 -0
  11. package/dist/controls/buttons.js +157 -0
  12. package/dist/controls/buttons.js.map +1 -0
  13. package/dist/controls/control.d.ts +12 -0
  14. package/dist/controls/control.d.ts.map +1 -0
  15. package/dist/controls/control.js +2 -0
  16. package/dist/controls/control.js.map +1 -0
  17. package/dist/controls/cues.d.ts +33 -0
  18. package/dist/controls/cues.d.ts.map +1 -0
  19. package/dist/controls/cues.js +170 -0
  20. package/dist/controls/cues.js.map +1 -0
  21. package/dist/controls/fullscreen.d.ts +21 -0
  22. package/dist/controls/fullscreen.d.ts.map +1 -0
  23. package/dist/controls/fullscreen.js +78 -0
  24. package/dist/controls/fullscreen.js.map +1 -0
  25. package/dist/controls/icons.d.ts +35 -0
  26. package/dist/controls/icons.d.ts.map +1 -0
  27. package/dist/controls/icons.js +76 -0
  28. package/dist/controls/icons.js.map +1 -0
  29. package/dist/controls/keys.d.ts +26 -0
  30. package/dist/controls/keys.d.ts.map +1 -0
  31. package/dist/controls/keys.js +66 -0
  32. package/dist/controls/keys.js.map +1 -0
  33. package/dist/controls/live.d.ts +10 -0
  34. package/dist/controls/live.d.ts.map +1 -0
  35. package/dist/controls/live.js +59 -0
  36. package/dist/controls/live.js.map +1 -0
  37. package/dist/controls/menu.d.ts +34 -0
  38. package/dist/controls/menu.d.ts.map +1 -0
  39. package/dist/controls/menu.js +225 -0
  40. package/dist/controls/menu.js.map +1 -0
  41. package/dist/controls/menus.d.ts +43 -0
  42. package/dist/controls/menus.d.ts.map +1 -0
  43. package/dist/controls/menus.js +262 -0
  44. package/dist/controls/menus.js.map +1 -0
  45. package/dist/controls/options.d.ts +44 -0
  46. package/dist/controls/options.d.ts.map +1 -0
  47. package/dist/controls/options.js +54 -0
  48. package/dist/controls/options.js.map +1 -0
  49. package/dist/controls/pip.d.ts +16 -0
  50. package/dist/controls/pip.d.ts.map +1 -0
  51. package/dist/controls/pip.js +53 -0
  52. package/dist/controls/pip.js.map +1 -0
  53. package/dist/controls/ranges.d.ts +24 -0
  54. package/dist/controls/ranges.d.ts.map +1 -0
  55. package/dist/controls/ranges.js +48 -0
  56. package/dist/controls/ranges.js.map +1 -0
  57. package/dist/controls/screens.d.ts +22 -0
  58. package/dist/controls/screens.d.ts.map +1 -0
  59. package/dist/controls/screens.js +69 -0
  60. package/dist/controls/screens.js.map +1 -0
  61. package/dist/controls/seek.d.ts +52 -0
  62. package/dist/controls/seek.d.ts.map +1 -0
  63. package/dist/controls/seek.js +215 -0
  64. package/dist/controls/seek.js.map +1 -0
  65. package/dist/controls/slider.d.ts +28 -0
  66. package/dist/controls/slider.d.ts.map +1 -0
  67. package/dist/controls/slider.js +145 -0
  68. package/dist/controls/slider.js.map +1 -0
  69. package/dist/controls/speed.d.ts +8 -0
  70. package/dist/controls/speed.d.ts.map +1 -0
  71. package/dist/controls/speed.js +34 -0
  72. package/dist/controls/speed.js.map +1 -0
  73. package/dist/controls/time.d.ts +8 -0
  74. package/dist/controls/time.d.ts.map +1 -0
  75. package/dist/controls/time.js +25 -0
  76. package/dist/controls/time.js.map +1 -0
  77. package/dist/controls/volume.d.ts +8 -0
  78. package/dist/controls/volume.d.ts.map +1 -0
  79. package/dist/controls/volume.js +29 -0
  80. package/dist/controls/volume.js.map +1 -0
  81. package/dist/dom.d.ts +18 -0
  82. package/dist/dom.d.ts.map +1 -0
  83. package/dist/dom.js +45 -0
  84. package/dist/dom.js.map +1 -0
  85. package/dist/element.d.ts +101 -0
  86. package/dist/element.d.ts.map +1 -0
  87. package/dist/element.js +327 -0
  88. package/dist/element.js.map +1 -0
  89. package/dist/es2015/_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/asyncToGenerator.js +27 -0
  90. package/dist/es2015/_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/defineProperty.js +12 -0
  91. package/dist/es2015/_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/objectSpread2.js +25 -0
  92. package/dist/es2015/_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/toPrimitive.js +14 -0
  93. package/dist/es2015/_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/toPropertyKey.js +9 -0
  94. package/dist/es2015/_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/typeof.js +11 -0
  95. package/dist/es2015/controls/bar.js +227 -0
  96. package/dist/es2015/controls/buttons.js +146 -0
  97. package/dist/es2015/controls/cues.js +153 -0
  98. package/dist/es2015/controls/fullscreen.js +54 -0
  99. package/dist/es2015/controls/icons.js +50 -0
  100. package/dist/es2015/controls/keys.js +60 -0
  101. package/dist/es2015/controls/live.js +60 -0
  102. package/dist/es2015/controls/menu.js +226 -0
  103. package/dist/es2015/controls/menus.js +258 -0
  104. package/dist/es2015/controls/options.js +46 -0
  105. package/dist/es2015/controls/pip.js +39 -0
  106. package/dist/es2015/controls/ranges.js +51 -0
  107. package/dist/es2015/controls/screens.js +74 -0
  108. package/dist/es2015/controls/seek.js +203 -0
  109. package/dist/es2015/controls/slider.js +136 -0
  110. package/dist/es2015/controls/speed.js +41 -0
  111. package/dist/es2015/controls/time.js +27 -0
  112. package/dist/es2015/controls/volume.js +30 -0
  113. package/dist/es2015/dom.js +43 -0
  114. package/dist/es2015/element.js +311 -0
  115. package/dist/es2015/index.js +10 -0
  116. package/dist/es2015/namespaces.js +7 -0
  117. package/dist/es2015/panels/drm.js +29 -0
  118. package/dist/es2015/panels/error.js +25 -0
  119. package/dist/es2015/panels/live.js +48 -0
  120. package/dist/es2015/panels/quality.js +52 -0
  121. package/dist/es2015/panels/tracks.js +47 -0
  122. package/dist/es2015/presets.js +46 -0
  123. package/dist/es2015/style.js +347 -0
  124. package/dist/index.d.ts +4 -0
  125. package/dist/index.d.ts.map +1 -0
  126. package/dist/index.js +9 -0
  127. package/dist/index.js.map +1 -0
  128. package/dist/namespaces.d.ts +45 -0
  129. package/dist/namespaces.d.ts.map +1 -0
  130. package/dist/namespaces.js +5 -0
  131. package/dist/namespaces.js.map +1 -0
  132. package/dist/panels/drm.d.ts +8 -0
  133. package/dist/panels/drm.d.ts.map +1 -0
  134. package/dist/panels/drm.js +36 -0
  135. package/dist/panels/drm.js.map +1 -0
  136. package/dist/panels/error.d.ts +13 -0
  137. package/dist/panels/error.d.ts.map +1 -0
  138. package/dist/panels/error.js +23 -0
  139. package/dist/panels/error.js.map +1 -0
  140. package/dist/panels/live.d.ts +3 -0
  141. package/dist/panels/live.d.ts.map +1 -0
  142. package/dist/panels/live.js +56 -0
  143. package/dist/panels/live.js.map +1 -0
  144. package/dist/panels/panel.d.ts +13 -0
  145. package/dist/panels/panel.d.ts.map +1 -0
  146. package/dist/panels/panel.js +2 -0
  147. package/dist/panels/panel.js.map +1 -0
  148. package/dist/panels/quality.d.ts +3 -0
  149. package/dist/panels/quality.d.ts.map +1 -0
  150. package/dist/panels/quality.js +59 -0
  151. package/dist/panels/quality.js.map +1 -0
  152. package/dist/panels/tracks.d.ts +3 -0
  153. package/dist/panels/tracks.d.ts.map +1 -0
  154. package/dist/panels/tracks.js +49 -0
  155. package/dist/panels/tracks.js.map +1 -0
  156. package/dist/presets.d.ts +23 -0
  157. package/dist/presets.d.ts.map +1 -0
  158. package/dist/presets.js +36 -0
  159. package/dist/presets.js.map +1 -0
  160. package/dist/style.d.ts +13 -0
  161. package/dist/style.d.ts.map +1 -0
  162. package/dist/style.js +345 -0
  163. package/dist/style.js.map +1 -0
  164. package/package.json +58 -0
@@ -0,0 +1,311 @@
1
+ import { namespaces } from "./namespaces.js";
2
+ import { ATTRIBUTES, resolve } from "./controls/options.js";
3
+ import { controlBar } from "./controls/bar.js";
4
+ import { drmPanel } from "./panels/drm.js";
5
+ import { errorSurface } from "./panels/error.js";
6
+ import { livePanel } from "./panels/live.js";
7
+ import { qualityPanel } from "./panels/quality.js";
8
+ import { tracksPanel } from "./panels/tracks.js";
9
+ import _asyncToGenerator from "./_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/asyncToGenerator.js";
10
+ import { drmGuard, resolvePreset } from "./presets.js";
11
+ import { STYLE } from "./style.js";
12
+ import _objectSpread2 from "./_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/objectSpread2.js";
13
+ import { createPlayer, matteboxHandler, nativeHandler } from "@mattebox/player-core";
14
+ //#region src/element.ts
15
+ const TAG = "mattebox-player";
16
+ /** Attributes forwarded onto the video as attributes, never as properties. */
17
+ const FORWARDED = [
18
+ "autoplay",
19
+ "muted",
20
+ "poster"
21
+ ];
22
+ /** Everything else the element watches. Changing any of them reloads. */
23
+ const OWN = [
24
+ "src",
25
+ "type",
26
+ "preset",
27
+ "license-url",
28
+ "thumbnails"
29
+ ];
30
+ /**
31
+ * The one attribute whose meaning the element owns: `native` keeps the
32
+ * browser's controls on the video, `custom` swaps them for the bar, `none`
33
+ * leaves the video bare for a page that draws its own. Anything else is
34
+ * `native`. Changing it swaps the bar without touching the session.
35
+ */
36
+ const CONTROLS = "controls";
37
+ /** The bar's knobs as attributes. Changing one rebuilds the bar with the new value. */
38
+ const KNOBS = Object.values(ATTRIBUTES);
39
+ /** Composed in this order, and each one answers null when its namespace is absent. */
40
+ const PANELS = [
41
+ qualityPanel,
42
+ tracksPanel,
43
+ livePanel,
44
+ drmPanel
45
+ ];
46
+ /** Every stage the engine ships. A narrower preset is optimization. */
47
+ const DEFAULT_PRESET = "full";
48
+ /**
49
+ * What `define()` recorded, for the elements the parser creates. It is the
50
+ * one value shared across elements, and the prompt asks for it: a page that
51
+ * writes only markup still has to be able to say what the engine carries.
52
+ */
53
+ let defaults = {};
54
+ /**
55
+ * The interface above merges only `addEventListener` and `removeEventListener`
56
+ * overloads, which the class inherits from HTMLElement. The rule it suppresses
57
+ * guards against properties an interface declares and a constructor never
58
+ * assigns, and there are none.
59
+ */
60
+ var MatteboxPlayerElement = class MatteboxPlayerElement extends HTMLElement {
61
+ static get observedAttributes() {
62
+ return [
63
+ ...FORWARDED,
64
+ ...OWN,
65
+ CONTROLS,
66
+ ...KNOBS
67
+ ];
68
+ }
69
+ /** Registers the element once, and records the stages markup-only pages get. */
70
+ static define(options) {
71
+ if (options !== void 0) defaults = options;
72
+ if (customElements.get(TAG) === void 0) customElements.define(TAG, MatteboxPlayerElement);
73
+ }
74
+ constructor(options) {
75
+ super();
76
+ this.options = options !== null && options !== void 0 ? options : defaults;
77
+ this.panels = [];
78
+ this.controls = null;
79
+ this.focusable = false;
80
+ this.core = null;
81
+ this.current = null;
82
+ this.offs = [];
83
+ this.queue = Promise.resolve();
84
+ this.pending = false;
85
+ this.media = document.createElement("video");
86
+ this.media.controls = true;
87
+ this.stage = document.createElement("div");
88
+ this.stage.setAttribute("part", "stage");
89
+ this.stage.append(document.createElement("slot"));
90
+ this.bar = document.createElement("div");
91
+ this.bar.setAttribute("part", "panels");
92
+ this.surface = errorSurface(() => {
93
+ this.reload();
94
+ });
95
+ const root = this.attachShadow({ mode: "open" });
96
+ const style = document.createElement("style");
97
+ style.textContent = STYLE;
98
+ root.append(style, this.stage, this.bar, this.surface.root);
99
+ }
100
+ /** The media element. Everything the browser already does is here. */
101
+ get video() {
102
+ return this.media;
103
+ }
104
+ /** The core, or null until the first source has been set up: resolving a preset is asynchronous. */
105
+ get player() {
106
+ return this.core;
107
+ }
108
+ /** The engine feeding the video, or null for a native session and before the first load. */
109
+ get engine() {
110
+ var _this$current$engine, _this$current;
111
+ return (_this$current$engine = (_this$current = this.current) === null || _this$current === void 0 ? void 0 : _this$current.engine) !== null && _this$current$engine !== void 0 ? _this$current$engine : null;
112
+ }
113
+ connectedCallback() {
114
+ if (this.media.parentNode !== this) this.append(this.media);
115
+ for (const name of FORWARDED) this.forward(name);
116
+ this.reload();
117
+ }
118
+ disconnectedCallback() {
119
+ this.enqueue(() => this.teardown());
120
+ }
121
+ attributeChangedCallback(name) {
122
+ if (FORWARDED.includes(name)) {
123
+ this.forward(name);
124
+ return;
125
+ }
126
+ if (name === CONTROLS || KNOBS.includes(name)) {
127
+ this.mode();
128
+ return;
129
+ }
130
+ if (name === "preset") this.enqueue(() => this.discard());
131
+ this.reload();
132
+ }
133
+ /**
134
+ * Applies the `controls` attribute and the knobs: the video's own
135
+ * controls, and the bar rebuilt from scratch. Rebuilding is cheap, the
136
+ * knobs are read once at build, and a session re-attaches in one call.
137
+ */
138
+ mode() {
139
+ const value = this.getAttribute(CONTROLS);
140
+ const custom = value === "custom";
141
+ this.media.controls = !custom && value !== "none";
142
+ this.bar.hidden = custom;
143
+ if (this.controls !== null) {
144
+ this.controls.dispose();
145
+ this.controls = null;
146
+ this.removeAttribute("fullscreen");
147
+ if (this.focusable) this.removeAttribute("tabindex");
148
+ this.focusable = false;
149
+ }
150
+ if (!custom) return;
151
+ if (!this.hasAttribute("tabindex")) {
152
+ this.tabIndex = 0;
153
+ this.focusable = true;
154
+ }
155
+ const knobs = resolve(this.options.controls, (name) => this.getAttribute(name));
156
+ this.controls = controlBar(this, this.stage, this.media, knobs, (name, on) => {
157
+ this.toggleAttribute(name, on);
158
+ }, () => {
159
+ this.reload();
160
+ });
161
+ this.surface.clear();
162
+ this.stage.append(this.controls.root);
163
+ if (this.current !== null) this.controls.attach(this.current);
164
+ }
165
+ forward(name) {
166
+ const value = this.getAttribute(name);
167
+ if (value === null) this.media.removeAttribute(name);
168
+ else this.media.setAttribute(name, value);
169
+ if (name === "muted") this.media.muted = value !== null;
170
+ }
171
+ enqueue(task) {
172
+ this.queue = this.queue.then(task, task).catch(() => void 0);
173
+ }
174
+ /**
175
+ * One run per burst. The parser sets every attribute and then connects the
176
+ * element before any queued task starts, and each of those would load on
177
+ * its own; the flag folds them into the one run that sees the final state.
178
+ */
179
+ reload() {
180
+ if (this.pending) return;
181
+ this.pending = true;
182
+ this.enqueue(() => {
183
+ this.pending = false;
184
+ return this.run();
185
+ });
186
+ }
187
+ emit(name, detail) {
188
+ this.dispatchEvent(new CustomEvent(name, {
189
+ detail,
190
+ bubbles: true,
191
+ composed: true
192
+ }));
193
+ }
194
+ report(error) {
195
+ if (error.fatal) {
196
+ if (this.controls !== null) this.controls.error(error);
197
+ else this.surface.show(error);
198
+ }
199
+ this.emit("error", error);
200
+ }
201
+ run() {
202
+ var _this = this;
203
+ return _asyncToGenerator(function* () {
204
+ var _this$controls, _this$controls2;
205
+ _this.unmount();
206
+ if (!_this.isConnected) return;
207
+ const url = _this.getAttribute("src");
208
+ if (url === null) return;
209
+ const core = yield _this.ensure();
210
+ const type = _this.getAttribute("type");
211
+ const source = type === null ? { url } : {
212
+ url,
213
+ type
214
+ };
215
+ _this.surface.clear();
216
+ (_this$controls = _this.controls) === null || _this$controls === void 0 || _this$controls.clearError();
217
+ let session;
218
+ try {
219
+ session = yield core.load(source);
220
+ } catch (_unused) {
221
+ return;
222
+ }
223
+ if (!_this.isConnected) {
224
+ yield core.unload();
225
+ return;
226
+ }
227
+ _this.current = session;
228
+ _this.configure(session);
229
+ (_this$controls2 = _this.controls) === null || _this$controls2 === void 0 || _this$controls2.attach(session);
230
+ for (const factory of PANELS) {
231
+ const panel = factory(session, _this.media);
232
+ if (panel === null) continue;
233
+ _this.panels.push(panel);
234
+ _this.bar.append(panel.root);
235
+ }
236
+ })();
237
+ }
238
+ ensure() {
239
+ var _this2 = this;
240
+ return _asyncToGenerator(function* () {
241
+ if (_this2.core !== null) return _this2.core;
242
+ const core = createPlayer(_this2.media, { handlers: yield _this2.chain() });
243
+ _this2.offs.push(core.on("sourcechange", (session) => {
244
+ _this2.emit("sourcechange", session);
245
+ }), core.on("error", (error) => {
246
+ _this2.report(error);
247
+ }));
248
+ _this2.core = core;
249
+ return core;
250
+ })();
251
+ }
252
+ chain() {
253
+ var _this3 = this;
254
+ return _asyncToGenerator(function* () {
255
+ var _this$getAttribute;
256
+ if (_this3.options.handlers !== void 0) return _this3.options.handlers;
257
+ if (_this3.options.stages !== void 0) return [matteboxHandler({ stages: _this3.options.stages }), nativeHandler()];
258
+ const name = (_this$getAttribute = _this3.getAttribute("preset")) !== null && _this$getAttribute !== void 0 ? _this$getAttribute : DEFAULT_PRESET;
259
+ const preset = yield resolvePreset(name);
260
+ if (preset === null) {
261
+ _this3.report({
262
+ category: "config",
263
+ code: "CONFIG_INVALID",
264
+ fatal: true,
265
+ recoverable: false,
266
+ handler: null,
267
+ context: { preset: name }
268
+ });
269
+ return [nativeHandler()];
270
+ }
271
+ return [matteboxHandler(_objectSpread2({ preset }, drmGuard())), nativeHandler()];
272
+ })();
273
+ }
274
+ /** What the attributes ask of the session's namespaces, once there is one. */
275
+ configure(session) {
276
+ const engine = session.engine;
277
+ if (engine === null) return;
278
+ const optional = namespaces(engine);
279
+ const license = this.getAttribute("license-url");
280
+ if (license !== null && optional.drm !== void 0) optional.drm.setLicenseUrl(license);
281
+ const track = this.getAttribute("thumbnails");
282
+ if (track !== null && optional.thumbnails !== void 0) optional.thumbnails.load(track).catch(() => void 0);
283
+ }
284
+ unmount() {
285
+ var _this$controls3;
286
+ (_this$controls3 = this.controls) === null || _this$controls3 === void 0 || _this$controls3.detach();
287
+ for (const panel of this.panels) panel.dispose();
288
+ this.panels = [];
289
+ this.bar.replaceChildren();
290
+ this.current = null;
291
+ }
292
+ teardown() {
293
+ var _this4 = this;
294
+ return _asyncToGenerator(function* () {
295
+ _this4.unmount();
296
+ if (_this4.core !== null) yield _this4.core.unload();
297
+ })();
298
+ }
299
+ /** Drops the player itself, so the next run builds a chain from the new preset. */
300
+ discard() {
301
+ var _this5 = this;
302
+ return _asyncToGenerator(function* () {
303
+ yield _this5.teardown();
304
+ for (const off of _this5.offs) off();
305
+ _this5.offs = [];
306
+ _this5.core = null;
307
+ })();
308
+ }
309
+ };
310
+ //#endregion
311
+ export { MatteboxPlayerElement };
@@ -0,0 +1,10 @@
1
+ import { LAYOUT } from "./controls/options.js";
2
+ import { MatteboxPlayerElement } from "./element.js";
3
+ //#region src/index.ts
4
+ /**
5
+ * @mattebox/player: the <mattebox-player> custom element. Importing this
6
+ * module registers the element; that is the package's one side effect.
7
+ */
8
+ MatteboxPlayerElement.define();
9
+ //#endregion
10
+ export { LAYOUT, MatteboxPlayerElement };
@@ -0,0 +1,7 @@
1
+ //#region src/namespaces.ts
2
+ /** The optional namespaces on an engine, each present exactly when its stage was composed. */
3
+ function namespaces(engine) {
4
+ return engine;
5
+ }
6
+ //#endregion
7
+ export { namespaces };
@@ -0,0 +1,29 @@
1
+ import { el } from "../dom.js";
2
+ import { namespaces } from "../namespaces.js";
3
+ //#region src/panels/drm.ts
4
+ const drmPanel = (session) => {
5
+ const engine = session.engine;
6
+ if (engine === null) return null;
7
+ const found = namespaces(engine).drm;
8
+ if (found === void 0) return null;
9
+ const drm = found;
10
+ const root = el("div", "panel drm");
11
+ root.append(el("span", "text drm-text", "DRM"));
12
+ const keySystem = el("span", "value drm-key-system");
13
+ root.append(keySystem);
14
+ function render() {
15
+ const name = drm.keySystem;
16
+ keySystem.textContent = name !== null && name !== void 0 ? name : "";
17
+ root.hidden = name === null;
18
+ }
19
+ const off = engine.on("drm:keysystem", render);
20
+ render();
21
+ return {
22
+ root,
23
+ dispose() {
24
+ off();
25
+ }
26
+ };
27
+ };
28
+ //#endregion
29
+ export { drmPanel };
@@ -0,0 +1,25 @@
1
+ import { el } from "../dom.js";
2
+ //#region src/panels/error.ts
3
+ function errorSurface(retry) {
4
+ const root = el("div", "panel error");
5
+ const category = el("span", "value error-category");
6
+ const code = el("span", "value error-code");
7
+ const button = el("button", "button error-retry", "Retry");
8
+ button.type = "button";
9
+ button.addEventListener("click", retry);
10
+ root.append(category, code, button);
11
+ root.hidden = true;
12
+ return {
13
+ root,
14
+ show(error) {
15
+ category.textContent = error.category;
16
+ code.textContent = error.code;
17
+ root.hidden = false;
18
+ },
19
+ clear() {
20
+ root.hidden = true;
21
+ }
22
+ };
23
+ }
24
+ //#endregion
25
+ export { errorSurface };
@@ -0,0 +1,48 @@
1
+ import { el, state } from "../dom.js";
2
+ import { namespaces } from "../namespaces.js";
3
+ //#region src/panels/live.ts
4
+ /**
5
+ * The live badge over `engine.live`. Present only when a live stage is
6
+ * composed, so the factory answers null for VOD-only engines and for native.
7
+ *
8
+ * Latency has no event; it moves with the playhead. The video's `timeupdate`
9
+ * is the clock the browser already runs, so the badge reads on that.
10
+ */
11
+ const livePanel = (session, video) => {
12
+ const engine = session.engine;
13
+ if (engine === null) return null;
14
+ const found = namespaces(engine).live;
15
+ if (found === void 0) return null;
16
+ const live = found;
17
+ const root = el("div", "panel live");
18
+ const badge = el("span", "badge live-badge", "Live");
19
+ const latency = el("span", "value live-latency");
20
+ const button = el("button", "button live-edge-button", "Go to live");
21
+ button.type = "button";
22
+ root.append(badge, latency, button);
23
+ let shown = "";
24
+ function tick() {
25
+ root.hidden = live.edge === null;
26
+ const atEdge = live.atEdge;
27
+ state(root, "panel live", { "at-edge": atEdge });
28
+ button.disabled = atEdge;
29
+ const seconds = live.latency;
30
+ const text = seconds === null ? "" : `${seconds.toFixed(1)}s behind`;
31
+ if (text === shown) return;
32
+ shown = text;
33
+ latency.textContent = text;
34
+ }
35
+ button.addEventListener("click", () => {
36
+ live.seekToEdge();
37
+ });
38
+ video.addEventListener("timeupdate", tick);
39
+ tick();
40
+ return {
41
+ root,
42
+ dispose() {
43
+ video.removeEventListener("timeupdate", tick);
44
+ }
45
+ };
46
+ };
47
+ //#endregion
48
+ export { livePanel };
@@ -0,0 +1,52 @@
1
+ import { el, fill, menu } from "../dom.js";
2
+ //#region src/panels/quality.ts
3
+ const AUTO = "auto";
4
+ /** A rendition's height when the manifest declared one, its bitrate otherwise. */
5
+ function label(rendition) {
6
+ if (rendition.height !== void 0) return `${rendition.height}p`;
7
+ return `${Math.round(rendition.bitrate / 1e3)} kbps`;
8
+ }
9
+ const qualityPanel = (session, video) => {
10
+ const engine = session.engine;
11
+ if (engine === null) return null;
12
+ const quality = engine.quality;
13
+ const root = el("div", "panel quality");
14
+ const [wrapper, select] = menu("quality", "Quality");
15
+ const playing = el("span", "value quality-playing");
16
+ root.append(wrapper, playing);
17
+ let shown = "";
18
+ function render() {
19
+ var _quality$pinned;
20
+ const items = [[AUTO, "Auto"]];
21
+ for (const rendition of quality.renditions) items.push([rendition.id, label(rendition)]);
22
+ fill(select, items, (_quality$pinned = quality.pinned) !== null && _quality$pinned !== void 0 ? _quality$pinned : AUTO);
23
+ root.hidden = quality.renditions.length === 0;
24
+ }
25
+ function tick() {
26
+ const current = quality.playing === null ? "" : label(quality.playing);
27
+ if (current === shown) return;
28
+ shown = current;
29
+ playing.textContent = current;
30
+ }
31
+ select.addEventListener("change", () => {
32
+ if (select.value === AUTO) quality.auto();
33
+ else quality.pin(select.value);
34
+ });
35
+ const offs = [
36
+ engine.on("tracks:changed", render),
37
+ engine.on("quality:constraints-unsatisfiable", render),
38
+ engine.on("quality:pin-unsatisfiable", render)
39
+ ];
40
+ video.addEventListener("timeupdate", tick);
41
+ render();
42
+ tick();
43
+ return {
44
+ root,
45
+ dispose() {
46
+ for (const off of offs) off();
47
+ video.removeEventListener("timeupdate", tick);
48
+ }
49
+ };
50
+ };
51
+ //#endregion
52
+ export { qualityPanel };
@@ -0,0 +1,47 @@
1
+ import { el, fill, menu } from "../dom.js";
2
+ //#region src/panels/tracks.ts
3
+ const OFF = "off";
4
+ /** The language, then the role, then the id: whichever the manifest gave. */
5
+ function label(track) {
6
+ const parts = [track.lang, track.role].filter((part) => part !== void 0);
7
+ return parts.length === 0 ? track.id : parts.join(" · ");
8
+ }
9
+ const tracksPanel = (session) => {
10
+ const engine = session.engine;
11
+ if (engine === null) return null;
12
+ const tracks = engine.tracks;
13
+ const root = el("div", "panel tracks");
14
+ const [audioLabel, audio] = menu("audio", "Audio");
15
+ const [textLabel, text] = menu("text", "Subtitles");
16
+ root.append(audioLabel, textLabel);
17
+ function renderOne(select, wrapper, contentType, off) {
18
+ var _tracks$active$id, _tracks$active;
19
+ const available = tracks.available.filter((track) => track.contentType === contentType);
20
+ const items = off ? [[OFF, "Off"]] : [];
21
+ for (const track of available) items.push([track.id, label(track)]);
22
+ fill(select, items, (_tracks$active$id = (_tracks$active = tracks.active(contentType)) === null || _tracks$active === void 0 ? void 0 : _tracks$active.id) !== null && _tracks$active$id !== void 0 ? _tracks$active$id : OFF);
23
+ wrapper.hidden = available.length === 0;
24
+ return available.length;
25
+ }
26
+ function render() {
27
+ const count = renderOne(audio, audioLabel, "audio", false) + renderOne(text, textLabel, "text", true);
28
+ root.hidden = count === 0;
29
+ }
30
+ audio.addEventListener("change", () => {
31
+ tracks.select(audio.value);
32
+ });
33
+ text.addEventListener("change", () => {
34
+ if (text.value === OFF) tracks.deselect("text");
35
+ else tracks.select(text.value);
36
+ });
37
+ const offs = [engine.on("tracks:changed", render), engine.on("tracks:selected", render)];
38
+ render();
39
+ return {
40
+ root,
41
+ dispose() {
42
+ for (const off of offs) off();
43
+ }
44
+ };
45
+ };
46
+ //#endregion
47
+ export { tracksPanel };
@@ -0,0 +1,46 @@
1
+ import _asyncToGenerator from "./_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/asyncToGenerator.js";
2
+ //#region src/presets.ts
3
+ /** eme-core's teardown calls `element.setMediaKeys(null)` unguarded, so a build without EME throws out of `detach`. */
4
+ const DRM_STAGES = [
5
+ "eme-core",
6
+ "eme-cenc",
7
+ "eme-fairplay"
8
+ ];
9
+ /** The engine's preset matrix: a protocol line, a `-ts` tier, a `-drm` tier, plus `full` and `kernel`. */
10
+ const PRESETS = {
11
+ dash: () => import("mattebox/presets/dash"),
12
+ "dash-drm": () => import("mattebox/presets/dash-drm"),
13
+ dual: () => import("mattebox/presets/dual"),
14
+ "dual-drm": () => import("mattebox/presets/dual-drm"),
15
+ "dual-ts": () => import("mattebox/presets/dual-ts"),
16
+ "dual-ts-drm": () => import("mattebox/presets/dual-ts-drm"),
17
+ full: () => import("mattebox/presets/full"),
18
+ hls: () => import("mattebox/presets/hls"),
19
+ "hls-drm": () => import("mattebox/presets/hls-drm"),
20
+ "hls-ts": () => import("mattebox/presets/hls-ts"),
21
+ "hls-ts-drm": () => import("mattebox/presets/hls-ts-drm"),
22
+ kernel: () => import("mattebox/presets/kernel")
23
+ };
24
+ function resolvePreset(_x) {
25
+ return _resolvePreset.apply(this, arguments);
26
+ }
27
+ function _resolvePreset() {
28
+ _resolvePreset = _asyncToGenerator(function* (name) {
29
+ const engine = globalThis.mattebox;
30
+ if ((engine === null || engine === void 0 ? void 0 : engine.preset) !== void 0) return engine.preset;
31
+ const load = PRESETS[name];
32
+ if (load === void 0) return null;
33
+ try {
34
+ return (yield load()).default;
35
+ } catch (_unused) {
36
+ return null;
37
+ }
38
+ });
39
+ return _resolvePreset.apply(this, arguments);
40
+ }
41
+ /** The DRM stages to leave out of a preset on a browser that cannot use them. */
42
+ function drmGuard() {
43
+ return "setMediaKeys" in HTMLMediaElement.prototype ? {} : { without: DRM_STAGES };
44
+ }
45
+ //#endregion
46
+ export { drmGuard, resolvePreset };