@banou/media-player 0.10.18 → 0.10.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +196 -163
- package/dist/react/components/track-menu.d.ts +1 -1
- package/dist/react/player.d.ts +1 -0
- package/dist/react/source-feature.d.ts +5 -0
- package/dist/react/video-player.d.ts +6 -0
- package/package.json +1 -1
- package/src/lib/react/components/chrome.tsx +31 -6
- package/src/lib/react/components/control-bar.tsx +1 -0
- package/src/lib/react/components/settings.tsx +37 -1
- package/src/lib/react/components/track-menu.tsx +1 -1
- package/src/lib/react/source-feature.ts +5 -0
- package/src/lib/react/video-player.tsx +11 -0
package/dist/index.js
CHANGED
|
@@ -5,10 +5,10 @@ import { createPlayer as v, useContainerAttach as y, useMediaAttach as b } from
|
|
|
5
5
|
import { definePlayerFeature as x, videoFeatures as S } from "@videojs/core/dom";
|
|
6
6
|
import { createMediaElementSource as C } from "@banou/ponyfill/web-audio";
|
|
7
7
|
import { Fragment as w, jsx as T, jsxs as E } from "@emotion/react/jsx-runtime";
|
|
8
|
-
import { Captions as D, CaptionsOff as O, Check as k, ChevronLeft as A, ChevronRight as j, Copy as M, Maximize as N, Minimize as P, Pause as F, Play as I, RotateCcw as L, Settings as R, TriangleAlert as z, Volume1 as ee, Volume2 as
|
|
9
|
-
import { Tooltip as
|
|
8
|
+
import { Captions as D, CaptionsOff as O, Check as k, ChevronLeft as A, ChevronRight as j, Copy as M, Maximize as N, Minimize as P, Pause as F, Play as I, RotateCcw as L, Settings as R, TriangleAlert as z, Volume1 as ee, Volume2 as te, VolumeX as B } from "lucide-react";
|
|
9
|
+
import { Tooltip as V } from "react-tooltip";
|
|
10
10
|
//#region src/lib/react/source-feature.ts
|
|
11
|
-
var
|
|
11
|
+
var ne = {
|
|
12
12
|
indexes: [],
|
|
13
13
|
chapters: [],
|
|
14
14
|
thumbnails: [],
|
|
@@ -20,6 +20,7 @@ var te = {
|
|
|
20
20
|
audioTracks: [],
|
|
21
21
|
selectedAudioTrack: void 0,
|
|
22
22
|
selectAudioTrack: () => {},
|
|
23
|
+
quality: null,
|
|
23
24
|
hideUI: !1,
|
|
24
25
|
setHideUI: () => {},
|
|
25
26
|
hasMedia: !1,
|
|
@@ -32,19 +33,19 @@ var te = {
|
|
|
32
33
|
playbackErrors: [],
|
|
33
34
|
ready: !1,
|
|
34
35
|
setSourceState: () => {}
|
|
35
|
-
},
|
|
36
|
+
}, H = x({
|
|
36
37
|
name: "source",
|
|
37
|
-
state: () => ({ ...
|
|
38
|
+
state: () => ({ ...ne }),
|
|
38
39
|
attach({ set: e }) {
|
|
39
40
|
e({
|
|
40
41
|
setSourceState: (t) => e(t),
|
|
41
42
|
setHideUI: (t) => e({ hideUI: t })
|
|
42
43
|
});
|
|
43
44
|
}
|
|
44
|
-
}),
|
|
45
|
-
features: [...S,
|
|
45
|
+
}), U = v({
|
|
46
|
+
features: [...S, H],
|
|
46
47
|
displayName: "MediaPlayer"
|
|
47
|
-
}),
|
|
48
|
+
}), W = U.usePlayer, G = (e) => {
|
|
48
49
|
if (e && e !== "und") try {
|
|
49
50
|
return new Intl.DisplayNames([navigator.language || "en"], {
|
|
50
51
|
type: "language",
|
|
@@ -53,28 +54,28 @@ var te = {
|
|
|
53
54
|
} catch {
|
|
54
55
|
return;
|
|
55
56
|
}
|
|
56
|
-
},
|
|
57
|
-
let t = e.map((e) =>
|
|
57
|
+
}, K = (e) => e?.replace(/_/g, " ").trim() || void 0, q = (e) => {
|
|
58
|
+
let t = e.map((e) => G(e.language) ?? K(e.title) ?? `Track ${e.streamIndex}`), n = t.reduce((e, t) => ({
|
|
58
59
|
...e,
|
|
59
60
|
[t]: (e[t] ?? 0) + 1
|
|
60
61
|
}), {});
|
|
61
62
|
return e.map((e, r) => {
|
|
62
|
-
let i = t[r], a =
|
|
63
|
+
let i = t[r], a = K(e.title);
|
|
63
64
|
return {
|
|
64
65
|
track: e,
|
|
65
66
|
label: (n[i] ?? 0) > 1 && a && a !== i ? `${i} (${a})` : i
|
|
66
67
|
};
|
|
67
68
|
});
|
|
68
|
-
},
|
|
69
|
+
}, J = (e) => q(e).map(({ track: e, label: t }) => ({
|
|
69
70
|
id: e.streamIndex,
|
|
70
71
|
label: t
|
|
71
|
-
})),
|
|
72
|
+
})), Y = [
|
|
72
73
|
0,
|
|
73
74
|
250,
|
|
74
75
|
1e3,
|
|
75
76
|
3e3,
|
|
76
77
|
1e4
|
|
77
|
-
],
|
|
78
|
+
], X = 6e4, re = 500, ie = 15e3, ae = (e) => e instanceof Error ? e.message : String(e), oe = (e) => {
|
|
78
79
|
let t = [], n = e?.cause;
|
|
79
80
|
for (let e = 0; n != null && e < 8; e++) {
|
|
80
81
|
if (typeof MediaError < "u" && n instanceof MediaError) {
|
|
@@ -85,7 +86,7 @@ var te = {
|
|
|
85
86
|
}
|
|
86
87
|
return t.length ? t.join("\n") : void 0;
|
|
87
88
|
}, se = (e, t, r) => {
|
|
88
|
-
let a =
|
|
89
|
+
let a = W(), { read: o, size: s, publicPath: c = "", libavWorkerUrl: l = "", jassubWorkerUrl: f = "", jassubWasmUrl: h = "", jassubLegacyWasmUrl: g, defaultFontUrl: _, bufferSize: v, autoplay: y = !1, chapters: b, seekPrepareBudgetMs: x = re } = r ?? {}, S = p(b);
|
|
89
90
|
S.current = b;
|
|
90
91
|
let [C, w] = m(void 0), [T, E] = m(0), D = p({
|
|
91
92
|
count: 0,
|
|
@@ -132,7 +133,7 @@ var te = {
|
|
|
132
133
|
});
|
|
133
134
|
}, [a, x]), L = u((e) => {
|
|
134
135
|
typeof e == "number" && (a.setSourceState({ selectedAudioTrack: e }), w(e));
|
|
135
|
-
}, [a]), R =
|
|
136
|
+
}, [a]), R = W((e) => e.setSourceState);
|
|
136
137
|
d(() => {
|
|
137
138
|
R({
|
|
138
139
|
selectSubtitleTrack: F,
|
|
@@ -177,11 +178,11 @@ var te = {
|
|
|
177
178
|
let t = n(e);
|
|
178
179
|
if (u(e, t), t) {
|
|
179
180
|
let t = performance.now();
|
|
180
|
-
t - D.current.at >
|
|
181
|
+
t - D.current.at > X && (D.current = {
|
|
181
182
|
count: 0,
|
|
182
183
|
at: t
|
|
183
184
|
});
|
|
184
|
-
let n =
|
|
185
|
+
let n = Y[Math.min(D.current.count, Y.length - 1)];
|
|
185
186
|
D.current = {
|
|
186
187
|
count: D.current.count + 1,
|
|
187
188
|
at: t
|
|
@@ -218,13 +219,13 @@ var te = {
|
|
|
218
219
|
onSeek: (e) => N.current?.(e),
|
|
219
220
|
onSubtitleStreams: (e) => {
|
|
220
221
|
r || a.setSourceState({
|
|
221
|
-
subtitleTracks:
|
|
222
|
+
subtitleTracks: J(e),
|
|
222
223
|
...j.current ? {} : { selectedSubtitleTrack: e[0]?.streamIndex }
|
|
223
224
|
});
|
|
224
225
|
},
|
|
225
226
|
onAudioStreams: (e, t) => {
|
|
226
227
|
r || a.setSourceState({
|
|
227
|
-
audioTracks:
|
|
228
|
+
audioTracks: J(e),
|
|
228
229
|
selectedAudioTrack: t
|
|
229
230
|
});
|
|
230
231
|
}
|
|
@@ -572,7 +573,7 @@ var te = {
|
|
|
572
573
|
text-shadow: 0 0 4px rgba(0, 0, 0, 1);
|
|
573
574
|
pointer-events: none;
|
|
574
575
|
`, Oe = (e) => e instanceof Error ? e.message : typeof e == "string" ? e : "Playback failed", ke = ({ onSubtitleRef: e }) => {
|
|
575
|
-
let t =
|
|
576
|
+
let t = W((e) => e.title), n = W((e) => e.hideUI), r = W((e) => e.playbackError), i = W((e) => e.ready), a = W((e) => e.size), o = W((e) => e.waiting), s = W((e) => e.seekingTo);
|
|
576
577
|
return /* @__PURE__ */ E(w, { children: [
|
|
577
578
|
t ? /* @__PURE__ */ T("div", {
|
|
578
579
|
className: "title",
|
|
@@ -676,7 +677,7 @@ var te = {
|
|
|
676
677
|
"data-tooltip-delay-hide": i,
|
|
677
678
|
"data-tooltip-place": o,
|
|
678
679
|
children: n
|
|
679
|
-
}), !s && /* @__PURE__ */ T(
|
|
680
|
+
}), !s && /* @__PURE__ */ T(V, {
|
|
680
681
|
ref: u,
|
|
681
682
|
css: Ie(c),
|
|
682
683
|
id: e,
|
|
@@ -1053,7 +1054,7 @@ var te = {
|
|
|
1053
1054
|
--thumbnail-half: calc(9 * var(--mp-unit));
|
|
1054
1055
|
}
|
|
1055
1056
|
`, ut = 4, dt = () => {
|
|
1056
|
-
let e =
|
|
1057
|
+
let e = W(), t = W((e) => e.currentTime), n = W((e) => e.requestSeek), r = W((e) => e.duration), i = W((e) => e.size), a = W((e) => e.downloadedRanges), o = W((e) => e.buffered), s = W((e) => e.indexes), c = W((e) => e.thumbnails), l = W((e) => e.thumbnailAt), u = W((e) => e.requestThumbnail), h = W((e) => e.chapters), g = W((e) => e.seekingTo), _ = W((e) => e.setSourceState), v = W((e) => e.hideUI), y = p(null), [b, x] = m(void 0), [S, C] = m(void 0), w = (e) => {
|
|
1057
1058
|
C(e), u(e);
|
|
1058
1059
|
}, D = !!S;
|
|
1059
1060
|
d(() => {
|
|
@@ -1089,9 +1090,9 @@ var te = {
|
|
|
1089
1090
|
R.current = e.pointerType;
|
|
1090
1091
|
}, ee = (e) => {
|
|
1091
1092
|
R.current !== "touch" && w(L(e.clientX));
|
|
1092
|
-
},
|
|
1093
|
+
}, te = () => {
|
|
1093
1094
|
y.current && w(void 0);
|
|
1094
|
-
},
|
|
1095
|
+
}, B = (e) => r ? e / r * 100 : 0, V = (e, t, n) => {
|
|
1095
1096
|
let r = Number((n - t).toFixed(4)), i = t * 100;
|
|
1096
1097
|
return /* @__PURE__ */ T("div", {
|
|
1097
1098
|
className: "loaded-part",
|
|
@@ -1100,10 +1101,10 @@ var te = {
|
|
|
1100
1101
|
marginLeft: `${i}%`
|
|
1101
1102
|
}
|
|
1102
1103
|
}, e);
|
|
1103
|
-
},
|
|
1104
|
+
}, ne = f(() => i === void 0 ? r ? o.map(([e, t], n) => V(n, e / r, t / r)) : [] : a?.map((e, t) => {
|
|
1104
1105
|
if (!i || !r) return null;
|
|
1105
1106
|
let n = s.filter((t) => e.startByteOffset <= t.pos && t.pos <= e.endByteOffset), a = n.at(0), o = n.at(-1);
|
|
1106
|
-
return !a || !o ? null :
|
|
1107
|
+
return !a || !o ? null : V(t, a.timestamp / r, o.timestamp / r);
|
|
1107
1108
|
}) ?? [], [
|
|
1108
1109
|
r,
|
|
1109
1110
|
i,
|
|
@@ -1119,7 +1120,7 @@ var te = {
|
|
|
1119
1120
|
b,
|
|
1120
1121
|
r
|
|
1121
1122
|
]);
|
|
1122
|
-
let
|
|
1123
|
+
let H = f(() => {
|
|
1123
1124
|
if (!r) return 0;
|
|
1124
1125
|
if (N && b !== void 0) return b;
|
|
1125
1126
|
let e = g ?? t;
|
|
@@ -1130,22 +1131,22 @@ var te = {
|
|
|
1130
1131
|
N,
|
|
1131
1132
|
b,
|
|
1132
1133
|
g
|
|
1133
|
-
]),
|
|
1134
|
-
if (!
|
|
1135
|
-
let e = S / r * 100, t =
|
|
1136
|
-
return t >= 0 ? t :
|
|
1134
|
+
]), U = f(() => We(h, r), [h, r]), G = U.length > 0, K = f(() => {
|
|
1135
|
+
if (!G || S === void 0 || !r) return -1;
|
|
1136
|
+
let e = S / r * 100, t = U.findIndex((t, n) => n < U.length - 1 && e >= t && e < U[n + 1]);
|
|
1137
|
+
return t >= 0 ? t : U.length - 2;
|
|
1137
1138
|
}, [
|
|
1138
|
-
|
|
1139
|
-
|
|
1139
|
+
G,
|
|
1140
|
+
U,
|
|
1140
1141
|
S,
|
|
1141
1142
|
r
|
|
1142
|
-
]),
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
]),
|
|
1143
|
+
]), q = f(() => G ? Ge(U, (e) => e !== K) : void 0, [
|
|
1144
|
+
G,
|
|
1145
|
+
U,
|
|
1146
|
+
K
|
|
1147
|
+
]), J = f(() => K >= 0 ? Ge(U, (e) => e === K) : void 0, [U, K]), Y = f(() => {
|
|
1147
1148
|
if (S !== void 0) return h.find(({ start: e, end: t }) => e <= S && S < t);
|
|
1148
|
-
}, [h, S]),
|
|
1149
|
+
}, [h, S]), X = f(() => {
|
|
1149
1150
|
if (S) {
|
|
1150
1151
|
if (l) return l(S);
|
|
1151
1152
|
if (c.length) return c.find(({ startTime: e, endTime: t }) => e <= S && S < t);
|
|
@@ -1161,13 +1162,13 @@ var te = {
|
|
|
1161
1162
|
/* @__PURE__ */ T("div", { className: "background-bar" }),
|
|
1162
1163
|
/* @__PURE__ */ T("div", {
|
|
1163
1164
|
className: "loaded",
|
|
1164
|
-
children:
|
|
1165
|
+
children: ne
|
|
1165
1166
|
}),
|
|
1166
1167
|
/* @__PURE__ */ T("div", {
|
|
1167
1168
|
className: "play-container",
|
|
1168
1169
|
children: /* @__PURE__ */ T("div", {
|
|
1169
1170
|
className: "play",
|
|
1170
|
-
style: { transform: `scaleX(${
|
|
1171
|
+
style: { transform: `scaleX(${H})` }
|
|
1171
1172
|
})
|
|
1172
1173
|
})
|
|
1173
1174
|
]
|
|
@@ -1178,31 +1179,31 @@ var te = {
|
|
|
1178
1179
|
className: [
|
|
1179
1180
|
"progress-bar",
|
|
1180
1181
|
N ? "dragging" : "",
|
|
1181
|
-
|
|
1182
|
+
G ? "segmented" : ""
|
|
1182
1183
|
].filter(Boolean).join(" "),
|
|
1183
1184
|
onPointerDown: z,
|
|
1184
1185
|
onPointerMove: z,
|
|
1185
1186
|
onMouseMove: ee,
|
|
1186
|
-
onMouseOut:
|
|
1187
|
+
onMouseOut: te,
|
|
1187
1188
|
children: [
|
|
1188
|
-
re(void 0,
|
|
1189
|
+
re(void 0, q),
|
|
1189
1190
|
S ? /* @__PURE__ */ E("div", {
|
|
1190
1191
|
className: "cursor-time",
|
|
1191
1192
|
style: {
|
|
1192
|
-
left:
|
|
1193
|
-
maxWidth:
|
|
1193
|
+
left: Y ? `clamp(var(--thumbnail-half), ${B(S)}%, calc(100% - var(--thumbnail-half)))` : `clamp(calc(3 * var(--mp-unit)), ${B(S)}%, calc(100% - calc(3 * var(--mp-unit))))`,
|
|
1194
|
+
maxWidth: Y ? "var(--thumbnail-width)" : void 0
|
|
1194
1195
|
},
|
|
1195
1196
|
children: [/* @__PURE__ */ T("span", {
|
|
1196
1197
|
className: "cursor-time-value",
|
|
1197
1198
|
children: Pe(S)
|
|
1198
|
-
}),
|
|
1199
|
+
}), Y ? /* @__PURE__ */ T("span", {
|
|
1199
1200
|
className: "chapter-title",
|
|
1200
|
-
children:
|
|
1201
|
+
children: Y.title
|
|
1201
1202
|
}) : void 0]
|
|
1202
1203
|
}) : void 0,
|
|
1203
1204
|
/* @__PURE__ */ T("div", { className: "progress" }),
|
|
1204
1205
|
/* @__PURE__ */ T("div", { className: "hover" }),
|
|
1205
|
-
|
|
1206
|
+
J ? re("focus", J) : void 0,
|
|
1206
1207
|
/* @__PURE__ */ T("div", { className: "scrubber" }),
|
|
1207
1208
|
/* @__PURE__ */ T("div", {
|
|
1208
1209
|
className: "padding",
|
|
@@ -1213,8 +1214,8 @@ var te = {
|
|
|
1213
1214
|
}),
|
|
1214
1215
|
/* @__PURE__ */ T("div", {
|
|
1215
1216
|
className: "thumbnail",
|
|
1216
|
-
style: { left: `clamp(var(--thumbnail-half), ${
|
|
1217
|
-
children:
|
|
1217
|
+
style: { left: `clamp(var(--thumbnail-half), ${B(S ?? 1)}%, calc(100% - var(--thumbnail-half)))` },
|
|
1218
|
+
children: X?.url ? /* @__PURE__ */ T("img", { src: X.url }) : void 0
|
|
1218
1219
|
})
|
|
1219
1220
|
]
|
|
1220
1221
|
});
|
|
@@ -1549,7 +1550,7 @@ ${gt}
|
|
|
1549
1550
|
e.detail
|
|
1550
1551
|
].filter(Boolean).join("\n");
|
|
1551
1552
|
}).join("\n\n"), wt = () => {
|
|
1552
|
-
let e =
|
|
1553
|
+
let e = W((e) => e.playbackErrors), { open: t, toggle: n, containerRef: r } = vt(), [i, a] = m(!1);
|
|
1553
1554
|
if (e.length === 0) return null;
|
|
1554
1555
|
let o = () => {
|
|
1555
1556
|
navigator.clipboard?.writeText(Ct(e)).then(() => {
|
|
@@ -1664,7 +1665,7 @@ width: 100%;
|
|
|
1664
1665
|
}
|
|
1665
1666
|
}
|
|
1666
1667
|
`, Et = () => {
|
|
1667
|
-
let e =
|
|
1668
|
+
let e = W(), t = W((e) => e.playbackRate), n = p(null), r = p(null), i = .25, a = 2.75, o = (e, t = .05) => Math.round(e / t) * t, s = o(t || 1), c = (e) => (e - i) / a * 100, l = (e) => o(i + e * a), { handlers: u } = ze({
|
|
1668
1669
|
ref: r,
|
|
1669
1670
|
onChange: (t) => e.setPlaybackRate(l(t))
|
|
1670
1671
|
}), f = (t) => {
|
|
@@ -1793,65 +1794,74 @@ ${gt}
|
|
|
1793
1794
|
}
|
|
1794
1795
|
|
|
1795
1796
|
`, Ot = () => {
|
|
1796
|
-
let e =
|
|
1797
|
-
e.setPlaybackRate(t),
|
|
1798
|
-
},
|
|
1799
|
-
e !== void 0 &&
|
|
1800
|
-
},
|
|
1801
|
-
|
|
1797
|
+
let e = W(), t = W((e) => e.playbackRate), n = W((e) => e.audioTracks), r = W((e) => e.selectedAudioTrack), i = W((e) => e.selectAudioTrack), a = W((e) => e.quality), o = W((e) => e.volumeNormalizer), [s, c] = m(0), { open: l, toggle: u, containerRef: d, pending: f, failed: p, runSelect: h } = vt({ onReset: () => c(0) }), g = (t) => {
|
|
1798
|
+
e.setPlaybackRate(t), c(0);
|
|
1799
|
+
}, _ = (e) => {
|
|
1800
|
+
e !== void 0 && h(e, () => i(e));
|
|
1801
|
+
}, v = (e) => {
|
|
1802
|
+
a && h(e ?? null, () => a.select(e === void 0 ? null : String(e)));
|
|
1803
|
+
}, y = a ? a.options.length + +!!a.offLabel : 0, b = a?.selectedId == null ? a?.offLabel : a.options.find(({ id: e }) => e === a.selectedId)?.label, x = (e) => () => {
|
|
1804
|
+
c(e);
|
|
1802
1805
|
};
|
|
1803
1806
|
return /* @__PURE__ */ E("div", {
|
|
1804
1807
|
css: Dt,
|
|
1805
|
-
ref:
|
|
1808
|
+
ref: d,
|
|
1806
1809
|
children: [
|
|
1807
1810
|
/* @__PURE__ */ T($, {
|
|
1808
1811
|
id: "settings",
|
|
1809
|
-
disabled:
|
|
1812
|
+
disabled: l,
|
|
1810
1813
|
text: /* @__PURE__ */ T("button", {
|
|
1811
1814
|
className: "settings",
|
|
1812
1815
|
type: "button",
|
|
1813
|
-
onClick:
|
|
1816
|
+
onClick: u,
|
|
1814
1817
|
"aria-label": "Settings",
|
|
1815
|
-
"aria-expanded":
|
|
1818
|
+
"aria-expanded": l,
|
|
1816
1819
|
children: /* @__PURE__ */ T(R, {})
|
|
1817
1820
|
}),
|
|
1818
1821
|
toolTipText: /* @__PURE__ */ T("span", { children: "Settings" })
|
|
1819
1822
|
}),
|
|
1820
|
-
|
|
1823
|
+
l && s === 0 && /* @__PURE__ */ E("div", {
|
|
1821
1824
|
className: "popover menu",
|
|
1822
1825
|
children: [
|
|
1823
1826
|
n.length > 1 ? /* @__PURE__ */ E("div", {
|
|
1824
|
-
onClick:
|
|
1827
|
+
onClick: x(2),
|
|
1825
1828
|
children: [/* @__PURE__ */ T("div", { children: "Audio" }), /* @__PURE__ */ T("div", { children: /* @__PURE__ */ T(j, {}) })]
|
|
1826
1829
|
}) : null,
|
|
1830
|
+
y > 1 ? /* @__PURE__ */ E("div", {
|
|
1831
|
+
onClick: x(3),
|
|
1832
|
+
children: [/* @__PURE__ */ T("div", { children: "Quality" }), /* @__PURE__ */ E("div", { children: [b ? /* @__PURE__ */ T("span", {
|
|
1833
|
+
className: "secondary",
|
|
1834
|
+
children: b
|
|
1835
|
+
}) : null, /* @__PURE__ */ T(j, {})] })]
|
|
1836
|
+
}) : null,
|
|
1827
1837
|
/* @__PURE__ */ E("div", {
|
|
1828
|
-
onClick:
|
|
1838
|
+
onClick: x(1),
|
|
1829
1839
|
children: [/* @__PURE__ */ T("div", { children: "Playback speed" }), /* @__PURE__ */ E("div", { children: [/* @__PURE__ */ T("span", {
|
|
1830
1840
|
className: "secondary",
|
|
1831
1841
|
children: t === 1 ? "(Default)" : t.toFixed(2)
|
|
1832
1842
|
}), /* @__PURE__ */ T(j, {})] })]
|
|
1833
1843
|
}),
|
|
1834
|
-
|
|
1844
|
+
o ? /* @__PURE__ */ E("div", {
|
|
1835
1845
|
role: "switch",
|
|
1836
|
-
"aria-checked":
|
|
1837
|
-
"aria-disabled": !
|
|
1838
|
-
className:
|
|
1846
|
+
"aria-checked": o.available && o.enabled,
|
|
1847
|
+
"aria-disabled": !o.available,
|
|
1848
|
+
className: o.available ? void 0 : "no-hover unavailable",
|
|
1839
1849
|
onClick: () => {
|
|
1840
|
-
|
|
1850
|
+
o.available && o.setEnabled(!o.enabled);
|
|
1841
1851
|
},
|
|
1842
|
-
children: [/* @__PURE__ */ T("div", { children: "Normalize volume" }), /* @__PURE__ */ T("div", { children:
|
|
1852
|
+
children: [/* @__PURE__ */ T("div", { children: "Normalize volume" }), /* @__PURE__ */ T("div", { children: o.available ? /* @__PURE__ */ T("span", { className: o.enabled ? "switch on" : "switch" }) : /* @__PURE__ */ T("span", {
|
|
1843
1853
|
className: "secondary",
|
|
1844
1854
|
children: "Unavailable"
|
|
1845
1855
|
}) })]
|
|
1846
1856
|
}) : null
|
|
1847
1857
|
]
|
|
1848
1858
|
}),
|
|
1849
|
-
|
|
1859
|
+
l && s === 1 && /* @__PURE__ */ E("div", {
|
|
1850
1860
|
className: "popover playback-rate",
|
|
1851
1861
|
children: [
|
|
1852
1862
|
/* @__PURE__ */ E("div", {
|
|
1853
1863
|
className: "back",
|
|
1854
|
-
onClick:
|
|
1864
|
+
onClick: x(0),
|
|
1855
1865
|
children: [/* @__PURE__ */ T(A, {}), /* @__PURE__ */ T("span", { children: "Playback speed" })]
|
|
1856
1866
|
}),
|
|
1857
1867
|
/* @__PURE__ */ T("div", {
|
|
@@ -1862,33 +1872,43 @@ ${gt}
|
|
|
1862
1872
|
className: "options no-hover",
|
|
1863
1873
|
children: [
|
|
1864
1874
|
/* @__PURE__ */ T("div", {
|
|
1865
|
-
onClick: () =>
|
|
1875
|
+
onClick: () => g(.5),
|
|
1866
1876
|
children: "0.5x"
|
|
1867
1877
|
}),
|
|
1868
1878
|
/* @__PURE__ */ T("div", {
|
|
1869
|
-
onClick: () =>
|
|
1879
|
+
onClick: () => g(1),
|
|
1870
1880
|
children: "1x"
|
|
1871
1881
|
}),
|
|
1872
1882
|
/* @__PURE__ */ T("div", {
|
|
1873
|
-
onClick: () =>
|
|
1883
|
+
onClick: () => g(1.5),
|
|
1874
1884
|
children: "1.5x"
|
|
1875
1885
|
}),
|
|
1876
1886
|
/* @__PURE__ */ T("div", {
|
|
1877
|
-
onClick: () =>
|
|
1887
|
+
onClick: () => g(2),
|
|
1878
1888
|
children: "2x"
|
|
1879
1889
|
})
|
|
1880
1890
|
]
|
|
1881
1891
|
})
|
|
1882
1892
|
]
|
|
1883
1893
|
}),
|
|
1884
|
-
|
|
1894
|
+
l && s === 2 && /* @__PURE__ */ T(_t, {
|
|
1885
1895
|
title: "Audio",
|
|
1886
1896
|
tracks: n,
|
|
1887
1897
|
selected: r,
|
|
1888
|
-
onSelect:
|
|
1889
|
-
onBack:
|
|
1890
|
-
pending:
|
|
1891
|
-
failed:
|
|
1898
|
+
onSelect: _,
|
|
1899
|
+
onBack: x(0),
|
|
1900
|
+
pending: f,
|
|
1901
|
+
failed: p
|
|
1902
|
+
}),
|
|
1903
|
+
l && s === 3 && a && /* @__PURE__ */ T(_t, {
|
|
1904
|
+
title: "Quality",
|
|
1905
|
+
tracks: a.options,
|
|
1906
|
+
selected: a.selectedId ?? void 0,
|
|
1907
|
+
onSelect: v,
|
|
1908
|
+
onBack: x(0),
|
|
1909
|
+
offLabel: a.offLabel,
|
|
1910
|
+
pending: f,
|
|
1911
|
+
failed: p
|
|
1892
1912
|
})
|
|
1893
1913
|
]
|
|
1894
1914
|
});
|
|
@@ -1910,7 +1930,7 @@ position: static;
|
|
|
1910
1930
|
|
|
1911
1931
|
${gt}
|
|
1912
1932
|
`, At = () => {
|
|
1913
|
-
let e =
|
|
1933
|
+
let e = W((e) => e.subtitleTracks), t = W((e) => e.selectedSubtitleTrack), n = W((e) => e.selectSubtitleTrack), r = W((e) => e.subtitleOffLabel), { open: i, toggle: a, containerRef: o, pending: s, failed: c, runSelect: l } = vt();
|
|
1914
1934
|
if (e.length === 0) return null;
|
|
1915
1935
|
let u = t !== void 0;
|
|
1916
1936
|
return /* @__PURE__ */ E("div", {
|
|
@@ -1989,7 +2009,7 @@ ${gt}
|
|
|
1989
2009
|
height: 10px;
|
|
1990
2010
|
}
|
|
1991
2011
|
`, Nt = ({ value: e, onChange: t }) => {
|
|
1992
|
-
let n =
|
|
2012
|
+
let n = W((e) => e.volume), r = W((e) => e.muted), i = p(null), a = p(null), { handlers: o } = ze({
|
|
1993
2013
|
ref: a,
|
|
1994
2014
|
onChange: t
|
|
1995
2015
|
}), s = (n) => {
|
|
@@ -2091,7 +2111,7 @@ ${gt}
|
|
|
2091
2111
|
}
|
|
2092
2112
|
|
|
2093
2113
|
`, Ft = ({ ref: e }) => {
|
|
2094
|
-
let t =
|
|
2114
|
+
let t = W(), n = W((e) => e.volume), r = W((e) => e.muted), i = f(() => Ne(n), [n]);
|
|
2095
2115
|
return /* @__PURE__ */ E("div", {
|
|
2096
2116
|
css: Pt,
|
|
2097
2117
|
children: [/* @__PURE__ */ T($, {
|
|
@@ -2103,7 +2123,7 @@ ${gt}
|
|
|
2103
2123
|
ref: e,
|
|
2104
2124
|
"aria-label": "Mute",
|
|
2105
2125
|
"aria-pressed": r || n === 0,
|
|
2106
|
-
children: T(r || n === 0 ?
|
|
2126
|
+
children: T(r || n === 0 ? B : n <= .5 ? ee : te, {
|
|
2107
2127
|
size: 18,
|
|
2108
2128
|
color: "#fff"
|
|
2109
2129
|
})
|
|
@@ -2253,7 +2273,7 @@ ${gt}
|
|
|
2253
2273
|
}
|
|
2254
2274
|
}
|
|
2255
2275
|
`, Vt = ({ shortcuts: e = "window", root: t }) => {
|
|
2256
|
-
let n =
|
|
2276
|
+
let n = W(), r = W((e) => e.paused), i = W((e) => e.currentTime), a = W((e) => e.duration), o = W((e) => e.seekingTo), s = W((e) => e.fullscreen), c = W((e) => e.hideUI), l = W((e) => e.togglePictureInPicture), f = W((e) => e.pictureInPictureMode), h = W((e) => e.burnedInSubtitles), g = W((e) => e.passThroughPictureInPicture), _ = W((e) => e.subtitleTracks), v = W((e) => e.requestSeek), [y, b] = m(null), x = p(null), S = p(void 0), C = f === "burn-in", w = _.length > 0, D = a > 0 && i === a, O = u(({ direction: e, stepSize: t }) => {
|
|
2257
2277
|
let r = Ne(n.volume), i = e === "up" ? t : -t, a = Math.max(0, Math.min(1, r + i)), o = n.muted;
|
|
2258
2278
|
n.setVolume(Me(a)), o && !n.muted && n.toggleMuted();
|
|
2259
2279
|
}, [n]), k = p(!1);
|
|
@@ -2331,6 +2351,7 @@ ${gt}
|
|
|
2331
2351
|
e = Date.now() + zt;
|
|
2332
2352
|
};
|
|
2333
2353
|
}, [M]), d(() => () => clearInterval(S.current), []), /* @__PURE__ */ E("div", {
|
|
2354
|
+
className: "control-bar",
|
|
2334
2355
|
css: Bt,
|
|
2335
2356
|
style: { ...c ? {
|
|
2336
2357
|
opacity: "0",
|
|
@@ -2461,7 +2482,7 @@ ${gt}
|
|
|
2461
2482
|
color: #fff;
|
|
2462
2483
|
}
|
|
2463
2484
|
`, Wt = () => {
|
|
2464
|
-
let e =
|
|
2485
|
+
let e = W((e) => e.burnedInSubtitles), t = W((e) => e.pictureInPictureMode), [n, r] = m(!1);
|
|
2465
2486
|
return d(() => {
|
|
2466
2487
|
if (t !== "burn-in" || !e) {
|
|
2467
2488
|
r(!1);
|
|
@@ -2541,7 +2562,7 @@ ${gt}
|
|
|
2541
2562
|
}
|
|
2542
2563
|
}
|
|
2543
2564
|
`, Xt = () => {
|
|
2544
|
-
let e =
|
|
2565
|
+
let e = W((e) => e.chapters), t = W((e) => e.currentTime), n = W((e) => e.requestSeek), r = W((e) => e.seekPreview), i = W(), a = f(() => ct(e), [e]), o = f(() => {
|
|
2545
2566
|
if (typeof t != "number") return;
|
|
2546
2567
|
let n = e.findIndex((e, n) => a[n] !== void 0 && e.start - Gt <= t && t < e.end), r = n >= 0 ? a[n] : void 0;
|
|
2547
2568
|
return r ? {
|
|
@@ -2658,50 +2679,54 @@ ${gt}
|
|
|
2658
2679
|
item: e
|
|
2659
2680
|
}];
|
|
2660
2681
|
}), nn = ({ ref: e, onVideoRef: t, onSubtitleRef: n, overlay: r, controls: i, shortcuts: a, children: o }) => {
|
|
2661
|
-
let s =
|
|
2682
|
+
let s = W(), c = W((e) => e.hideUI), l = W((e) => e.setHideUI), u = W((e) => e.hasMedia), f = W((e) => e.passThroughPictureInPicture), m = p(void 0), h = p("mouse"), g = p({
|
|
2662
2683
|
x: -1,
|
|
2663
2684
|
y: -1
|
|
2664
|
-
}), _ = p(null);
|
|
2685
|
+
}), _ = p(!1), v = p(null);
|
|
2665
2686
|
d(() => () => clearTimeout(m.current), []), d(() => {
|
|
2666
2687
|
u || (clearTimeout(m.current), l(!1));
|
|
2667
2688
|
}, [u, l]);
|
|
2668
|
-
let
|
|
2689
|
+
let y = () => {
|
|
2669
2690
|
u && l(!0);
|
|
2670
|
-
},
|
|
2671
|
-
|
|
2672
|
-
},
|
|
2691
|
+
}, b = (t) => {
|
|
2692
|
+
v.current = t, typeof e == "function" ? e(t) : e && (e.current = t);
|
|
2693
|
+
}, x = () => {
|
|
2673
2694
|
if (s.passThroughPictureInPicture?.armed) return !0;
|
|
2674
2695
|
if (h.current !== "mouse") return !1;
|
|
2675
2696
|
let { x: e, y: t } = g.current;
|
|
2676
|
-
if (!
|
|
2697
|
+
if (!v.current || e < 0) return !1;
|
|
2677
2698
|
let n = document.elementFromPoint(e, t);
|
|
2678
|
-
return
|
|
2679
|
-
},
|
|
2680
|
-
if (
|
|
2681
|
-
m.current = setTimeout(
|
|
2699
|
+
return !n || !v.current.contains(n) || n.closest(".video") ? !1 : !n.closest(".control-bar") || _.current || !!v.current.querySelector(".control-bar [aria-expanded=\"true\"]");
|
|
2700
|
+
}, S = () => {
|
|
2701
|
+
if (x()) {
|
|
2702
|
+
m.current = setTimeout(S, Zt);
|
|
2682
2703
|
return;
|
|
2683
2704
|
}
|
|
2684
|
-
|
|
2685
|
-
},
|
|
2686
|
-
l(!1), clearTimeout(m.current), m.current = setTimeout(
|
|
2687
|
-
}
|
|
2705
|
+
y();
|
|
2706
|
+
}, C = () => {
|
|
2707
|
+
l(!1), clearTimeout(m.current), m.current = setTimeout(S, Zt);
|
|
2708
|
+
}, w = (e) => {
|
|
2709
|
+
h.current = e.pointerType, e.pointerType === "mouse" && (g.current = {
|
|
2710
|
+
x: e.clientX,
|
|
2711
|
+
y: e.clientY
|
|
2712
|
+
}, C());
|
|
2713
|
+
}, D = (e) => {
|
|
2714
|
+
h.current = e.pointerType, _.current = !0;
|
|
2715
|
+
}, O = () => {
|
|
2716
|
+
_.current = !1;
|
|
2717
|
+
}, k = p(!0);
|
|
2688
2718
|
return /* @__PURE__ */ E("div", {
|
|
2689
2719
|
css: en,
|
|
2690
|
-
ref:
|
|
2691
|
-
onPointerMove:
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
}, S());
|
|
2696
|
-
},
|
|
2697
|
-
onPointerDown: (e) => {
|
|
2698
|
-
h.current = e.pointerType;
|
|
2699
|
-
},
|
|
2720
|
+
ref: b,
|
|
2721
|
+
onPointerMove: w,
|
|
2722
|
+
onPointerDown: D,
|
|
2723
|
+
onPointerUp: O,
|
|
2724
|
+
onPointerCancel: O,
|
|
2700
2725
|
onMouseOut: (e) => {
|
|
2701
2726
|
let t = e.relatedTarget;
|
|
2702
2727
|
if (t && e.currentTarget.contains(t)) return;
|
|
2703
2728
|
let { left: n, right: r, top: i, bottom: a } = e.currentTarget.getBoundingClientRect();
|
|
2704
|
-
e.clientX >= n && e.clientX < r && e.clientY >= i && e.clientY < a || (clearTimeout(m.current),
|
|
2729
|
+
e.clientX >= n && e.clientX < r && e.clientY >= i && e.clientY < a || (clearTimeout(m.current), y());
|
|
2705
2730
|
},
|
|
2706
2731
|
className: c ? "hide" : "",
|
|
2707
2732
|
tabIndex: a === "player" ? -1 : void 0,
|
|
@@ -2718,23 +2743,27 @@ ${gt}
|
|
|
2718
2743
|
}, e)),
|
|
2719
2744
|
i === !1 ? null : /* @__PURE__ */ T(Vt, {
|
|
2720
2745
|
shortcuts: a,
|
|
2721
|
-
root:
|
|
2746
|
+
root: v
|
|
2722
2747
|
}),
|
|
2723
2748
|
/* @__PURE__ */ T(Wt, {}),
|
|
2724
2749
|
/* @__PURE__ */ T(Xt, {}),
|
|
2725
|
-
f?.armed &&
|
|
2750
|
+
f?.armed && v.current ? /* @__PURE__ */ T("div", {
|
|
2726
2751
|
className: "pass-through",
|
|
2727
|
-
style: { clipPath: $t(f.armed,
|
|
2752
|
+
style: { clipPath: $t(f.armed, v.current.getBoundingClientRect()) },
|
|
2728
2753
|
onPointerOver: f.disarm
|
|
2729
2754
|
}) : null,
|
|
2730
2755
|
/* @__PURE__ */ E("div", {
|
|
2731
2756
|
className: "video",
|
|
2732
|
-
onClick: () => {
|
|
2757
|
+
onClick: (e) => {
|
|
2733
2758
|
if (h.current === "mouse") {
|
|
2734
|
-
|
|
2759
|
+
if (e.detail > 1) return;
|
|
2760
|
+
k.current = s.paused, s.togglePaused();
|
|
2735
2761
|
return;
|
|
2736
2762
|
}
|
|
2737
|
-
c ?
|
|
2763
|
+
c ? C() : (clearTimeout(m.current), y());
|
|
2764
|
+
},
|
|
2765
|
+
onDoubleClick: () => {
|
|
2766
|
+
h.current === "mouse" && (k.current ? s.pause() : s.play().catch(() => {}), s.toggleFullscreen());
|
|
2738
2767
|
},
|
|
2739
2768
|
children: [t ? /* @__PURE__ */ T("video", {
|
|
2740
2769
|
ref: t,
|
|
@@ -2780,75 +2809,79 @@ ${gt}
|
|
|
2780
2809
|
L,
|
|
2781
2810
|
P,
|
|
2782
2811
|
R
|
|
2783
|
-
]), { thumbnails: ee, requestThumbnail:
|
|
2812
|
+
]), { thumbnails: ee, requestThumbnail: te } = ue({
|
|
2784
2813
|
publicPath: c,
|
|
2785
2814
|
workerUrl: l,
|
|
2786
2815
|
length: g === !1 ? void 0 : o,
|
|
2787
2816
|
read: h ?? u,
|
|
2788
2817
|
downloadedRanges: s
|
|
2789
|
-
}),
|
|
2818
|
+
}), B = n?.thumbnails?.all ?? ee, { toggle: V, mode: ne, burnedIn: H } = de(x, C), U = _e(n?.media ?? null, n?.pictureInPicture), G = W((e) => e.setSourceState), K = n ? !!n.media : o !== void 0;
|
|
2790
2819
|
d(() => {
|
|
2791
|
-
|
|
2820
|
+
G({
|
|
2792
2821
|
title: i,
|
|
2793
2822
|
size: o,
|
|
2794
2823
|
downloadedRanges: s,
|
|
2795
|
-
hasMedia:
|
|
2824
|
+
hasMedia: K
|
|
2796
2825
|
});
|
|
2797
2826
|
}, [
|
|
2798
|
-
|
|
2827
|
+
G,
|
|
2799
2828
|
i,
|
|
2800
2829
|
o,
|
|
2801
2830
|
s,
|
|
2802
|
-
|
|
2831
|
+
K
|
|
2803
2832
|
]);
|
|
2804
|
-
let
|
|
2833
|
+
let q = n?.thumbnails?.at;
|
|
2805
2834
|
d(() => {
|
|
2806
|
-
|
|
2807
|
-
thumbnails:
|
|
2808
|
-
thumbnailAt:
|
|
2809
|
-
requestThumbnail:
|
|
2810
|
-
togglePictureInPicture:
|
|
2811
|
-
pictureInPictureMode:
|
|
2812
|
-
burnedInSubtitles:
|
|
2835
|
+
G({
|
|
2836
|
+
thumbnails: B,
|
|
2837
|
+
thumbnailAt: q,
|
|
2838
|
+
requestThumbnail: te,
|
|
2839
|
+
togglePictureInPicture: V,
|
|
2840
|
+
pictureInPictureMode: ne,
|
|
2841
|
+
burnedInSubtitles: H
|
|
2813
2842
|
});
|
|
2814
2843
|
}, [
|
|
2815
|
-
|
|
2816
|
-
V,
|
|
2817
|
-
J,
|
|
2844
|
+
G,
|
|
2818
2845
|
B,
|
|
2819
|
-
|
|
2846
|
+
q,
|
|
2820
2847
|
te,
|
|
2821
|
-
|
|
2848
|
+
V,
|
|
2849
|
+
ne,
|
|
2850
|
+
H
|
|
2822
2851
|
]), d(() => {
|
|
2823
|
-
|
|
2824
|
-
}, [
|
|
2825
|
-
|
|
2826
|
-
}, [
|
|
2827
|
-
let
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
subtitleTracks:
|
|
2852
|
+
G({ volumeNormalizer: z });
|
|
2853
|
+
}, [G, z]), d(() => {
|
|
2854
|
+
G({ passThroughPictureInPicture: U });
|
|
2855
|
+
}, [G, U]);
|
|
2856
|
+
let J = n?.subtitles?.selection, Y = n?.audioTracks?.selection;
|
|
2857
|
+
d(() => {
|
|
2858
|
+
J && G({
|
|
2859
|
+
subtitleTracks: J.options.map(({ id: e, label: t, disabled: n }) => ({
|
|
2831
2860
|
id: e,
|
|
2832
2861
|
label: t,
|
|
2833
2862
|
disabled: n
|
|
2834
2863
|
})),
|
|
2835
|
-
selectedSubtitleTrack:
|
|
2836
|
-
selectSubtitleTrack: (e) =>
|
|
2837
|
-
subtitleOffLabel:
|
|
2864
|
+
selectedSubtitleTrack: J.selectedId ?? void 0,
|
|
2865
|
+
selectSubtitleTrack: (e) => J.select(e == null ? null : String(e)),
|
|
2866
|
+
subtitleOffLabel: J.offLabel
|
|
2838
2867
|
});
|
|
2839
|
-
}, [
|
|
2840
|
-
|
|
2841
|
-
audioTracks:
|
|
2868
|
+
}, [G, J]), d(() => {
|
|
2869
|
+
Y && G({
|
|
2870
|
+
audioTracks: Y.options.map(({ id: e, label: t, disabled: n }) => ({
|
|
2842
2871
|
id: e,
|
|
2843
2872
|
label: t,
|
|
2844
2873
|
disabled: n
|
|
2845
2874
|
})),
|
|
2846
|
-
selectedAudioTrack:
|
|
2847
|
-
selectAudioTrack: (e) =>
|
|
2875
|
+
selectedAudioTrack: Y.selectedId ?? void 0,
|
|
2876
|
+
selectAudioTrack: (e) => Y.select(String(e))
|
|
2848
2877
|
});
|
|
2849
|
-
}, [
|
|
2850
|
-
|
|
2851
|
-
|
|
2878
|
+
}, [G, Y]);
|
|
2879
|
+
let X = n?.quality?.selection ?? null;
|
|
2880
|
+
return d(() => {
|
|
2881
|
+
G({ quality: X });
|
|
2882
|
+
}, [G, X]), d(() => {
|
|
2883
|
+
a && G({ chapters: a });
|
|
2884
|
+
}, [G, a]), /* @__PURE__ */ T(nn, {
|
|
2852
2885
|
ref: v,
|
|
2853
2886
|
onVideoRef: n ? void 0 : S,
|
|
2854
2887
|
onSubtitleRef: w,
|
|
@@ -2929,7 +2962,7 @@ ${gt}
|
|
|
2929
2962
|
overflow: hidden;
|
|
2930
2963
|
`, on = (e) => /* @__PURE__ */ T("div", {
|
|
2931
2964
|
css: an,
|
|
2932
|
-
children: /* @__PURE__ */ T(
|
|
2965
|
+
children: /* @__PURE__ */ T(U.Provider, { children: /* @__PURE__ */ T(rn, {
|
|
2933
2966
|
options: e,
|
|
2934
2967
|
children: e.children
|
|
2935
2968
|
}) })
|
|
@@ -2974,4 +3007,4 @@ ${gt}
|
|
|
2974
3007
|
throw Error(`Unknown source type: ${e}`);
|
|
2975
3008
|
};
|
|
2976
3009
|
//#endregion
|
|
2977
|
-
export { on as MediaPlayer, on as default,
|
|
3010
|
+
export { on as MediaPlayer, on as default, U as Player, e as SUBTITLES_OFF, a as createPictureInPicture, o as createSubtitleRenderer, r as createThumbnailGenerator, fn as inputToRemuxerInput, cn as isDelegatedTracks, sn as isExternalThumbnails, H as sourceFeature, i as startPlayback, de as usePictureInPicture, W as usePlayer, ue as useSeekThumbnails };
|
|
@@ -17,7 +17,7 @@ export declare const popoverStyle: import("@emotion/utils").SerializedStyles;
|
|
|
17
17
|
*/
|
|
18
18
|
export declare const TrackMenu: ({ title, tracks, selected, onSelect, onBack, offLabel, pending, failed }: {
|
|
19
19
|
title: string;
|
|
20
|
-
tracks: TrackChoice[];
|
|
20
|
+
tracks: readonly TrackChoice[];
|
|
21
21
|
selected: string | number | undefined;
|
|
22
22
|
onSelect: (id: string | number | undefined) => void;
|
|
23
23
|
/** Absent means the menu was opened straight from the bar, so the header is a label and no more. */
|
package/dist/react/player.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export declare const Player: import("@videojs/react").CreatePlayerResult<import(
|
|
|
15
15
|
audioTracks: import("./source-feature").TrackChoice[];
|
|
16
16
|
selectedAudioTrack: string | number | undefined;
|
|
17
17
|
selectAudioTrack: (id: string | number) => void | Promise<void>;
|
|
18
|
+
quality: import("./media").DelegatedSelection | null;
|
|
18
19
|
hideUI: boolean;
|
|
19
20
|
setHideUI: (hide: boolean) => void;
|
|
20
21
|
hasMedia: boolean;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { MediaChapter, MediaIndex, PictureInPictureMode, ThumbnailImage } from '../engine';
|
|
2
|
+
import type { DelegatedSelection } from './media';
|
|
2
3
|
/**
|
|
3
4
|
* One row of a track menu, already named.
|
|
4
5
|
*
|
|
@@ -162,6 +163,8 @@ export type SourceState = {
|
|
|
162
163
|
audioTracks: TrackChoice[];
|
|
163
164
|
selectedAudioTrack: string | number | undefined;
|
|
164
165
|
selectAudioTrack: (id: string | number) => void | Promise<void>;
|
|
166
|
+
/** The settings menu's quality choices, from a media the player does not own. null when none are offered. */
|
|
167
|
+
quality: DelegatedSelection | null;
|
|
165
168
|
/** Chrome auto-hide. True means the controls, title and cursor are hidden. */
|
|
166
169
|
hideUI: boolean;
|
|
167
170
|
setHideUI: (hide: boolean) => void;
|
|
@@ -307,6 +310,8 @@ export declare const sourceFeature: import("@videojs/react").PlayerFeature<{
|
|
|
307
310
|
audioTracks: TrackChoice[];
|
|
308
311
|
selectedAudioTrack: string | number | undefined;
|
|
309
312
|
selectAudioTrack: (id: string | number) => void | Promise<void>;
|
|
313
|
+
/** The settings menu's quality choices, from a media the player does not own. null when none are offered. */
|
|
314
|
+
quality: DelegatedSelection | null;
|
|
310
315
|
/** Chrome auto-hide. True means the controls, title and cursor are hidden. */
|
|
311
316
|
hideUI: boolean;
|
|
312
317
|
setHideUI: (hide: boolean) => void;
|
|
@@ -193,6 +193,12 @@ export type MediaPlayerRemoteOptions = CommonOptions & {
|
|
|
193
193
|
/** The source renders these; the player draws the menu and reports the pick. */
|
|
194
194
|
subtitles?: DelegatedTracks;
|
|
195
195
|
audioTracks?: DelegatedTracks;
|
|
196
|
+
/**
|
|
197
|
+
* The source's own quality choices, as a row of the settings menu that shows the selected one.
|
|
198
|
+
* Offered only with two or more rows to choose between; `offLabel` names a row with no quality
|
|
199
|
+
* picked, such as an automatic one, whose id is null.
|
|
200
|
+
*/
|
|
201
|
+
quality?: DelegatedTracks;
|
|
196
202
|
/**
|
|
197
203
|
* Offer picture in picture, entered by the host from inside the media's own document. Off by
|
|
198
204
|
* default, and then there is no control at all. See `PassThroughPictureInPicture` for what the
|
package/package.json
CHANGED
|
@@ -147,6 +147,8 @@ export const Chrome = ({ ref, onVideoRef, onSubtitleRef, overlay, controls, shor
|
|
|
147
147
|
const lastPointerType = useRef<string>('mouse')
|
|
148
148
|
// where the mouse last was, which is where it still is when nothing has moved
|
|
149
149
|
const pointer = useRef({ x: -1, y: -1 })
|
|
150
|
+
// a mouse button held down with the mouse still is a drag on the seekbar or the volume, not yet over
|
|
151
|
+
const pressed = useRef(false)
|
|
150
152
|
const root = useRef<HTMLDivElement | null>(null)
|
|
151
153
|
|
|
152
154
|
useEffect(() => () => clearTimeout(autoHide.current), [])
|
|
@@ -189,9 +191,10 @@ export const Chrome = ({ ref, onVideoRef, onSubtitleRef, overlay, controls, shor
|
|
|
189
191
|
* and a right click offers the browser's video menu rather than the control's. That is how a link
|
|
190
192
|
* drawn in an overlay stops behaving like a link, which is what this exists to stop.
|
|
191
193
|
*
|
|
192
|
-
* Only what actually takes pointer events can be hit
|
|
193
|
-
*
|
|
194
|
-
*
|
|
194
|
+
* Only what actually takes pointer events can be hit: the chrome's own layers opt out, and an
|
|
195
|
+
* overlay item opts out until an app opts a child back in. The picture and the control bar are
|
|
196
|
+
* excluded here because resting on them is exactly when hiding is right, unless a button is held
|
|
197
|
+
* on the bar or one of its menus is open, which are still in use.
|
|
195
198
|
*
|
|
196
199
|
* The mouse only. A finger has no resting position, and `elementFromPoint` on the last tap would
|
|
197
200
|
* keep the controls up forever after one press.
|
|
@@ -205,7 +208,9 @@ export const Chrome = ({ ref, onVideoRef, onSubtitleRef, overlay, controls, shor
|
|
|
205
208
|
const { x, y } = pointer.current
|
|
206
209
|
if (!root.current || x < 0) return false
|
|
207
210
|
const hit = document.elementFromPoint(x, y)
|
|
208
|
-
|
|
211
|
+
if (!hit || !root.current.contains(hit) || hit.closest('.video')) return false
|
|
212
|
+
if (hit.closest('.control-bar')) return pressed.current || !!root.current.querySelector('.control-bar [aria-expanded="true"]')
|
|
213
|
+
return true
|
|
209
214
|
}
|
|
210
215
|
|
|
211
216
|
const hideUnlessTheMouseIsOnAControl = () => {
|
|
@@ -233,11 +238,20 @@ export const Chrome = ({ ref, onVideoRef, onSubtitleRef, overlay, controls, shor
|
|
|
233
238
|
|
|
234
239
|
const onPointerDown = (event: React.PointerEvent<HTMLDivElement>) => {
|
|
235
240
|
lastPointerType.current = event.pointerType
|
|
241
|
+
pressed.current = true
|
|
236
242
|
}
|
|
237
243
|
|
|
244
|
+
const onPointerUp = () => {
|
|
245
|
+
pressed.current = false
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const pausedBeforeClick = useRef(true)
|
|
249
|
+
|
|
238
250
|
// play/pause with a mouse, show or hide the controls with a finger, which has no other way back
|
|
239
|
-
const onVideoClick = () => {
|
|
251
|
+
const onVideoClick = (event: React.MouseEvent) => {
|
|
240
252
|
if (lastPointerType.current === 'mouse') {
|
|
253
|
+
if (event.detail > 1) return
|
|
254
|
+
pausedBeforeClick.current = player.paused
|
|
241
255
|
player.togglePaused()
|
|
242
256
|
return
|
|
243
257
|
}
|
|
@@ -248,6 +262,15 @@ export const Chrome = ({ ref, onVideoRef, onSubtitleRef, overlay, controls, shor
|
|
|
248
262
|
}
|
|
249
263
|
}
|
|
250
264
|
|
|
265
|
+
// The first click toggled playback, so it is put back rather than toggled again: a remote media
|
|
266
|
+
// reports its pause late, and a second toggle would read it as still playing and pause it twice.
|
|
267
|
+
const onVideoDoubleClick = () => {
|
|
268
|
+
if (lastPointerType.current !== 'mouse') return
|
|
269
|
+
if (pausedBeforeClick.current) player.pause()
|
|
270
|
+
else void player.play().catch(() => {})
|
|
271
|
+
void player.toggleFullscreen()
|
|
272
|
+
}
|
|
273
|
+
|
|
251
274
|
// relatedTarget alone is not enough: the browser fires mouseout with a null one while the pointer
|
|
252
275
|
// still sits over the video, so a null is confirmed against the pointer's own coordinates.
|
|
253
276
|
const onMouseOut: React.DOMAttributes<HTMLDivElement>['onMouseOut'] = (ev) => {
|
|
@@ -266,6 +289,8 @@ export const Chrome = ({ ref, onVideoRef, onSubtitleRef, overlay, controls, shor
|
|
|
266
289
|
ref={setRoot}
|
|
267
290
|
onPointerMove={onPointerMove}
|
|
268
291
|
onPointerDown={onPointerDown}
|
|
292
|
+
onPointerUp={onPointerUp}
|
|
293
|
+
onPointerCancel={onPointerUp}
|
|
269
294
|
onMouseOut={onMouseOut}
|
|
270
295
|
className={hideUI ? 'hide' : ''}
|
|
271
296
|
// focusable, so a click on the picture puts the focus, and with it the shortcuts, inside the player
|
|
@@ -294,7 +319,7 @@ export const Chrome = ({ ref, onVideoRef, onSubtitleRef, overlay, controls, shor
|
|
|
294
319
|
/>
|
|
295
320
|
)
|
|
296
321
|
: null}
|
|
297
|
-
<div className="video" onClick={onVideoClick}>
|
|
322
|
+
<div className="video" onClick={onVideoClick} onDoubleClick={onVideoDoubleClick}>
|
|
298
323
|
{onVideoRef ? <video ref={onVideoRef} playsInline /> : null}
|
|
299
324
|
{children}
|
|
300
325
|
</div>
|
|
@@ -308,6 +308,7 @@ export const ControlBar = (
|
|
|
308
308
|
// Armed, the whole bar takes no pointer events, or the rows around the control would still catch
|
|
309
309
|
// the click at its box. The chrome's layer covers everything but the control meanwhile.
|
|
310
310
|
<div
|
|
311
|
+
className='control-bar'
|
|
311
312
|
css={style}
|
|
312
313
|
style={{ ...hideUI ? { opacity: '0', pointerEvents: 'none' } : armed ? { pointerEvents: 'none' } : {} }}
|
|
313
314
|
>
|
|
@@ -107,7 +107,8 @@ ${popoverStyle}
|
|
|
107
107
|
enum PopoverContent {
|
|
108
108
|
Default,
|
|
109
109
|
PlaybackRate,
|
|
110
|
-
Audio
|
|
110
|
+
Audio,
|
|
111
|
+
Quality
|
|
111
112
|
}
|
|
112
113
|
|
|
113
114
|
export const SettingsAction = () => {
|
|
@@ -116,6 +117,7 @@ export const SettingsAction = () => {
|
|
|
116
117
|
const audioTracks = usePlayer((state) => state.audioTracks)
|
|
117
118
|
const selectedAudioTrack = usePlayer((state) => state.selectedAudioTrack)
|
|
118
119
|
const selectAudioTrack = usePlayer((state) => state.selectAudioTrack)
|
|
120
|
+
const quality = usePlayer((state) => state.quality)
|
|
119
121
|
const volumeNormalizer = usePlayer((state) => state.volumeNormalizer)
|
|
120
122
|
|
|
121
123
|
const [popoverContent, setPopoverContent] = useState(PopoverContent.Default)
|
|
@@ -134,6 +136,15 @@ export const SettingsAction = () => {
|
|
|
134
136
|
runSelect(id, () => selectAudioTrack(id))
|
|
135
137
|
}
|
|
136
138
|
|
|
139
|
+
const chooseQuality = (id: string | number | undefined) => {
|
|
140
|
+
if (!quality) return
|
|
141
|
+
runSelect(id ?? null, () => quality.select(id === undefined ? null : String(id)))
|
|
142
|
+
}
|
|
143
|
+
const qualityRows = quality ? quality.options.length + (quality.offLabel ? 1 : 0) : 0
|
|
144
|
+
const qualityLabel = quality?.selectedId == null
|
|
145
|
+
? quality?.offLabel
|
|
146
|
+
: quality.options.find(({ id }) => id === quality.selectedId)?.label
|
|
147
|
+
|
|
137
148
|
const changePopoverContent = (newPopoverContent: PopoverContent) => () => {
|
|
138
149
|
setPopoverContent(newPopoverContent)
|
|
139
150
|
}
|
|
@@ -179,6 +190,17 @@ export const SettingsAction = () => {
|
|
|
179
190
|
</div>
|
|
180
191
|
)
|
|
181
192
|
: null}
|
|
193
|
+
{qualityRows > 1
|
|
194
|
+
? (
|
|
195
|
+
<div onClick={changePopoverContent(PopoverContent.Quality)}>
|
|
196
|
+
<div>Quality</div>
|
|
197
|
+
<div>
|
|
198
|
+
{qualityLabel ? <span className='secondary'>{qualityLabel}</span> : null}
|
|
199
|
+
<ChevronRight />
|
|
200
|
+
</div>
|
|
201
|
+
</div>
|
|
202
|
+
)
|
|
203
|
+
: null}
|
|
182
204
|
<div onClick={changePopoverContent(PopoverContent.PlaybackRate)}>
|
|
183
205
|
<div>Playback speed</div>
|
|
184
206
|
<div>
|
|
@@ -255,6 +277,20 @@ export const SettingsAction = () => {
|
|
|
255
277
|
/>
|
|
256
278
|
)
|
|
257
279
|
}
|
|
280
|
+
{
|
|
281
|
+
open && popoverContent === PopoverContent.Quality && quality && (
|
|
282
|
+
<TrackMenu
|
|
283
|
+
title='Quality'
|
|
284
|
+
tracks={quality.options}
|
|
285
|
+
selected={quality.selectedId ?? undefined}
|
|
286
|
+
onSelect={chooseQuality}
|
|
287
|
+
onBack={changePopoverContent(PopoverContent.Default)}
|
|
288
|
+
offLabel={quality.offLabel}
|
|
289
|
+
pending={pending}
|
|
290
|
+
failed={failed}
|
|
291
|
+
/>
|
|
292
|
+
)
|
|
293
|
+
}
|
|
258
294
|
</div>
|
|
259
295
|
)
|
|
260
296
|
}
|
|
@@ -154,7 +154,7 @@ export const popoverStyle = css`
|
|
|
154
154
|
export const TrackMenu = (
|
|
155
155
|
{ title, tracks, selected, onSelect, onBack, offLabel, pending, failed }: {
|
|
156
156
|
title: string
|
|
157
|
-
tracks: TrackChoice[]
|
|
157
|
+
tracks: readonly TrackChoice[]
|
|
158
158
|
selected: string | number | undefined
|
|
159
159
|
onSelect: (id: string | number | undefined) => void
|
|
160
160
|
/** Absent means the menu was opened straight from the bar, so the header is a label and no more. */
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { MediaChapter, MediaIndex, PictureInPictureMode, ThumbnailImage } from '../engine'
|
|
2
|
+
import type { DelegatedSelection } from './media'
|
|
2
3
|
|
|
3
4
|
import { definePlayerFeature } from '@videojs/core/dom'
|
|
4
5
|
|
|
@@ -175,6 +176,9 @@ export type SourceState = {
|
|
|
175
176
|
selectedAudioTrack: string | number | undefined
|
|
176
177
|
selectAudioTrack: (id: string | number) => void | Promise<void>
|
|
177
178
|
|
|
179
|
+
/** The settings menu's quality choices, from a media the player does not own. null when none are offered. */
|
|
180
|
+
quality: DelegatedSelection | null
|
|
181
|
+
|
|
178
182
|
/** Chrome auto-hide. True means the controls, title and cursor are hidden. */
|
|
179
183
|
hideUI: boolean
|
|
180
184
|
setHideUI: (hide: boolean) => void
|
|
@@ -283,6 +287,7 @@ const initialState: SourceState = {
|
|
|
283
287
|
audioTracks: [],
|
|
284
288
|
selectedAudioTrack: undefined,
|
|
285
289
|
selectAudioTrack: () => {},
|
|
290
|
+
quality: null,
|
|
286
291
|
hideUI: false,
|
|
287
292
|
setHideUI: () => {},
|
|
288
293
|
hasMedia: false,
|
|
@@ -220,6 +220,12 @@ export type MediaPlayerRemoteOptions =
|
|
|
220
220
|
/** The source renders these; the player draws the menu and reports the pick. */
|
|
221
221
|
subtitles?: DelegatedTracks
|
|
222
222
|
audioTracks?: DelegatedTracks
|
|
223
|
+
/**
|
|
224
|
+
* The source's own quality choices, as a row of the settings menu that shows the selected one.
|
|
225
|
+
* Offered only with two or more rows to choose between; `offLabel` names a row with no quality
|
|
226
|
+
* picked, such as an automatic one, whose id is null.
|
|
227
|
+
*/
|
|
228
|
+
quality?: DelegatedTracks
|
|
223
229
|
/**
|
|
224
230
|
* Offer picture in picture, entered by the host from inside the media's own document. Off by
|
|
225
231
|
* default, and then there is no control at all. See `PassThroughPictureInPicture` for what the
|
|
@@ -397,6 +403,11 @@ const PlayerRoot = ({ options, children }: { options: MediaPlayerOptions, childr
|
|
|
397
403
|
})
|
|
398
404
|
}, [setSourceState, audio])
|
|
399
405
|
|
|
406
|
+
const quality = remote?.quality?.selection ?? null
|
|
407
|
+
useEffect(() => {
|
|
408
|
+
setSourceState({ quality })
|
|
409
|
+
}, [setSourceState, quality])
|
|
410
|
+
|
|
400
411
|
/*
|
|
401
412
|
* Guarded, and its own effect, for the reason the ones above are.
|
|
402
413
|
*
|