@banou/media-player 0.6.2 → 0.7.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 (119) hide show
  1. package/README.md +148 -3
  2. package/dist/engine/index.d.ts +10 -0
  3. package/dist/engine/index.js +2 -0
  4. package/dist/engine/picture-in-picture.d.ts +27 -0
  5. package/dist/engine/playback.d.ts +45 -0
  6. package/dist/engine/source-buffer.d.ts +12 -0
  7. package/dist/engine/subtitles.d.ts +36 -0
  8. package/dist/engine/thumbnails.d.ts +22 -0
  9. package/dist/engine-Dmc7JSd9.js +470 -0
  10. package/dist/index.d.ts +12 -0
  11. package/dist/index.js +1566 -0
  12. package/dist/react/components/chrome.d.ts +9 -0
  13. package/dist/react/components/control-bar.d.ts +2 -0
  14. package/dist/react/components/overlay.d.ts +4 -0
  15. package/dist/react/components/playback-slider.d.ts +2 -0
  16. package/dist/react/components/progress-bar.d.ts +2 -0
  17. package/dist/react/components/settings.d.ts +2 -0
  18. package/dist/react/components/sound.d.ts +7 -0
  19. package/{build → dist/react}/components/tooltip-display.d.ts +8 -8
  20. package/dist/react/components/volume-slider.d.ts +6 -0
  21. package/dist/react/hooks/use-drag-value.d.ts +23 -0
  22. package/dist/react/hooks/use-picture-in-picture.d.ts +2 -0
  23. package/dist/react/hooks/use-playback.d.ts +6 -0
  24. package/dist/react/hooks/use-thumbnails.d.ts +11 -0
  25. package/dist/react/player.d.ts +25 -0
  26. package/dist/react/source-feature.d.ts +84 -0
  27. package/dist/react/video-player.d.ts +43 -0
  28. package/dist/utils/source.d.ts +30 -0
  29. package/dist/utils/track-label.d.ts +19 -0
  30. package/dist/utils/volume-utils.d.ts +6 -0
  31. package/package.json +49 -37
  32. package/src/lib/engine/index.ts +14 -0
  33. package/src/lib/engine/picture-in-picture.ts +250 -0
  34. package/src/lib/engine/playback.ts +352 -0
  35. package/src/lib/engine/source-buffer.ts +61 -0
  36. package/src/lib/engine/subtitles.ts +210 -0
  37. package/src/lib/engine/thumbnails.ts +137 -0
  38. package/src/lib/globals.d.ts +8 -0
  39. package/src/lib/index.tsx +36 -0
  40. package/src/lib/react/components/chrome.tsx +126 -0
  41. package/src/{components → lib/react/components}/control-bar.tsx +74 -96
  42. package/src/lib/react/components/overlay.tsx +121 -0
  43. package/src/{components → lib/react/components}/playback-slider.tsx +30 -52
  44. package/src/{components → lib/react/components}/progress-bar.tsx +128 -41
  45. package/src/lib/react/components/settings.tsx +391 -0
  46. package/src/lib/react/components/sound.tsx +133 -0
  47. package/src/{components → lib/react/components}/tooltip-display.tsx +11 -8
  48. package/src/{components → lib/react/components}/volume-slider.tsx +32 -46
  49. package/src/lib/react/hooks/use-drag-value.ts +73 -0
  50. package/src/lib/react/hooks/use-picture-in-picture.ts +36 -0
  51. package/src/lib/react/hooks/use-playback.ts +134 -0
  52. package/src/lib/react/hooks/use-thumbnails.ts +77 -0
  53. package/src/lib/react/player.ts +20 -0
  54. package/src/lib/react/source-feature.ts +90 -0
  55. package/src/lib/react/video-player.tsx +119 -0
  56. package/src/lib/utils/source.ts +60 -0
  57. package/src/lib/utils/track-label.ts +36 -0
  58. package/src/lib/utils/volume-utils.ts +13 -0
  59. package/build/components/chrome.d.ts +0 -7
  60. package/build/components/control-bar.d.ts +0 -6
  61. package/build/components/overlay.d.ts +0 -4
  62. package/build/components/playback-slider.d.ts +0 -2
  63. package/build/components/progress-bar.d.ts +0 -1
  64. package/build/components/settings.d.ts +0 -2
  65. package/build/components/sound.d.ts +0 -4
  66. package/build/components/volume-slider.d.ts +0 -6
  67. package/build/index.d.ts +0 -26
  68. package/build/index.js +0 -3416
  69. package/build/main.d.ts +0 -1
  70. package/build/state-machines/data-source.d.ts +0 -27
  71. package/build/state-machines/index.d.ts +0 -34038
  72. package/build/state-machines/media-properties.d.ts +0 -45
  73. package/build/state-machines/media-source.d.ts +0 -34
  74. package/build/state-machines/media.d.ts +0 -8558
  75. package/build/state-machines/subtitles.d.ts +0 -56
  76. package/build/state-machines/thumbnails.d.ts +0 -24
  77. package/build/state-machines/utils.d.ts +0 -26
  78. package/build/utils/actor-utils.d.ts +0 -2
  79. package/build/utils/context.d.ts +0 -14
  80. package/build/utils/index.d.ts +0 -4
  81. package/build/utils/languages.d.ts +0 -260
  82. package/build/utils/mp4box.d.ts +0 -61
  83. package/build/utils/use-local-storage.d.ts +0 -3
  84. package/build/utils/use-scrub.d.ts +0 -11
  85. package/build/utils/volume-utils.d.ts +0 -17
  86. package/build/utils/window-height.d.ts +0 -2
  87. package/src/components/chrome.tsx +0 -95
  88. package/src/components/overlay.tsx +0 -91
  89. package/src/components/settings.tsx +0 -322
  90. package/src/components/sound.tsx +0 -101
  91. package/src/index.tsx +0 -195
  92. package/src/main.tsx +0 -169
  93. package/src/state-machines/data-source.ts +0 -84
  94. package/src/state-machines/index.ts +0 -5
  95. package/src/state-machines/media-properties.ts +0 -82
  96. package/src/state-machines/media-source.ts +0 -129
  97. package/src/state-machines/media.ts +0 -255
  98. package/src/state-machines/subtitles.ts +0 -285
  99. package/src/state-machines/thumbnails.ts +0 -123
  100. package/src/state-machines/utils.ts +0 -94
  101. package/src/utils/actor-utils.ts +0 -19
  102. package/src/utils/context.ts +0 -23
  103. package/src/utils/index.ts +0 -229
  104. package/src/utils/languages.ts +0 -262
  105. package/src/utils/mp4box.ts +0 -74
  106. package/src/utils/use-local-storage.ts +0 -30
  107. package/src/utils/use-scrub.ts +0 -40
  108. package/src/utils/volume-utils.ts +0 -39
  109. package/src/utils/window-height.ts +0 -19
  110. package/src/vite-env.d.ts +0 -1
  111. package/tsconfig.json +0 -28
  112. package/tsconfig.node.json +0 -9
  113. /package/{build → dist}/utils/colors.d.ts +0 -0
  114. /package/{build → dist}/utils/fonts.d.ts +0 -0
  115. /package/{build → dist}/utils/time.d.ts +0 -0
  116. /package/src/{assets → lib/assets}/picture-in-picture.svg +0 -0
  117. /package/src/{utils → lib/utils}/colors.ts +0 -0
  118. /package/src/{utils → lib/utils}/fonts.ts +0 -0
  119. /package/src/{utils → lib/utils}/time.ts +0 -0
package/dist/index.js ADDED
@@ -0,0 +1,1566 @@
1
+ import { i as e, n as t, o as n, s as r, t as i } from "./engine-Dmc7JSd9.js";
2
+ import { useCallback as a, useEffect as o, useMemo as s, useRef as c, useState as l } from "react";
3
+ import { css as u, keyframes as d } from "@emotion/react";
4
+ import { createPlayer as f, useContainerAttach as p, useMediaAttach as m } from "@videojs/react";
5
+ import { definePlayerFeature as h, videoFeatures as g } from "@videojs/core/dom";
6
+ import { Fragment as _, jsx as v, jsxs as y } from "@emotion/react/jsx-runtime";
7
+ import { ChevronLeft as b, ChevronRight as x, Maximize as S, Minimize as C, Pause as w, Play as T, RotateCcw as E, Settings as D, Volume1 as O, Volume2 as k, VolumeX as A } from "react-feather";
8
+ import { Tooltip as ee } from "react-tooltip";
9
+ //#region src/lib/react/source-feature.ts
10
+ var te = {
11
+ indexes: [],
12
+ thumbnails: [],
13
+ subtitleStreams: [],
14
+ selectedSubtitleStream: void 0,
15
+ selectSubtitleStream: () => {},
16
+ audioStreams: [],
17
+ selectedAudioStream: -1,
18
+ selectAudioStream: () => {},
19
+ hideUI: !1,
20
+ setHideUI: () => {},
21
+ togglePictureInPicture: () => {},
22
+ playbackError: null,
23
+ ready: !1,
24
+ setSourceState: () => {}
25
+ }, j = h({
26
+ name: "source",
27
+ state: () => ({ ...te }),
28
+ attach({ set: e }) {
29
+ e({
30
+ setSourceState: (t) => e(t),
31
+ setHideUI: (t) => e({ hideUI: t })
32
+ });
33
+ }
34
+ }), M = f({
35
+ features: [...g, j],
36
+ displayName: "MediaPlayer"
37
+ }), N = M.usePlayer, ne = (t, n, r) => {
38
+ let i = N(), { read: s, size: u, publicPath: d, libavWorkerUrl: f, jassubWorkerUrl: p, jassubWasmUrl: m, jassubLegacyWasmUrl: h, defaultFontUrl: g, bufferSize: _, autoplay: v = !1 } = r, [y, b] = l(void 0), x = c(null), S = c(0), C = c(!1), w = c(s);
39
+ w.current = s;
40
+ let T = c(r.onSeek);
41
+ T.current = r.onSeek;
42
+ let E = c(r.onPlaybackError);
43
+ E.current = r.onPlaybackError;
44
+ let D = a((e) => {
45
+ C.current = !0, i.setSourceState({ selectedSubtitleStream: e }), x.current?.selectSubtitleStream(e);
46
+ }, [i]), O = a((e) => {
47
+ i.setSourceState({ selectedAudioStream: e }), b(e);
48
+ }, [i]), k = N((e) => e.setSourceState);
49
+ o(() => {
50
+ k({
51
+ selectSubtitleStream: D,
52
+ selectAudioStream: O
53
+ });
54
+ }, [
55
+ k,
56
+ D,
57
+ O
58
+ ]), o(() => {
59
+ if (!t || !n || !u || !s) return;
60
+ let r = !1;
61
+ i.setSourceState({
62
+ playbackError: null,
63
+ ready: !1
64
+ });
65
+ let a = (e) => {
66
+ r || (console.error("playback failed", e), i.setSourceState({ playbackError: e }), E.current?.(e));
67
+ };
68
+ return (async () => {
69
+ try {
70
+ let o = await e({
71
+ videoElement: t,
72
+ canvasElement: n,
73
+ read: (e, t) => w.current(e, t),
74
+ length: u,
75
+ publicPath: d,
76
+ libavWorkerUrl: f,
77
+ jassubWorkerUrl: p,
78
+ jassubWasmUrl: m,
79
+ jassubLegacyWasmUrl: h,
80
+ defaultFontUrl: g,
81
+ bufferSize: _,
82
+ audioStreamIndex: y,
83
+ onReady: () => {
84
+ r || (i.setSourceState({ ready: !0 }), S.current > 0 && (t.currentTime = S.current, S.current = 0), v && t.play().catch(() => {}));
85
+ },
86
+ onError: a,
87
+ onRecovered: () => {
88
+ r || i.setSourceState({ playbackError: null });
89
+ },
90
+ onSeek: (e) => T.current?.(e),
91
+ onSubtitleStreams: (e) => {
92
+ r || i.setSourceState({
93
+ subtitleStreams: e,
94
+ ...C.current ? {} : { selectedSubtitleStream: e[0]?.streamIndex }
95
+ });
96
+ },
97
+ onAudioStreams: (e, t) => {
98
+ r || i.setSourceState({
99
+ audioStreams: e,
100
+ selectedAudioStream: t
101
+ });
102
+ }
103
+ });
104
+ if (r) {
105
+ o.destroy();
106
+ return;
107
+ }
108
+ x.current = o, i.setSourceState({ indexes: o.indexes });
109
+ let s = i.selectedSubtitleStream;
110
+ s !== void 0 && o.selectSubtitleStream(s);
111
+ } catch (e) {
112
+ a(e);
113
+ }
114
+ })(), () => {
115
+ r = !0, S.current = t.currentTime, x.current?.destroy(), x.current = null, i.setSourceState({ ready: !1 });
116
+ };
117
+ }, [
118
+ i,
119
+ t,
120
+ n,
121
+ u,
122
+ s,
123
+ d,
124
+ f,
125
+ p,
126
+ m,
127
+ h,
128
+ g,
129
+ _,
130
+ y,
131
+ v
132
+ ]);
133
+ }, re = 5e3, ie = 6e4, P = ({ publicPath: e, workerUrl: n, length: r, read: i, downloadedRanges: a }) => {
134
+ let [u, d] = l([]), f = c(null), p = c(i);
135
+ p.current = i;
136
+ let m = s(() => a?.map(({ startByteOffset: e, endByteOffset: t }) => [e, t]), [a?.map(({ startByteOffset: e, endByteOffset: t }) => `${e}-${t}`).join(",")]), h = c(m);
137
+ return h.current = m, o(() => {
138
+ if (!r || !i) return;
139
+ let a = !1, o = null, s, c = re, l = () => {
140
+ t({
141
+ publicPath: e,
142
+ workerUrl: n,
143
+ length: r,
144
+ read: (e, t) => p.current(e, t),
145
+ onThumbnails: (e) => {
146
+ a || d(e);
147
+ }
148
+ }).then((e) => {
149
+ if (a) {
150
+ e.destroy();
151
+ return;
152
+ }
153
+ o = e, f.current = e, e.update(h.current);
154
+ }, () => {
155
+ a || (s = setTimeout(l, c), c = Math.min(c * 2, ie));
156
+ });
157
+ };
158
+ return l(), () => {
159
+ a = !0, clearTimeout(s), f.current = null, o?.destroy(), d([]);
160
+ };
161
+ }, [
162
+ r,
163
+ e,
164
+ n
165
+ ]), o(() => {
166
+ f.current?.update(m);
167
+ }, [m]), u;
168
+ }, F = (e, t) => {
169
+ let n = N(), r = c(null);
170
+ return o(() => {
171
+ if (!e || !t) return;
172
+ let a = i({
173
+ video: e,
174
+ canvas: t,
175
+ fallback: () => n.togglePictureInPicture()
176
+ });
177
+ return r.current = a, () => {
178
+ a.destroy(), r.current = null;
179
+ };
180
+ }, [
181
+ e,
182
+ t,
183
+ n
184
+ ]), a(() => {
185
+ r.current?.toggle().catch((e) => {
186
+ console.warn("picture in picture was refused", e);
187
+ });
188
+ }, []);
189
+ }, I = {
190
+ headings: {
191
+ large: "\n font-weight: 600;\n font-size: 2.8rem;\n line-height: 3.4rem;\n @media (min-width: 960px) {\n font-size: 3.4rem;\n line-height: 4.1rem;\n }\n ",
192
+ medium: "\n font-weight: 600;\n font-size: 2.4rem;\n line-height: 2.9rem;\n @media (min-width: 960px) {\n font-size: 2.8rem;\n line-height: 3.4rem;\n }\n ",
193
+ small: "\n font-weight: 500;\n font-size: 1.8rem;\n line-height: 1.9rem;\n @media (min-width: 960px) {\n font-size: 1.8rem;\n line-height: 2.2rem;\n }\n @media (min-width: 2560px) {\n font-size: 2.2rem;\n line-height: 2.6rem;\n }\n ",
194
+ extraSmall: "\n font-weight: 500;\n font-size: 1.4rem;\n line-height: 1.7rem;\n @media (min-width: 960px) {\n font-size: 1.6rem;\n line-height: 1.9rem;\n }\n "
195
+ },
196
+ bLarge: {
197
+ bold: "\n font-weight: 600;\n font-size: 1.4rem;\n line-height: 2rem;\n @media (min-width: 960px) {\n font-size: 1.6rem;\n line-height: 2.2rem;\n }\n ",
198
+ medium: "\n font-weight: 500;\n font-size: 1.4rem;\n line-height: 2rem;\n @media (min-width: 960px) {\n font-size: 1.6rem;\n line-height: 2.2rem;\n }\n ",
199
+ regular: "\n font-weight: 400;\n font-size: 1.4rem;\n line-height: 2rem;\n @media (min-width: 960px) {\n font-size: 1.6rem;\n line-height: 2.2rem;\n }\n "
200
+ },
201
+ bMedium: {
202
+ bold: "\n font-weight: 600;\n font-size: 1.2rem;\n line-height: 1.7rem;\n @media (min-width: 960px) {\n font-size: 1.4rem;\n line-height: 2rem;\n }\n ",
203
+ medium: "\n font-weight: 500;\n font-size: 1.2rem;\n line-height: 1.7rem;\n @media (min-width: 960px) {\n font-size: 1.4rem;\n line-height: 2rem;\n }\n ",
204
+ regular: "\n font-weight: 400;\n font-size: 1.2rem;\n line-height: 1.7rem;\n @media (min-width: 960px) {\n font-size: 1.4rem;\n line-height: 2rem;\n }\n "
205
+ },
206
+ bSmall: {
207
+ bold: "\n font-weight: 600;\n font-size: 1rem;\n line-height: 1.4rem;\n @media (min-width: 960px) {\n font-size: 1.2rem;\n line-height: 1.7rem;\n }\n ",
208
+ medium: "\n font-weight: 500;\n font-size: 1rem;\n line-height: 1.4rem;\n @media (min-width: 960px) {\n font-size: 1.2rem;\n line-height: 1.7rem;\n }\n ",
209
+ regular: "\n font-weight: 400;\n font-size: 1rem;\n line-height: 1.4rem;\n @media (min-width: 960px) {\n font-size: 1.2rem;\n line-height: 1.7rem;\n }\n "
210
+ },
211
+ bExtraSmall: {
212
+ bold: "\n font-weight: 600;\n font-size: 0.8rem;\n line-height: 1.1rem;\n @media (min-width: 960px) {\n font-size: 1rem;\n line-height: 1.4rem;\n }\n ",
213
+ medium: "\n font-weight: 500;\n font-size: 0.8rem;\n line-height: 1.1rem;\n @media (min-width: 960px) {\n font-size: 1rem;\n line-height: 1.4rem;\n }\n ",
214
+ regular: "\n font-weight: 400;\n font-size: 0.8rem;\n line-height: 1.1rem;\n @media (min-width: 960px) {\n font-size: 1rem;\n line-height: 1.4rem;\n }\n "
215
+ }
216
+ }, ae = u`
217
+ top: unset !important;
218
+ left: unset !important;
219
+ width: 100%;
220
+ height: 100%;
221
+ margin: auto;
222
+ pointer-events: none;
223
+ `, L = u`
224
+ position: absolute;
225
+ top: 0;
226
+ left: 0;
227
+ width: 100%;
228
+ padding: 1.2rem 1.6rem;
229
+ /* clears a notch once the player is fullscreen; resolves to zero everywhere else */
230
+ padding-top: calc(1.2rem + env(safe-area-inset-top, 0px));
231
+ ${I.headings.small}
232
+ color: white;
233
+ text-shadow: 0 0 4px rgba(0, 0, 0, 1);
234
+ z-index: 2;
235
+ pointer-events: none;
236
+
237
+ /* one line with an ellipsis until there is width to wrap, since a release filename would
238
+ otherwise run to four lines on a phone */
239
+ white-space: nowrap;
240
+ overflow: hidden;
241
+ text-overflow: ellipsis;
242
+
243
+ @media (min-width: 768px) {
244
+ padding: 2.4rem;
245
+ padding-top: calc(2.4rem + env(safe-area-inset-top, 0px));
246
+ white-space: normal;
247
+ overflow: visible;
248
+ }
249
+
250
+ background: linear-gradient(180deg, rgba(0,0,0,0.4) 0%, rgba(0,0,0,0.3) 30%, rgba(0,0,0,0.2) 60%, rgba(0,0,0,0.1) 80%, transparent 100%);
251
+ transition: opacity 0.1s cubic-bezier(.4,0,1,1);
252
+ `, oe = u`
253
+ position: absolute;
254
+ inset: 0;
255
+ z-index: 2;
256
+ display: flex;
257
+ justify-content: center;
258
+ align-items: center;
259
+ pointer-events: none;
260
+
261
+ ::after {
262
+ content: '';
263
+ width: 4rem;
264
+ height: 4rem;
265
+ border-radius: 50%;
266
+ border: 3px solid rgba(255, 255, 255, 0.25);
267
+ border-top-color: #fff;
268
+ animation: ${d`
269
+ to { transform: rotate(360deg); }
270
+ `} 0.8s linear infinite;
271
+ }
272
+ `, se = u`
273
+ position: absolute;
274
+ inset: auto 0 20%;
275
+ z-index: 3;
276
+ padding: 0 2rem;
277
+ text-align: center;
278
+ color: #fff;
279
+ ${I.bLarge.regular}
280
+ text-shadow: 0 0 4px rgba(0, 0, 0, 1);
281
+ pointer-events: none;
282
+ `, ce = (e) => e instanceof Error ? e.message : typeof e == "string" ? e : "Playback failed", le = ({ onCanvasRef: e }) => {
283
+ let t = N((e) => e.title), n = N((e) => e.hideUI), r = N((e) => e.playbackError), i = N((e) => e.ready), a = N((e) => e.size);
284
+ return /* @__PURE__ */ y(_, { children: [
285
+ t ? /* @__PURE__ */ v("div", {
286
+ css: L,
287
+ style: { ...n ? {
288
+ opacity: "0",
289
+ pointerEvents: "none"
290
+ } : {} },
291
+ children: t
292
+ }) : void 0,
293
+ a && !i && !r ? /* @__PURE__ */ v("div", { css: oe }) : void 0,
294
+ r ? /* @__PURE__ */ v("div", {
295
+ css: se,
296
+ children: ce(r)
297
+ }) : void 0,
298
+ /* @__PURE__ */ v("canvas", {
299
+ ref: e,
300
+ css: ae
301
+ })
302
+ ] });
303
+ }, R = 1e-4, z = 1, B = (e) => Math.max(R, Math.min(z, e)) ** 2, V = (e) => Math.max(R, Math.min(z, e)) ** (z / 2), H = (e) => {
304
+ if (e === void 0) return "0:00";
305
+ let t = Math.floor(e / 3600), n = Math.floor(e % 3600 / 60), r = Math.floor(e % 60);
306
+ return t > 0 ? `${t}:${n < 10 ? "0" : ""}${n}:${r < 10 ? "0" : ""}${r}` : `${n}:${r < 10 ? "0" : ""}${r}`;
307
+ }, U = (e, t) => `${H(e)} / ${H(t)}`, W = (e) => u`
308
+ display: flex;
309
+ justify-content: flex-end;
310
+
311
+ border-radius: 0.4rem;
312
+ user-select: none;
313
+
314
+ z-index: 3;
315
+
316
+ * {
317
+ ${I.bMedium.regular}
318
+ }
319
+
320
+ ${e === "sm" && u`
321
+ padding: 0.4rem!important;
322
+ `}
323
+ ${e === "md" && u`
324
+ padding: 0.6rem!important;
325
+ `}
326
+ ${e === "lg" && u`
327
+ padding: 1.2rem!important;
328
+ `}
329
+ `, G = ({ id: e, toolTipText: t, text: n, delayShow: r = 0, closeDelay: i = 0, offset: a = 20, tooltipPlace: o = "top", disabled: s = !1, size: c = "md" }) => /* @__PURE__ */ y(_, { children: [/* @__PURE__ */ v("div", {
330
+ "data-tooltip-id": e,
331
+ "data-open": !0,
332
+ "data-tooltip-offset": a,
333
+ "data-tooltip-delay-show": r,
334
+ "data-tooltip-delay-hide": i,
335
+ "data-tooltip-place": o,
336
+ children: n
337
+ }), !s && /* @__PURE__ */ v(ee, {
338
+ css: W(c),
339
+ id: e,
340
+ noArrow: !0,
341
+ children: t
342
+ })] }), K = ({ ref: e, onChange: t, orientation: n = "horizontal", disabled: r = !1 }) => {
343
+ let [i, s] = l(!1), u = c(t);
344
+ u.current = t;
345
+ let d = c(null), f = a((t, r) => {
346
+ let i = e.current;
347
+ if (!i) return 0;
348
+ let { left: a, right: o, top: s, bottom: c } = i.getBoundingClientRect(), l = n === "horizontal" ? (t - a) / (o - a) : (c - r) / (c - s);
349
+ return Math.min(Math.max(l, 0), 1);
350
+ }, [e, n]), p = a((e) => {
351
+ r || e.button > 0 || (d.current = e.pointerId, e.currentTarget.setPointerCapture?.(e.pointerId), s(!0), u.current(f(e.clientX, e.clientY)));
352
+ }, [r, f]), m = a((e) => {
353
+ d.current === e.pointerId && u.current(f(e.clientX, e.clientY));
354
+ }, [f]), h = a((e) => {
355
+ d.current === e.pointerId && (d.current = null, e.currentTarget.releasePointerCapture?.(e.pointerId), s(!1));
356
+ }, []);
357
+ return o(() => {
358
+ r && (d.current = null, s(!1));
359
+ }, [r]), {
360
+ dragging: i,
361
+ handlers: {
362
+ onPointerDown: p,
363
+ onPointerMove: m,
364
+ onPointerUp: h,
365
+ onPointerCancel: h,
366
+ style: { touchAction: "none" }
367
+ }
368
+ };
369
+ }, ue = u`
370
+ position: relative;
371
+ height: .4rem;
372
+
373
+ --thumbnail-width: 25rem;
374
+ --thumbnail-half: 12.5rem;
375
+
376
+ transition: transform .2s ease-in-out;
377
+
378
+ margin: 0 12px;
379
+ @media (min-width: 768px) {
380
+ margin: 0 16px;
381
+ }
382
+ @media (min-width: 2560px) {
383
+ margin: 0 24px;
384
+ }
385
+
386
+ user-select: none;
387
+ cursor: pointer;
388
+
389
+ :hover {
390
+ .background-bar {
391
+ transform: scaleY(1.5);
392
+ }
393
+ .loaded {
394
+ transform: scaleY(1.5);
395
+ top: -.1rem;
396
+ }
397
+ .play-container {
398
+ transform: scaleY(1.5);
399
+ }
400
+ }
401
+
402
+
403
+ .background-bar {
404
+ position: absolute;
405
+ inset: 0;
406
+ background-color: hsla(0, 100%, 100%, .2);
407
+ }
408
+
409
+ .cursor-time {
410
+ display: flex;
411
+ justify-content: center;
412
+
413
+ text-shadow: 0 0 4px rgba(0, 0, 0, 1);
414
+ ${I.bMedium.bold}
415
+
416
+ position: absolute;
417
+ top: -2.5rem;
418
+ width: 5rem;
419
+
420
+ margin-left: -2.5rem;
421
+ pointer-events: none;
422
+ }
423
+
424
+ .loaded {
425
+ transform-origin: 0 0;
426
+ position: absolute;
427
+ bottom: 0;
428
+ height: .4rem;
429
+ width: 100%;
430
+ .loaded-part {
431
+ transform-origin: 0 0;
432
+ bottom: 0;
433
+ height: .4rem;
434
+ width: 100%;
435
+ position: absolute;
436
+ background-color: hsla(0, 100%, 100%, .4);
437
+ }
438
+ }
439
+
440
+ .play-container {
441
+ position: absolute;
442
+ bottom: 0;
443
+ width: 100%;
444
+ height: .4rem;
445
+ }
446
+
447
+ .play {
448
+ transform-origin: 0 0;
449
+ background-color: #f03;
450
+ position: absolute;
451
+ bottom: 0;
452
+ height: .4rem;
453
+ width: 100%;
454
+ }
455
+
456
+ .padding {
457
+ position: absolute;
458
+ bottom: -7.5px;
459
+ height: 2rem;
460
+ width: 100%;
461
+ }
462
+
463
+ .thumbnail {
464
+ display: flex;
465
+ justify-content: center;
466
+
467
+ position: absolute;
468
+ top: calc(-3.4375rem - var(--thumbnail-width) * 9/16);
469
+ height: calc(var(--thumbnail-width) * 9/16);
470
+ width: var(--thumbnail-width);
471
+
472
+ margin-left: calc(var(--thumbnail-half) * -1);
473
+ pointer-events: none;
474
+
475
+ img {
476
+ border-radius: .4rem;
477
+ box-shadow: 0 0 1rem rgba(0, 0, 0, .5);
478
+
479
+ width: 100%;
480
+ height: 100%;
481
+ object-fit: cover;
482
+ }
483
+ }
484
+
485
+ /* a thumb is far wider than a cursor, so the hit strip grows to 44px and the track to .6rem */
486
+ @media (pointer: coarse) {
487
+ height: .6rem;
488
+
489
+ /* press feedback, coarse only: a finger has no hover, so without this the bar gives no sign
490
+ it is being dragged */
491
+ &.dragging {
492
+ .background-bar, .loaded, .play-container {
493
+ transform: scaleY(1.5);
494
+ }
495
+ }
496
+
497
+ .loaded {
498
+ height: .6rem;
499
+ .loaded-part {
500
+ height: .6rem;
501
+ }
502
+ }
503
+
504
+ .play-container {
505
+ height: .6rem;
506
+ }
507
+
508
+ .play {
509
+ height: .6rem;
510
+ }
511
+
512
+ /* grows upward into the video: centred, it would hang over the control row and win hit
513
+ testing against taps meant for play, mute and fullscreen */
514
+ .padding {
515
+ height: 44px;
516
+ bottom: 0;
517
+ }
518
+ }
519
+
520
+ /* the clamp only holds the preview inside the bar while the bar is the wider of the two, so
521
+ below that the preview is narrowed instead */
522
+ @media (max-width: 480px) {
523
+ --thumbnail-width: 18rem;
524
+ --thumbnail-half: 9rem;
525
+ }
526
+ `, de = () => {
527
+ let e = N(), t = N((e) => e.currentTime), n = N((e) => e.duration), r = N((e) => e.size), i = N((e) => e.downloadedRanges), a = N((e) => e.indexes), u = N((e) => e.thumbnails), d = c(null), [f, p] = l(void 0), [m, h] = l(void 0), g = c(void 0), { dragging: _, handlers: b } = K({
528
+ ref: d,
529
+ onChange: (e) => {
530
+ p(e), g.current !== "mouse" && h(e * n);
531
+ }
532
+ }), x = (e) => {
533
+ g.current = e.pointerType, b.onPointerDown(e);
534
+ }, S = (e) => {
535
+ b.onPointerUp(e), e.pointerType !== "mouse" && h(void 0);
536
+ }, C = (e) => {
537
+ if (!d.current) return;
538
+ let { left: t, right: r } = d.current.getBoundingClientRect();
539
+ return Math.min(Math.max((e - t) / (r - t), 0), 1) * n;
540
+ }, w = (e) => {
541
+ h(C(e.clientX));
542
+ }, T = () => {
543
+ d.current && h(void 0);
544
+ }, E = (e) => n ? e / n * 100 : 0, D = s(() => i?.map((e, t) => {
545
+ if (!r || !n) return null;
546
+ let i = a.filter((t) => e.startByteOffset <= t.pos && t.pos <= e.endByteOffset), o = i.at(0), s = i.at(-1);
547
+ if (!o || !s) return null;
548
+ let c = o.timestamp / n, l = s.timestamp / n, u = Number((l - c).toFixed(2)), d = c * 100;
549
+ return /* @__PURE__ */ v("div", {
550
+ className: "loaded-part",
551
+ style: {
552
+ transform: `scaleX(${u})`,
553
+ marginLeft: `${d}%`
554
+ }
555
+ }, t);
556
+ }) ?? [], [
557
+ n,
558
+ a.length,
559
+ i?.map(({ startByteOffset: e, endByteOffset: t }) => `${e}/${t}`).join(",")
560
+ ]), O = s(() => {
561
+ if (!m || m < 0) return;
562
+ let e = Math.floor(m / 3600), t = Math.floor((m - e * 3600) / 60), n = Math.floor(m - e * 3600 - t * 60);
563
+ return `${e > 0 ? `${e}:` : ""}${t < 10 ? "0" : ""}${t}:${n < 10 ? "0" : ""}${n}`;
564
+ }, [m]);
565
+ o(() => {
566
+ if (f === void 0 || !n) return;
567
+ let t = f * n;
568
+ e.seek(t);
569
+ }, [
570
+ e,
571
+ f,
572
+ n
573
+ ]);
574
+ let k = s(() => !n || typeof t != "number" ? 0 : t / n, [n, t]), A = s(() => {
575
+ if (!(!u.length || !m)) return u.find(({ startTime: e, endTime: t }) => e <= m && m < t);
576
+ }, [u.length, m]);
577
+ return /* @__PURE__ */ y("div", {
578
+ css: ue,
579
+ ref: d,
580
+ className: _ ? "progress-bar dragging" : "progress-bar",
581
+ onMouseMove: w,
582
+ onMouseOut: T,
583
+ children: [
584
+ /* @__PURE__ */ v("div", { className: "background-bar" }),
585
+ m ? /* @__PURE__ */ v("div", {
586
+ className: "cursor-time",
587
+ style: { left: `clamp(1.8rem, ${E(m)}%, calc(100% - 1.8rem))` },
588
+ children: O
589
+ }) : void 0,
590
+ /* @__PURE__ */ v("div", { className: "progress" }),
591
+ /* @__PURE__ */ v("div", {
592
+ className: "loaded",
593
+ children: D
594
+ }),
595
+ /* @__PURE__ */ v("div", { className: "hover" }),
596
+ /* @__PURE__ */ v("div", {
597
+ className: "play-container",
598
+ children: /* @__PURE__ */ v("div", {
599
+ className: "play",
600
+ style: { transform: `scaleX(${k})` }
601
+ })
602
+ }),
603
+ /* @__PURE__ */ v("div", { className: "chapters" }),
604
+ /* @__PURE__ */ v("div", { className: "scrubber" }),
605
+ /* @__PURE__ */ v("div", {
606
+ className: "padding",
607
+ ...b,
608
+ onPointerDown: x,
609
+ onPointerUp: S,
610
+ onPointerCancel: S
611
+ }),
612
+ /* @__PURE__ */ v("div", {
613
+ className: "thumbnail",
614
+ style: { left: `clamp(var(--thumbnail-half), ${E(m ?? 1)}%, calc(100% - var(--thumbnail-half)))` },
615
+ children: A?.url ? /* @__PURE__ */ v("img", { src: A.url }) : void 0
616
+ })
617
+ ]
618
+ });
619
+ }, fe = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20width='24'%20height='24'%20viewBox='0%200%2024%2024'%20fill='none'%20stroke='%23fff'%20stroke-width='2'%20stroke-linecap='round'%20stroke-linejoin='round'%20class='icon%20icon-tabler%20icons-tabler-outline%20icon-tabler-picture-in-picture'%3e%3cpath%20stroke='none'%20d='M0%200h24v24H0z'%20fill='none'%20/%3e%3cpath%20d='M11%2019h-6a2%202%200%200%201%20-2%20-2v-10a2%202%200%200%201%202%20-2h14a2%202%200%200%201%202%202v4'%20/%3e%3cpath%20d='M14%2014m0%201a1%201%200%200%201%201%20-1h5a1%201%200%200%201%201%201v3a1%201%200%200%201%20-1%201h-5a1%201%200%200%201%20-1%20-1z'%20/%3e%3c/svg%3e", pe = (e) => {
620
+ if (!(!e || e === "und")) try {
621
+ return new Intl.DisplayNames([navigator.language || "en"], {
622
+ type: "language",
623
+ fallback: "none"
624
+ }).of(e) || void 0;
625
+ } catch {
626
+ return;
627
+ }
628
+ }, q = (e) => e?.replace(/_/g, " ").trim() || void 0, me = (e) => {
629
+ let t = e.map((e) => pe(e.language) ?? q(e.title) ?? `Track ${e.streamIndex}`), n = t.reduce((e, t) => ({
630
+ ...e,
631
+ [t]: (e[t] ?? 0) + 1
632
+ }), {});
633
+ return e.map((e, r) => {
634
+ let i = t[r], a = q(e.title);
635
+ return {
636
+ track: e,
637
+ label: (n[i] ?? 0) > 1 && a && a !== i ? `${i} (${a})` : i
638
+ };
639
+ });
640
+ }, he = u`
641
+ display: flex;
642
+ align-items: center;
643
+ justify-content: center;
644
+ flex-direction: column;
645
+ width: 100%;
646
+
647
+ .playback-slider {
648
+ display: flex;
649
+ align-items: center;
650
+
651
+ padding: 8px 4px;
652
+ margin: 0 2px;
653
+ /* a finger needs a 44px band to press, so the padding grows while the track stays 3px */
654
+ @media (pointer: coarse) {
655
+ padding: 20px 8px;
656
+ }
657
+
658
+ cursor: pointer;
659
+
660
+ > div {
661
+ position: relative;
662
+
663
+ background: #ccc;
664
+ border-radius: 4px;
665
+
666
+ width: 120px;
667
+ max-width: 100%;
668
+ height: 3px;
669
+ @media (min-width: 768px) {
670
+ height: 4px;
671
+ }
672
+ }
673
+
674
+ .playback-handle {
675
+ position: absolute;
676
+
677
+
678
+ background: #ffffff;
679
+ border-radius: 50%;
680
+ transform: translateX(-50%);
681
+ pointer-events: none;
682
+
683
+ width: 10px;
684
+ height: 10px;
685
+ top: -3.5px;
686
+ @media (min-width: 768px) {
687
+ width: 12px;
688
+ height: 12px;
689
+ top: -4px;
690
+ }
691
+ }
692
+ }
693
+ `, ge = () => {
694
+ let e = N(), t = N((e) => e.playbackRate), n = c(null), r = c(null), i = .25, a = 3 - i, s = (e, t = .05) => Math.round(e / t) * t, l = s(t || 1), u = (e) => (e - i) / a * 100, d = (e) => s(i + e * a), { handlers: f } = K({
695
+ ref: r,
696
+ onChange: (t) => e.setPlaybackRate(d(t))
697
+ }), p = (t) => {
698
+ t.preventDefault();
699
+ let n = .05, r = l;
700
+ r = t.deltaY < 0 ? Math.min(3, r + n) : Math.max(i, r - n), e.setPlaybackRate(r);
701
+ };
702
+ o(() => {
703
+ let e = n.current;
704
+ return e && e.addEventListener("wheel", p, { passive: !1 }), () => {
705
+ e && e.removeEventListener("wheel", p);
706
+ };
707
+ }, [t]);
708
+ let m = u(l);
709
+ return /* @__PURE__ */ y("div", {
710
+ css: he,
711
+ children: [/* @__PURE__ */ y("div", { children: [l.toFixed(2), "x"] }), /* @__PURE__ */ v("div", {
712
+ className: "playback-slider",
713
+ ref: n,
714
+ ...f,
715
+ children: /* @__PURE__ */ v("div", {
716
+ ref: r,
717
+ style: { background: `linear-gradient(to right,
718
+ #fff 0% ${m}%,
719
+ #3A3A3A ${m}% 100%
720
+ )` },
721
+ children: /* @__PURE__ */ v("div", {
722
+ className: "playback-handle",
723
+ style: { left: `${m}%` }
724
+ })
725
+ })
726
+ })]
727
+ });
728
+ }, _e = u`
729
+ position: relative;
730
+
731
+ .settings {
732
+ /* the icon keeps its size, the pressable box grows around it */
733
+ @media (pointer: coarse) {
734
+ box-sizing: border-box;
735
+ justify-content: center;
736
+
737
+ min-width: 44px;
738
+ min-height: 44px;
739
+ }
740
+ }
741
+
742
+ .popover {
743
+ position: absolute;
744
+ /* the gear sits a couple of buttons in from the right edge, so a centred popover hangs off that
745
+ edge on a phone. It anchors to the button instead until the viewport has room to centre it. */
746
+ right: 0;
747
+ transform: none;
748
+
749
+ overflow-y: auto;
750
+ width: 180px;
751
+ max-width: calc(100vw - 24px);
752
+ height: 160px;
753
+ top: -180px;
754
+ @media (min-width: 768px) {
755
+ right: 50%;
756
+ transform: translateX(50%);
757
+ width: 250px;
758
+ height: 160px;
759
+ }
760
+
761
+ display: flex;
762
+ flex-direction: column;
763
+
764
+ border-radius: 8px;
765
+ background-color: rgba(28,28,28,0.95);
766
+ ${I.bMedium.regular}
767
+
768
+ z-index: 4;
769
+
770
+ > div {
771
+ display: flex;
772
+ align-items: center;
773
+ justify-content: space-between;
774
+
775
+ color: #fff;
776
+ outline: none;
777
+
778
+ padding: 8px 6px 8px 12px;
779
+
780
+ width: 100%;
781
+ /* rows keep their own height inside the fixed height column, so a long page scrolls rather than
782
+ squashing every row into it */
783
+ flex-shrink: 0;
784
+ @media (pointer: coarse) {
785
+ box-sizing: border-box;
786
+ min-height: 44px;
787
+ }
788
+
789
+ :first-of-type {
790
+ border-radius: 8px 8px 0 0;
791
+ }
792
+ :last-of-type {
793
+ border-radius: 0 0 8px 8px;
794
+ }
795
+ :not(&.no-hover) {
796
+ cursor: pointer;
797
+ }
798
+ :not(&.no-hover):hover {
799
+ background-color: rgba(255,255,255,.1);
800
+ }
801
+
802
+ > div {
803
+ display: flex;
804
+ align-items: center;
805
+ justify-content: center;
806
+
807
+ .secondary {
808
+ color: #eee;
809
+ ${I.bSmall.regular}
810
+ }
811
+ }
812
+ }
813
+
814
+ .back {
815
+ display: flex;
816
+ align-items: center;
817
+ justify-content: flex-start;
818
+ border-bottom: 1px solid #4D4D4E;
819
+
820
+ svg {
821
+ transform: translateX(-4px);
822
+ transition: transform 0.2s ease-in-out;
823
+ }
824
+
825
+ :hover {
826
+ svg {
827
+ transform: translateX(-8px);
828
+ }
829
+ }
830
+ }
831
+ }
832
+
833
+ .playback-rate {
834
+ .slider {
835
+ width: 100%;
836
+ }
837
+ .options {
838
+ display: grid;
839
+ grid-template-columns: 1fr 1fr 1fr 1fr;
840
+ align-items: center;
841
+
842
+ padding: 8px 12px;
843
+ /* four presets across a 180px popover leave targets 34px wide, so a finger gets two rows of two
844
+ until the popover is wide enough for the row of four */
845
+ @media (pointer: coarse) {
846
+ grid-template-columns: 1fr 1fr;
847
+ }
848
+ @media (pointer: coarse) and (min-width: 768px) {
849
+ grid-template-columns: 1fr 1fr 1fr 1fr;
850
+ }
851
+
852
+ > div {
853
+ display: flex;
854
+ align-items: center;
855
+ border-radius: 4px;
856
+
857
+ padding: 8px 6px;
858
+ height: 100%;
859
+ @media (pointer: coarse) {
860
+ box-sizing: border-box;
861
+ min-height: 44px;
862
+ }
863
+
864
+ cursor: pointer;
865
+ :hover {
866
+ background-color: rgba(255,255,255,.1);
867
+ }
868
+ }
869
+ }
870
+ }
871
+
872
+ .track-list {
873
+ .description {
874
+ word-break: break-word;
875
+ }
876
+ }
877
+ `, J = (e) => me(e).map(({ track: e, label: t }) => ({
878
+ streamIndex: e.streamIndex,
879
+ label: t
880
+ })), Y = ({ title: e, tracks: t, selected: n, onSelect: r, onBack: i, allowDisable: a }) => /* @__PURE__ */ y("div", {
881
+ className: "popover track-list",
882
+ children: [
883
+ /* @__PURE__ */ y("div", {
884
+ className: "back",
885
+ onClick: i,
886
+ children: [/* @__PURE__ */ v(b, {}), /* @__PURE__ */ v("span", { children: e })]
887
+ }),
888
+ a ? /* @__PURE__ */ y("div", {
889
+ onClick: () => r(void 0),
890
+ children: [/* @__PURE__ */ v("span", { children: "Disable" }), /* @__PURE__ */ v("span", { children: n === void 0 ? "✓" : "" })]
891
+ }) : null,
892
+ t.map(({ streamIndex: e, label: t }) => /* @__PURE__ */ y("div", {
893
+ onClick: () => r(e),
894
+ className: "description",
895
+ children: [/* @__PURE__ */ v("span", { children: t }), /* @__PURE__ */ v("span", { children: n === e ? "✓" : "" })]
896
+ }, e))
897
+ ]
898
+ }), ve = () => {
899
+ let e = N(), t = N((e) => e.playbackRate), n = N((e) => e.subtitleStreams), r = N((e) => e.selectedSubtitleStream), i = N((e) => e.selectSubtitleStream), a = N((e) => e.audioStreams), u = N((e) => e.selectedAudioStream), d = N((e) => e.selectAudioStream), [f, p] = l(!1), [m, h] = l(0), g = c(null), _ = () => {
900
+ p(!f), h(0);
901
+ }, S = (t) => {
902
+ e.setPlaybackRate(t), h(0);
903
+ };
904
+ o(() => {
905
+ let e = (e) => {
906
+ f && g.current && !g.current.contains(e.target) && _();
907
+ };
908
+ return document.addEventListener("pointerdown", e), () => document.removeEventListener("pointerdown", e);
909
+ }, [f]);
910
+ let C = s(() => J(n), [n]), w = s(() => J(a), [a]), T = (e) => {
911
+ i(e), _();
912
+ }, E = (e) => {
913
+ e !== void 0 && d(e), _();
914
+ }, O = (e) => () => {
915
+ h(e);
916
+ };
917
+ return /* @__PURE__ */ y("div", {
918
+ css: _e,
919
+ ref: g,
920
+ children: [
921
+ /* @__PURE__ */ v(G, {
922
+ id: "settings",
923
+ disabled: f,
924
+ text: /* @__PURE__ */ v("button", {
925
+ className: "settings",
926
+ type: "button",
927
+ onClick: _,
928
+ children: /* @__PURE__ */ v(D, {})
929
+ }),
930
+ toolTipText: /* @__PURE__ */ v("span", { children: "Settings" })
931
+ }),
932
+ f && m === 0 && /* @__PURE__ */ y("div", {
933
+ className: "popover menu",
934
+ children: [
935
+ w.length > 1 ? /* @__PURE__ */ y("div", {
936
+ onClick: O(3),
937
+ children: [/* @__PURE__ */ v("div", { children: "Audio" }), /* @__PURE__ */ v("div", { children: /* @__PURE__ */ v(x, {}) })]
938
+ }) : null,
939
+ C.length > 0 ? /* @__PURE__ */ y("div", {
940
+ onClick: O(2),
941
+ children: [/* @__PURE__ */ v("div", { children: "Subtitles" }), /* @__PURE__ */ v("div", { children: /* @__PURE__ */ v(x, {}) })]
942
+ }) : null,
943
+ /* @__PURE__ */ y("div", {
944
+ onClick: O(1),
945
+ children: [/* @__PURE__ */ v("div", { children: "Playback speed" }), /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("span", {
946
+ className: "secondary",
947
+ children: t === 1 ? "(Default)" : t.toFixed(2)
948
+ }), /* @__PURE__ */ v(x, {})] })]
949
+ })
950
+ ]
951
+ }),
952
+ f && m === 1 && /* @__PURE__ */ y("div", {
953
+ className: "popover playback-rate",
954
+ children: [
955
+ /* @__PURE__ */ y("div", {
956
+ className: "back",
957
+ onClick: O(0),
958
+ children: [/* @__PURE__ */ v(b, {}), /* @__PURE__ */ v("span", { children: "Playback speed" })]
959
+ }),
960
+ /* @__PURE__ */ v("div", {
961
+ className: "slider no-hover",
962
+ children: /* @__PURE__ */ v(ge, {})
963
+ }),
964
+ /* @__PURE__ */ y("div", {
965
+ className: "options no-hover",
966
+ children: [
967
+ /* @__PURE__ */ v("div", {
968
+ onClick: () => S(.5),
969
+ children: "0.5x"
970
+ }),
971
+ /* @__PURE__ */ v("div", {
972
+ onClick: () => S(1),
973
+ children: "1x"
974
+ }),
975
+ /* @__PURE__ */ v("div", {
976
+ onClick: () => S(1.5),
977
+ children: "1.5x"
978
+ }),
979
+ /* @__PURE__ */ v("div", {
980
+ onClick: () => S(2),
981
+ children: "2x"
982
+ })
983
+ ]
984
+ })
985
+ ]
986
+ }),
987
+ f && m === 2 && /* @__PURE__ */ v(Y, {
988
+ title: "Subtitles",
989
+ tracks: C,
990
+ selected: r,
991
+ onSelect: T,
992
+ onBack: O(0),
993
+ allowDisable: !0
994
+ }),
995
+ f && m === 3 && /* @__PURE__ */ v(Y, {
996
+ title: "Audio",
997
+ tracks: w,
998
+ selected: u,
999
+ onSelect: E,
1000
+ onBack: O(0)
1001
+ })
1002
+ ]
1003
+ });
1004
+ }, ye = {
1005
+ primary: "#EAEBEE",
1006
+ secondary: "#D0D0D9",
1007
+ hover: "rgba(255, 255, 255, 0.13)",
1008
+ borderPrimary: "#384F70",
1009
+ backgroundTooltip: "#222222"
1010
+ }, be = u`
1011
+ display: flex;
1012
+ align-items: center;
1013
+
1014
+ padding: 16px 4px;
1015
+ margin: 0 2px;
1016
+ /* a finger needs a 44px band to press, so the padding grows while the track stays 3px */
1017
+ @media (pointer: coarse) {
1018
+ padding: 22px 4px;
1019
+ }
1020
+
1021
+ cursor: pointer;
1022
+
1023
+ > div {
1024
+ position: relative;
1025
+
1026
+ background: #ccc;
1027
+ border-radius: 4px;
1028
+
1029
+ width: 80px;
1030
+ height: 3px;
1031
+ /* the control bar has under 360px to fit every button once the slider is always open, so the
1032
+ track lends some of its length back on a phone and takes it again on anything wider */
1033
+ @media (pointer: coarse) {
1034
+ width: 64px;
1035
+ }
1036
+ @media (pointer: coarse) and (min-width: 768px) {
1037
+ width: 80px;
1038
+ }
1039
+ }
1040
+
1041
+ .volume-handle {
1042
+ position: absolute;
1043
+ top: -3.5px;
1044
+
1045
+ background: #ffffff;
1046
+ border-radius: 50%;
1047
+ transform: translateX(-50%);
1048
+ pointer-events: none;
1049
+
1050
+ width: 10px;
1051
+ height: 10px;
1052
+ }
1053
+ `, xe = ({ value: e, onChange: t }) => {
1054
+ let n = N((e) => e.volume), r = N((e) => e.muted), i = c(null), a = c(null), { handlers: l } = K({
1055
+ ref: a,
1056
+ onChange: t
1057
+ }), u = (n) => {
1058
+ n.preventDefault();
1059
+ let r = .05, i = e;
1060
+ i = n.deltaY < 0 ? Math.min(1, i + r) : Math.max(0, i - r), t(i);
1061
+ };
1062
+ o(() => {
1063
+ let e = i.current;
1064
+ return e && e.addEventListener("wheel", u, { passive: !1 }), () => {
1065
+ e && e.removeEventListener("wheel", u);
1066
+ };
1067
+ }, [e]);
1068
+ let d = e * 100, f = s(() => Math.round(r ? 0 : V(n) * 100), [n, r]);
1069
+ return /* @__PURE__ */ v(G, {
1070
+ id: "volume-slider",
1071
+ text: /* @__PURE__ */ v("div", {
1072
+ ref: i,
1073
+ css: be,
1074
+ ...l,
1075
+ children: /* @__PURE__ */ v("div", {
1076
+ ref: a,
1077
+ style: { background: `linear-gradient(to right,
1078
+ #fff 0% ${d}%,
1079
+ #3A3A3A ${d}% 100%
1080
+ )` },
1081
+ children: /* @__PURE__ */ v("div", {
1082
+ className: "volume-handle",
1083
+ style: { left: `${e * 100}%` }
1084
+ })
1085
+ })
1086
+ }),
1087
+ toolTipText: /* @__PURE__ */ y("div", {
1088
+ className: "volume-value",
1089
+ children: [f, "%"]
1090
+ })
1091
+ });
1092
+ }, Se = u`
1093
+ position: relative;
1094
+ display: flex;
1095
+ align-items: center;
1096
+
1097
+ .volume-slider-container {
1098
+ display: flex;
1099
+ align-items: center;
1100
+
1101
+ width: 0;
1102
+ margin-right: 0;
1103
+ overflow: hidden;
1104
+ pointer-events: none;
1105
+
1106
+ transition: margin .2s cubic-bezier(0.4,0,1,1), width .2s cubic-bezier(0.4,0,1,1);
1107
+ -webkit-transition: margin .2s cubic-bezier(0.4,0,1,1), width .2s cubic-bezier(0.4,0,1,1);
1108
+
1109
+ .volume-slider-background {
1110
+ display: flex;
1111
+ flex-direction: row;
1112
+ align-items: center;
1113
+
1114
+ border-radius: 4px;
1115
+
1116
+ .volume-value {
1117
+ ${I.bMedium.regular};
1118
+ color: #ffffff;
1119
+ }
1120
+ }
1121
+ }
1122
+
1123
+ &:hover .volume-slider-container {
1124
+ width: 9rem;
1125
+
1126
+ transition: margin .2s cubic-bezier(0,0,0.2,1), width .2s cubic-bezier(0,0,0.2,1);
1127
+ -webkit-transition: margin .2s cubic-bezier(0,0,0.2,1), width .2s cubic-bezier(0,0,0.2,1);
1128
+
1129
+ pointer-events: auto;
1130
+ }
1131
+
1132
+ /* a touch screen has no hover, so the slider stays open there and the button only mutes */
1133
+ @media (pointer: coarse) {
1134
+ /* auto, not a fixed rem: the container keeps overflow: hidden, so a narrower fixed width
1135
+ silently clips the track and makes the clipped part unhittable */
1136
+ .volume-slider-container,
1137
+ &:hover .volume-slider-container {
1138
+ width: auto;
1139
+
1140
+ pointer-events: auto;
1141
+ }
1142
+
1143
+ /* a 44px press target around an 18px icon, floored here because the control bar sets the
1144
+ padding at a higher specificity */
1145
+ .sound {
1146
+ /* border-box explicitly, since a consumer reset cannot be assumed */
1147
+ box-sizing: border-box;
1148
+ justify-content: center;
1149
+
1150
+ min-width: 44px;
1151
+ min-height: 44px;
1152
+ }
1153
+ }
1154
+
1155
+ `, Ce = ({ ref: e }) => {
1156
+ let t = N(), n = N((e) => e.volume), r = N((e) => e.muted), i = s(() => V(n), [n]);
1157
+ return /* @__PURE__ */ y("div", {
1158
+ css: Se,
1159
+ children: [/* @__PURE__ */ v(G, {
1160
+ id: "sound",
1161
+ text: /* @__PURE__ */ v("button", {
1162
+ className: "sound",
1163
+ type: "button",
1164
+ onClick: () => t.toggleMuted(),
1165
+ ref: e,
1166
+ children: v(r || n === 0 ? A : n <= .5 ? O : k, {
1167
+ size: 18,
1168
+ color: "#fff"
1169
+ })
1170
+ }),
1171
+ toolTipText: /* @__PURE__ */ v("span", { children: r ? "Unmute (m)" : "Mute (m)" })
1172
+ }), /* @__PURE__ */ v("div", {
1173
+ className: "volume-slider-container",
1174
+ children: /* @__PURE__ */ v("div", {
1175
+ className: "volume-slider-background",
1176
+ children: /* @__PURE__ */ v(xe, {
1177
+ value: r ? 0 : i,
1178
+ onChange: (e) => {
1179
+ t.setVolume(B(e));
1180
+ }
1181
+ })
1182
+ })
1183
+ })]
1184
+ });
1185
+ }, X = .05, Z = 5, we = u`
1186
+ position: absolute;
1187
+ bottom: 0;
1188
+ width: 100%;
1189
+
1190
+ background: linear-gradient(0deg, rgba(0,0,0,0.6) 0%, rgba(0,0,0,0.45) 20%, rgba(0,0,0,0.3) 40%, rgba(0,0,0,0.15) 70%, transparent 100%);
1191
+ transition: opacity 0.1s cubic-bezier(.4,0,1,1);
1192
+
1193
+ .actions {
1194
+ display: flex;
1195
+ justify-content: space-between;
1196
+
1197
+ /* repeated per breakpoint: nested at-rules are hoisted after the rule, so a shorthand inside a
1198
+ media query would override a longhand above it and silently drop the safe-area inset */
1199
+ padding: 6px calc(12px + env(safe-area-inset-right, 0px)) calc(6px + env(safe-area-inset-bottom, 0px)) calc(12px + env(safe-area-inset-left, 0px));
1200
+ @media (min-width: 768px) {
1201
+ padding: 8px calc(16px + env(safe-area-inset-right, 0px)) calc(8px + env(safe-area-inset-bottom, 0px)) calc(16px + env(safe-area-inset-left, 0px));
1202
+ }
1203
+ @media (min-width: 2560px) {
1204
+ padding: 8px calc(24px + env(safe-area-inset-right, 0px)) calc(8px + env(safe-area-inset-bottom, 0px)) calc(24px + env(safe-area-inset-left, 0px));
1205
+ }
1206
+
1207
+ .left, .right {
1208
+ display: flex;
1209
+ align-items: center;
1210
+
1211
+ button {
1212
+ display: flex;
1213
+ align-items: center;
1214
+
1215
+ outline: none;
1216
+ border: none;
1217
+ background: none;
1218
+
1219
+ svg {
1220
+ width: 18px;
1221
+ height: 18px;
1222
+ @media (min-width: 768px) {
1223
+ width: 24px;
1224
+ height: 24px;
1225
+ }
1226
+ @media (min-width: 2560px) {
1227
+ width: 28px;
1228
+ height: 28px;
1229
+ }
1230
+ }
1231
+
1232
+ svg {
1233
+ stroke: #fff;
1234
+ }
1235
+ }
1236
+
1237
+ .play, .sound, .time, .settings, .picture-in-picture, .full-screen {
1238
+ display: flex;
1239
+ align-items: center;
1240
+
1241
+ height: 100%;
1242
+
1243
+ border-radius: 4px;
1244
+ user-select: none;
1245
+
1246
+ padding: 8px;
1247
+ @media (min-width: 768px) {
1248
+ padding: 8px 12px;
1249
+ }
1250
+ @media (min-width: 2560px) {
1251
+ padding: 8px 12px;
1252
+ }
1253
+ }
1254
+
1255
+ .play, .sound, .settings, .picture-in-picture, .full-screen {
1256
+ border-radius: 4px;
1257
+
1258
+ padding: 8px;
1259
+ @media (min-width: 768px) {
1260
+ padding: 8px 12px;
1261
+ }
1262
+ @media (min-width: 2560px) {
1263
+ padding: 8px 12px;
1264
+ }
1265
+
1266
+ cursor: pointer;
1267
+
1268
+ :hover {
1269
+ background-color: ${ye.hover};
1270
+ }
1271
+
1272
+ /* the hit area grows, the icon does not, keyed on the pointer because a narrow desktop
1273
+ window still has a mouse */
1274
+ @media (pointer: coarse) {
1275
+ /* border-box explicitly, since a consumer reset cannot be assumed */
1276
+ box-sizing: border-box;
1277
+ min-width: 44px;
1278
+ min-height: 44px;
1279
+ justify-content: center;
1280
+ }
1281
+ }
1282
+ }
1283
+
1284
+ .left {
1285
+ .time {
1286
+ ${I.bMedium.regular}
1287
+ text-shadow: 0 0 4px rgba(0, 0, 0, 1);
1288
+ }
1289
+ }
1290
+ .right {
1291
+ .picture-in-picture {
1292
+ img {
1293
+ width: 22px;
1294
+ height: 22px;
1295
+ @media (min-width: 768px) {
1296
+ width: 28px;
1297
+ height: 28px;
1298
+ }
1299
+ @media (min-width: 2560px) {
1300
+ width: 32px;
1301
+ height: 32px;
1302
+ }
1303
+ }
1304
+ }
1305
+ }
1306
+ }
1307
+ `, Te = () => {
1308
+ let e = N(), t = N((e) => e.paused), n = N((e) => e.currentTime), r = N((e) => e.duration), i = N((e) => e.fullscreen), s = N((e) => e.hideUI), c = N((e) => e.togglePictureInPicture), [u, d] = l(null), f = r > 0 && n === r, p = a(({ direction: t, stepSize: n }) => {
1309
+ let r = V(e.volume), i = t === "up" ? n : -n, a = Math.max(0, Math.min(1, r + i)), o = e.muted;
1310
+ e.setVolume(B(a)), o && !e.muted && e.toggleMuted();
1311
+ }, [e]);
1312
+ o(() => {
1313
+ let t = (t) => {
1314
+ let n = t.target;
1315
+ if (n?.isContentEditable || /^(input|textarea|select)$/i.test(n?.tagName ?? "")) return;
1316
+ let r = !0;
1317
+ if (t.key === "f") e.toggleFullscreen();
1318
+ else if (t.key === "k") e.togglePaused();
1319
+ else if (t.key === " ") e.togglePaused();
1320
+ else if (t.key === "m") e.toggleMuted();
1321
+ else if (t.key === "ArrowUp") p({
1322
+ direction: "up",
1323
+ stepSize: X
1324
+ });
1325
+ else if (t.key === "ArrowDown") p({
1326
+ direction: "down",
1327
+ stepSize: X
1328
+ });
1329
+ else if (t.key === "ArrowRight") {
1330
+ if (!e.duration) return;
1331
+ e.seek(Math.min(e.currentTime + Z, e.duration));
1332
+ } else t.key === "ArrowLeft" ? e.seek(Math.max(e.currentTime - Z, 0)) : r = !1;
1333
+ r && t.preventDefault();
1334
+ };
1335
+ return window.addEventListener("keydown", t), () => window.removeEventListener("keydown", t);
1336
+ }, [e, p]);
1337
+ let m = a((e) => {
1338
+ e.preventDefault(), e.deltaY < 0 ? p({
1339
+ direction: "up",
1340
+ stepSize: X
1341
+ }) : p({
1342
+ direction: "down",
1343
+ stepSize: X
1344
+ });
1345
+ }, [p]);
1346
+ return o(() => {
1347
+ if (u) return u.addEventListener("wheel", m, { passive: !1 }), () => u.removeEventListener("wheel", m);
1348
+ }, [u, m]), /* @__PURE__ */ y("div", {
1349
+ css: we,
1350
+ style: { ...s ? {
1351
+ opacity: "0",
1352
+ pointerEvents: "none"
1353
+ } : {} },
1354
+ children: [/* @__PURE__ */ v(de, {}), /* @__PURE__ */ y("div", {
1355
+ className: "actions",
1356
+ children: [/* @__PURE__ */ y("div", {
1357
+ className: "left",
1358
+ children: [
1359
+ /* @__PURE__ */ v(G, {
1360
+ id: "play",
1361
+ tooltipPlace: "top-start",
1362
+ text: /* @__PURE__ */ v("button", {
1363
+ className: "play",
1364
+ type: "button",
1365
+ onClick: () => e.togglePaused(),
1366
+ children: v(f ? E : t ? T : w, {})
1367
+ }),
1368
+ toolTipText: /* @__PURE__ */ v("span", { children: f ? "Replay (k)" : t ? "Play (k)" : "Pause (k)" })
1369
+ }),
1370
+ /* @__PURE__ */ v(Ce, { ref: d }),
1371
+ /* @__PURE__ */ v("div", {
1372
+ className: "time",
1373
+ children: U(n, r)
1374
+ })
1375
+ ]
1376
+ }), /* @__PURE__ */ y("div", {
1377
+ className: "right",
1378
+ children: [
1379
+ /* @__PURE__ */ v(ve, {}),
1380
+ /* @__PURE__ */ v(G, {
1381
+ id: "picture-in-picture",
1382
+ text: /* @__PURE__ */ v("button", {
1383
+ className: "picture-in-picture",
1384
+ type: "button",
1385
+ onClick: c,
1386
+ children: /* @__PURE__ */ v("img", { src: fe })
1387
+ }),
1388
+ toolTipText: /* @__PURE__ */ v("span", { children: "Picture in picture" })
1389
+ }),
1390
+ /* @__PURE__ */ v(G, {
1391
+ id: "full-screen",
1392
+ tooltipPlace: "top-end",
1393
+ text: /* @__PURE__ */ v("button", {
1394
+ className: "full-screen",
1395
+ type: "button",
1396
+ onClick: () => e.toggleFullscreen(),
1397
+ children: v(i ? C : S, {})
1398
+ }),
1399
+ toolTipText: /* @__PURE__ */ v("span", { children: i ? "Exit full screen (f)" : "Full screen (f)" })
1400
+ })
1401
+ ]
1402
+ })]
1403
+ })]
1404
+ });
1405
+ }, Ee = 3e3, De = u`
1406
+ position: relative;
1407
+ background-color: black;
1408
+ display: flex;
1409
+ justify-content: center;
1410
+ align-items: center;
1411
+ height: 100%;
1412
+ width: 100%;
1413
+
1414
+ & > div:not(:last-of-type) {
1415
+ position: absolute;
1416
+ z-index: 2;
1417
+ }
1418
+
1419
+ canvas {
1420
+ height: 100%;
1421
+ width: 100%;
1422
+ pointer-events: none;
1423
+ position: absolute;
1424
+ z-index: 1;
1425
+ }
1426
+
1427
+ .video, video {
1428
+ height: 100%;
1429
+ width: 100%;
1430
+ background-color: black;
1431
+ object-fit: contain;
1432
+ }
1433
+
1434
+ &.hide {
1435
+ cursor: none;
1436
+ }
1437
+ `, Oe = ({ ref: e, onVideoRef: t, onCanvasRef: n, children: r }) => {
1438
+ let i = N(), a = N((e) => e.hideUI), s = N((e) => e.setHideUI), l = c(void 0), u = c("mouse");
1439
+ o(() => () => clearTimeout(l.current), []);
1440
+ let d = () => {
1441
+ s(!1), clearTimeout(l.current), l.current = setTimeout(() => s(!0), Ee);
1442
+ };
1443
+ return /* @__PURE__ */ y("div", {
1444
+ css: De,
1445
+ ref: e,
1446
+ onPointerMove: (e) => {
1447
+ u.current = e.pointerType, e.pointerType === "mouse" && d();
1448
+ },
1449
+ onPointerDown: (e) => {
1450
+ u.current = e.pointerType;
1451
+ },
1452
+ onMouseOut: (e) => {
1453
+ let t = e.relatedTarget;
1454
+ if (t && e.currentTarget.contains(t)) return;
1455
+ let { left: n, right: r, top: i, bottom: a } = e.currentTarget.getBoundingClientRect();
1456
+ e.clientX >= n && e.clientX < r && e.clientY >= i && e.clientY < a || (clearTimeout(l.current), s(!0));
1457
+ },
1458
+ className: a ? "hide" : "",
1459
+ children: [
1460
+ /* @__PURE__ */ v(le, { onCanvasRef: n }),
1461
+ /* @__PURE__ */ v(Te, {}),
1462
+ /* @__PURE__ */ y("div", {
1463
+ className: "video",
1464
+ onClick: () => {
1465
+ if (u.current === "mouse") {
1466
+ i.togglePaused();
1467
+ return;
1468
+ }
1469
+ a ? d() : (clearTimeout(l.current), s(!0));
1470
+ },
1471
+ children: [/* @__PURE__ */ v("video", {
1472
+ ref: t,
1473
+ playsInline: !0
1474
+ }), r]
1475
+ })
1476
+ ]
1477
+ });
1478
+ }, ke = ({ options: e, children: t }) => {
1479
+ let { title: n, size: r, downloadedRanges: i, publicPath: a, libavWorkerUrl: s, read: c } = e, u = m(), d = p(), [f, h] = l(null), [g, _] = l(null);
1480
+ o(() => (u?.(f), () => u?.(null)), [f, u]), ne(f, g, e);
1481
+ let y = P({
1482
+ publicPath: a,
1483
+ workerUrl: s,
1484
+ length: r,
1485
+ read: c,
1486
+ downloadedRanges: i
1487
+ }), b = F(f, g), x = N((e) => e.setSourceState);
1488
+ return o(() => {
1489
+ x({
1490
+ title: n,
1491
+ size: r,
1492
+ downloadedRanges: i
1493
+ });
1494
+ }, [
1495
+ x,
1496
+ n,
1497
+ r,
1498
+ i
1499
+ ]), o(() => {
1500
+ x({
1501
+ thumbnails: y,
1502
+ togglePictureInPicture: b
1503
+ });
1504
+ }, [
1505
+ x,
1506
+ y,
1507
+ b
1508
+ ]), /* @__PURE__ */ v(Oe, {
1509
+ ref: d,
1510
+ onVideoRef: h,
1511
+ onCanvasRef: _,
1512
+ children: t
1513
+ });
1514
+ }, Q = u`
1515
+ display: flex;
1516
+ justify-content: center;
1517
+ background-color: #111;
1518
+ height: 100%;
1519
+ width: 100%;
1520
+ overflow: hidden;
1521
+ `, $ = (e) => /* @__PURE__ */ v("div", {
1522
+ css: Q,
1523
+ children: /* @__PURE__ */ v(M.Provider, { children: /* @__PURE__ */ v(ke, {
1524
+ options: e,
1525
+ children: e.children
1526
+ }) })
1527
+ }), Ae = ({ blob: e, name: t }) => ({
1528
+ length: e.size,
1529
+ name: t ?? (e instanceof File ? e.name : void 0),
1530
+ read: (t, n) => e.slice(t, t + n).arrayBuffer()
1531
+ }), je = async (e, t) => {
1532
+ let n = await fetch(e, {
1533
+ headers: { Range: "bytes=0-1" },
1534
+ credentials: t
1535
+ });
1536
+ if (!n.ok) throw Error(`The source could not be read: HTTP ${n.status}`);
1537
+ let r = n.headers.get("Content-Range")?.split("/").at(1), i = Number(r || n.headers.get("Content-Length"));
1538
+ if (!Number.isFinite(i) || i <= 0) throw Error("The source did not report its length");
1539
+ if (!r && n.status !== 206) throw Error("The source does not support range requests");
1540
+ return i;
1541
+ }, Me = async ({ url: e, length: t, name: n, credentials: r }) => {
1542
+ let i = t ?? await je(e, r);
1543
+ return {
1544
+ length: i,
1545
+ name: n,
1546
+ read: async (t, n) => {
1547
+ let a = await fetch(e, {
1548
+ headers: { Range: `bytes=${t}-${Math.min(t + n, i) - 1}` },
1549
+ credentials: r
1550
+ });
1551
+ if (!a.ok) throw Error(`The source could not be read: HTTP ${a.status}`);
1552
+ return a.arrayBuffer();
1553
+ }
1554
+ };
1555
+ }, Ne = async (e) => {
1556
+ if ("blob" in e) return Ae(e);
1557
+ if ("url" in e) return Me(e);
1558
+ if ("read" in e) return {
1559
+ length: e.length,
1560
+ name: e.name,
1561
+ read: e.read
1562
+ };
1563
+ throw Error(`Unknown source type: ${e}`);
1564
+ };
1565
+ //#endregion
1566
+ export { $ as MediaPlayer, $ as default, M as Player, n as SUBTITLES_OFF, i as createPictureInPicture, r as createSubtitleRenderer, t as createThumbnailGenerator, Ne as inputToRemuxerInput, j as sourceFeature, e as startPlayback, F as usePictureInPicture, N as usePlayer, P as useSeekThumbnails };