itube-modern-player 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +630 -0
- package/dist/core/dom.d.ts +18 -0
- package/dist/core/events.d.ts +10 -0
- package/dist/core/icons.d.ts +3 -0
- package/dist/core/labels.d.ts +2 -0
- package/dist/core/localeRegistry.d.ts +7 -0
- package/dist/core/locales.d.ts +11 -0
- package/dist/core.cjs +5 -0
- package/dist/core.cjs.map +1 -0
- package/dist/core.js +1668 -0
- package/dist/core.js.map +1 -0
- package/dist/coreEntry.d.ts +14 -0
- package/dist/features/ads/manager.d.ts +48 -0
- package/dist/features/ads/vast.d.ts +9 -0
- package/dist/features/chapters.d.ts +12 -0
- package/dist/features/heatmap.d.ts +17 -0
- package/dist/features/hls.d.ts +26 -0
- package/dist/features/thumbnails.d.ts +11 -0
- package/dist/global.d.ts +2 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/itube-modern-player.iife.js +5 -0
- package/dist/itube-modern-player.iife.js.map +1 -0
- package/dist/labels-C3gAZEm-.js +41 -0
- package/dist/labels-C3gAZEm-.js.map +1 -0
- package/dist/labels-DTgTxMuq.cjs +2 -0
- package/dist/labels-DTgTxMuq.cjs.map +1 -0
- package/dist/lazy.cjs +2 -0
- package/dist/lazy.cjs.map +1 -0
- package/dist/lazy.d.ts +23 -0
- package/dist/lazy.js +60 -0
- package/dist/lazy.js.map +1 -0
- package/dist/locales.cjs +2 -0
- package/dist/locales.cjs.map +1 -0
- package/dist/locales.js +380 -0
- package/dist/locales.js.map +1 -0
- package/dist/localesEntry.d.ts +8 -0
- package/dist/player.d.ts +142 -0
- package/dist/style.css +1 -0
- package/dist/types.d.ts +443 -0
- package/dist/ui/controls.d.ts +90 -0
- package/dist/ui/menu.d.ts +29 -0
- package/dist/ui/overlays.d.ts +51 -0
- package/dist/ui/playlistPanel.d.ts +16 -0
- package/dist/ui/progress.d.ts +41 -0
- package/dist/ui/scenesPanel.d.ts +19 -0
- package/dist/vue.cjs +2 -0
- package/dist/vue.cjs.map +1 -0
- package/dist/vue.d.ts +67 -0
- package/dist/vue.js +89 -0
- package/dist/vue.js.map +1 -0
- package/package.json +86 -0
package/dist/core.js
ADDED
|
@@ -0,0 +1,1668 @@
|
|
|
1
|
+
import { d as I } from "./labels-C3gAZEm-.js";
|
|
2
|
+
function l(r, t, e) {
|
|
3
|
+
const i = document.createElement(r);
|
|
4
|
+
if (t && (i.className = t), e)
|
|
5
|
+
for (const [s, n] of Object.entries(e)) i.setAttribute(s, n);
|
|
6
|
+
return i;
|
|
7
|
+
}
|
|
8
|
+
function m(r, t, e) {
|
|
9
|
+
const i = l("button", `imp-btn ${r}`, { type: "button", "aria-label": t, title: t });
|
|
10
|
+
return i.innerHTML = e, i;
|
|
11
|
+
}
|
|
12
|
+
function E(r, t, e) {
|
|
13
|
+
r.innerHTML = t, e !== void 0 && (r.setAttribute("aria-label", e), r.setAttribute("title", e));
|
|
14
|
+
}
|
|
15
|
+
function y(r, t, e) {
|
|
16
|
+
return Math.min(e, Math.max(t, r));
|
|
17
|
+
}
|
|
18
|
+
function w(r) {
|
|
19
|
+
if (!Number.isFinite(r) || r < 0) return "0:00";
|
|
20
|
+
const t = Math.floor(r % 60), e = Math.floor(r / 60 % 60), i = Math.floor(r / 3600), s = i > 0 ? String(e).padStart(2, "0") : String(e), n = String(t).padStart(2, "0");
|
|
21
|
+
return i > 0 ? `${i}:${s}:${n}` : `${s}:${n}`;
|
|
22
|
+
}
|
|
23
|
+
function R(r) {
|
|
24
|
+
const t = r.trim().match(/^(?:(\d+):)?(\d{1,2}):(\d{2})(?:[.,](\d{1,3}))?$/);
|
|
25
|
+
if (!t) return null;
|
|
26
|
+
const e = t[1] ? Number(t[1]) : 0, i = Number(t[2]), s = Number(t[3]), n = t[4] ? Number(t[4].padEnd(3, "0")) : 0;
|
|
27
|
+
return e * 3600 + i * 60 + s + n / 1e3;
|
|
28
|
+
}
|
|
29
|
+
function U(r) {
|
|
30
|
+
const t = [], e = r.replace(/\r\n?/g, `
|
|
31
|
+
`).split(/\n\n+/);
|
|
32
|
+
for (const i of e) {
|
|
33
|
+
const s = i.split(`
|
|
34
|
+
`).filter((f) => f.trim() !== "");
|
|
35
|
+
if (s.length === 0) continue;
|
|
36
|
+
let n = s.findIndex((f) => f.includes("-->"));
|
|
37
|
+
if (n === -1) continue;
|
|
38
|
+
const [o, a] = s[n].split("-->"), h = R(o), c = R((a ?? "").split(" ")[1] ?? a ?? "") ?? R(a ?? "");
|
|
39
|
+
if (h === null || c === null) continue;
|
|
40
|
+
const u = s.slice(n + 1).join(`
|
|
41
|
+
`).trim();
|
|
42
|
+
u && t.push({ start: h, end: c, text: u });
|
|
43
|
+
}
|
|
44
|
+
return t;
|
|
45
|
+
}
|
|
46
|
+
function G(r, t) {
|
|
47
|
+
try {
|
|
48
|
+
return new URL(r, new URL(t, window.location.href)).toString();
|
|
49
|
+
} catch {
|
|
50
|
+
return r;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
class Y {
|
|
54
|
+
constructor() {
|
|
55
|
+
this.listeners = /* @__PURE__ */ new Map();
|
|
56
|
+
}
|
|
57
|
+
/** Subscribe. Returns an unsubscribe function. */
|
|
58
|
+
on(t, e) {
|
|
59
|
+
let i = this.listeners.get(t);
|
|
60
|
+
return i || (i = /* @__PURE__ */ new Set(), this.listeners.set(t, i)), i.add(e), () => this.off(t, e);
|
|
61
|
+
}
|
|
62
|
+
once(t, e) {
|
|
63
|
+
const i = this.on(t, (s) => {
|
|
64
|
+
i(), e(s);
|
|
65
|
+
});
|
|
66
|
+
return i;
|
|
67
|
+
}
|
|
68
|
+
off(t, e) {
|
|
69
|
+
this.listeners.get(t)?.delete(e);
|
|
70
|
+
}
|
|
71
|
+
emit(t, e) {
|
|
72
|
+
const i = this.listeners.get(t);
|
|
73
|
+
if (i)
|
|
74
|
+
for (const s of [...i])
|
|
75
|
+
try {
|
|
76
|
+
s(e);
|
|
77
|
+
} catch (n) {
|
|
78
|
+
console.error("[itube-player] listener error", n);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
removeAll() {
|
|
82
|
+
this.listeners.clear();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const p = (r, t = "0 0 24 24") => `<svg viewBox="${t}" fill="currentColor" aria-hidden="true" focusable="false">${r}</svg>`, J = {
|
|
86
|
+
play: p('<path d="M8 5.14v13.72c0 .8.87 1.3 1.56.88l10.54-6.86a1.05 1.05 0 0 0 0-1.76L9.56 4.26C8.87 3.84 8 4.34 8 5.14Z"/>'),
|
|
87
|
+
pause: p('<rect x="6" y="5" width="4" height="14" rx="1"/><rect x="14" y="5" width="4" height="14" rx="1"/>'),
|
|
88
|
+
replay: p('<path d="M12 5V2.5L7.5 6 12 9.5V7a5 5 0 1 1-5 5H5a7 7 0 1 0 7-7Z"/>'),
|
|
89
|
+
bigPlay: p('<path d="M8 5.14v13.72c0 .8.87 1.3 1.56.88l10.54-6.86a1.05 1.05 0 0 0 0-1.76L9.56 4.26C8.87 3.84 8 4.34 8 5.14Z"/>'),
|
|
90
|
+
volumeHigh: p('<path d="M4 9v6h3.5L12 19.5v-15L7.5 9H4Z"/><path d="M14.5 8.6a4.5 4.5 0 0 1 0 6.8v-1.9a2.5 2.5 0 0 0 0-3v-1.9Z"/><path d="M14.5 5.2a8 8 0 0 1 0 13.6v-2a6 6 0 0 0 0-9.6v-2Z"/>'),
|
|
91
|
+
volumeLow: p('<path d="M4 9v6h3.5L12 19.5v-15L7.5 9H4Z"/><path d="M14.5 8.6a4.5 4.5 0 0 1 0 6.8v-1.9a2.5 2.5 0 0 0 0-3v-1.9Z"/>'),
|
|
92
|
+
volumeMute: p('<path d="M4 9v6h3.5L12 19.5v-15L7.5 9H4Z"/><path d="m15.3 9.3 1.4 1.4 1.4-1.4 1.4 1.4-1.4 1.4 1.4 1.4-1.4 1.4-1.4-1.4-1.4 1.4-1.4-1.4 1.4-1.4-1.4-1.4 1.4-1.4Z"/>'),
|
|
93
|
+
fullscreen: p('<path d="M5 5h5v2H7v3H5V5Zm9 0h5v5h-2V7h-3V5ZM5 14h2v3h3v2H5v-5Zm12 0h2v5h-5v-2h3v-3Z"/>'),
|
|
94
|
+
fullscreenExit: p('<path d="M10 10H5V8h3V5h2v5Zm4 0V5h2v3h3v2h-5Zm-4 4v5H8v-3H5v-2h5Zm4 0h5v2h-3v3h-2v-5Z"/>'),
|
|
95
|
+
pip: p('<path d="M3 5h18v14H3V5Zm2 2v10h14V7H5Z"/><rect x="12" y="11" width="6" height="4"/>'),
|
|
96
|
+
settings: p('<path d="M12 8.5A3.5 3.5 0 1 0 12 15.5 3.5 3.5 0 0 0 12 8.5Zm0 2a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3Z"/><path d="M10.3 2.8 9.9 5.1a7 7 0 0 0-1.5.86l-2.2-.8-1.7 3 1.8 1.5a7 7 0 0 0 0 1.7l-1.8 1.5 1.7 3 2.2-.8c.46.36.97.65 1.5.86l.4 2.3h3.4l.4-2.3a7 7 0 0 0 1.5-.86l2.2.8 1.7-3-1.8-1.5a7 7 0 0 0 0-1.7l1.8-1.5-1.7-3-2.2.8a7 7 0 0 0-1.5-.86l-.4-2.3h-3.4Zm1.7 5.7a3.5 3.5 0 1 1 0 7 3.5 3.5 0 0 1 0-7Z"/>'),
|
|
97
|
+
speed: p('<path d="M12 4a9 9 0 0 0-9 9 8.96 8.96 0 0 0 1.62 5.16l1.64-1.15A7 7 0 0 1 5 13a7 7 0 1 1 12.74 4.01l1.64 1.15A8.96 8.96 0 0 0 21 13a9 9 0 0 0-9-9Z"/><path d="m15.6 8.3-3.95 3.13a1.5 1.5 0 1 0 1.92 1.92L16.7 9.4a.78.78 0 0 0-1.1-1.1Z"/>'),
|
|
98
|
+
scenes: p('<path d="M3 5h18v14H3V5Zm2 2v10h3V7H5Zm5 0v10h9V7h-9Zm-5 3h3v1H5v-1Zm0 3h3v1H5v-1Z"/>'),
|
|
99
|
+
shuffle: p('<path d="M4 6h2.6c1.5 0 2.9.7 3.8 1.9l4.1 5.4a2.75 2.75 0 0 0 2.2 1.1H19l-1.8-1.8 1.4-1.4 4.2 4.2-4.2 4.2-1.4-1.4L19 16.4h-2.3a4.75 4.75 0 0 1-3.8-1.9L8.8 9.1A2.75 2.75 0 0 0 6.6 8H4V6Z"/><path d="M19 7.6h-2.3c-.86 0-1.67.41-2.18 1.1l-.93 1.23-1.25-1.65.58-.77A4.75 4.75 0 0 1 16.7 5.6H19L17.2 3.8l1.4-1.4 4.2 4.2-4.2 4.2-1.4-1.4L19 7.6Z" transform="translate(0 1.2)"/>'),
|
|
100
|
+
repeat: p('<path d="M7 7h10v2.5L21 6l-4-3.5V5H5v6h2V7Zm10 10H7v-2.5L3 18l4 3.5V19h12v-6h-2v4Z"/>'),
|
|
101
|
+
subtitles: p('<path d="M3 5h18v14H3V5Zm2 2v10h14V7H5Zm2 3h6v2H7v-2Zm8 0h2v2h-2v-2ZM7 14h2v2H7v-2Zm4 0h6v2h-6v-2Z"/>'),
|
|
102
|
+
list: p('<path d="M4 6h2v2H4V6Zm4 0h12v2H8V6ZM4 11h2v2H4v-2Zm4 0h12v2H8v-2ZM4 16h2v2H4v-2Zm4 0h12v2H8v-2Z"/>'),
|
|
103
|
+
next: p('<path d="M6 5.5v13c0 .77.84 1.25 1.5.85l9-6.5a1 1 0 0 0 0-1.7l-9-6.5c-.66-.4-1.5.08-1.5.85Z"/><rect x="17" y="5" width="2.5" height="14" rx="1"/>'),
|
|
104
|
+
previous: p('<path d="M18 5.5v13c0 .77-.84 1.25-1.5.85l-9-6.5a1 1 0 0 1 0-1.7l9-6.5c.66-.4 1.5.08 1.5.85Z"/><rect x="4.5" y="5" width="2.5" height="14" rx="1"/>'),
|
|
105
|
+
seekForward: p('<path d="M12 4V1.5L17 5.5 12 9.5V7a5.5 5.5 0 1 0 5.5 5.5H20A8 8 0 1 1 12 4Z"/>'),
|
|
106
|
+
seekBack: p('<path d="M12 4V1.5L7 5.5 12 9.5V7a5.5 5.5 0 1 1-5.5 5.5H4A8 8 0 1 0 12 4Z"/>'),
|
|
107
|
+
like: p('<path d="M9 21H5a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h4v11Zm2 0h7.1a2 2 0 0 0 2-1.6l1.3-7a2 2 0 0 0-2-2.4H14V5.5A2.5 2.5 0 0 0 11.5 3l-.5.1V10h-.9L11 21Z"/>'),
|
|
108
|
+
dislike: p('<path d="M15 3h4a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2h-4V3Zm-2 0H5.9a2 2 0 0 0-2 1.6l-1.3 7a2 2 0 0 0 2 2.4H10v4.5A2.5 2.5 0 0 0 12.5 21l.5-.1V14h.9L13 3Z"/>'),
|
|
109
|
+
addTo: p('<path d="M4 6h12v2H4V6Zm0 4h12v2H4v-2Zm0 4h8v2H4v-2Zm14 0v-4h2v4h4v2h-4v4h-2v-4h-4v-2h4Z"/>'),
|
|
110
|
+
share: p('<path d="M18 8a3 3 0 1 0-2.83-4H15a3 3 0 0 0 .14 1.06L8.4 8.94a3 3 0 1 0 0 6.12l6.74 3.88A3 3 0 1 0 16 16.6l-6.74-3.88a3.03 3.03 0 0 0 0-1.44L16 7.4c.55.38 1.24.6 2 .6Z"/>'),
|
|
111
|
+
report: p('<path d="M5 3h2v18H5V3Zm4 1h10l-2.5 4L19 12H9V4Z"/>'),
|
|
112
|
+
more: p('<circle cx="12" cy="5" r="2"/><circle cx="12" cy="12" r="2"/><circle cx="12" cy="19" r="2"/>'),
|
|
113
|
+
close: p('<path d="m6.4 5 5.6 5.6L17.6 5 19 6.4 13.4 12l5.6 5.6-1.4 1.4-5.6-5.6L6.4 19 5 17.6 10.6 12 5 6.4 6.4 5Z"/>')
|
|
114
|
+
}, T = { en: I };
|
|
115
|
+
function Lt(r, t) {
|
|
116
|
+
T[r] = t;
|
|
117
|
+
}
|
|
118
|
+
function St(r) {
|
|
119
|
+
for (const [t, e] of Object.entries(r))
|
|
120
|
+
e && (T[t] = e);
|
|
121
|
+
}
|
|
122
|
+
function tt(r) {
|
|
123
|
+
return r && T[r] || I;
|
|
124
|
+
}
|
|
125
|
+
function xt() {
|
|
126
|
+
return Object.keys(T);
|
|
127
|
+
}
|
|
128
|
+
function et(r, t, e = 100) {
|
|
129
|
+
if (r.length === 0 || !Number.isFinite(t) || t <= 0) return [];
|
|
130
|
+
const i = new Array(e).fill(0);
|
|
131
|
+
let s = !1;
|
|
132
|
+
for (const c of r) {
|
|
133
|
+
if (!Number.isFinite(c.time) || c.time < 0 || c.time > t) continue;
|
|
134
|
+
const u = Math.max(0, c.value);
|
|
135
|
+
if (u === 0) continue;
|
|
136
|
+
const f = Math.min(e - 1, Math.floor(c.time / t * e));
|
|
137
|
+
i[f] += u, s = !0;
|
|
138
|
+
}
|
|
139
|
+
if (!s) return [];
|
|
140
|
+
const n = [0.06, 0.24, 0.4, 0.24, 0.06], o = i.map(
|
|
141
|
+
(c, u) => n.reduce((f, d, b) => f + d * (i[u + b - 2] ?? 0), 0)
|
|
142
|
+
), a = Math.max(...o);
|
|
143
|
+
if (a <= 0) return [];
|
|
144
|
+
const h = 0.08;
|
|
145
|
+
return o.map((c) => h + (1 - h) * (c / a));
|
|
146
|
+
}
|
|
147
|
+
function it(r, t = 1e3, e = 100) {
|
|
148
|
+
if (r.length === 0) return "";
|
|
149
|
+
const i = t / (r.length - 1 || 1);
|
|
150
|
+
let s = `M 0 ${e}`;
|
|
151
|
+
return r.forEach((n, o) => {
|
|
152
|
+
s += ` L ${(o * i).toFixed(1)} ${(e - n * e).toFixed(1)}`;
|
|
153
|
+
}), s += ` L ${t} ${e} Z`, s;
|
|
154
|
+
}
|
|
155
|
+
async function st(r, t) {
|
|
156
|
+
if (r.src)
|
|
157
|
+
return {
|
|
158
|
+
roll: r.roll,
|
|
159
|
+
mediaUrl: r.src,
|
|
160
|
+
clickThrough: r.clickUrl,
|
|
161
|
+
impressions: [],
|
|
162
|
+
tracking: {}
|
|
163
|
+
};
|
|
164
|
+
if (!r.vastTag) throw new Error('Ad roll has neither "vastTag" nor "src"');
|
|
165
|
+
return j(r, r.vastTag, t, 0, { impressions: [], tracking: {} });
|
|
166
|
+
}
|
|
167
|
+
async function j(r, t, e, i, s) {
|
|
168
|
+
if (i > e.maxWrapperDepth) throw new Error("VAST wrapper depth limit exceeded");
|
|
169
|
+
const n = new AbortController(), o = setTimeout(() => n.abort(), e.requestTimeout);
|
|
170
|
+
let a;
|
|
171
|
+
try {
|
|
172
|
+
const v = await fetch(t, { signal: n.signal, credentials: "omit" });
|
|
173
|
+
if (!v.ok) throw new Error(`VAST request failed (${v.status})`);
|
|
174
|
+
a = await v.text();
|
|
175
|
+
} finally {
|
|
176
|
+
clearTimeout(o);
|
|
177
|
+
}
|
|
178
|
+
const h = new DOMParser().parseFromString(a, "text/xml");
|
|
179
|
+
if (h.querySelector("parsererror")) throw new Error("VAST response is not valid XML");
|
|
180
|
+
const c = h.querySelector("VAST > Ad");
|
|
181
|
+
if (!c) throw new Error("VAST response contains no ads");
|
|
182
|
+
nt(c, s);
|
|
183
|
+
const u = c.querySelector(":scope > Wrapper");
|
|
184
|
+
if (u) {
|
|
185
|
+
const v = k(u.querySelector("VASTAdTagURI"));
|
|
186
|
+
if (!v) throw new Error("VAST wrapper without VASTAdTagURI");
|
|
187
|
+
return j(r, v, e, i + 1, s);
|
|
188
|
+
}
|
|
189
|
+
const f = c.querySelector(":scope > InLine");
|
|
190
|
+
if (!f) throw new Error("VAST ad has neither InLine nor Wrapper");
|
|
191
|
+
const d = f.querySelector("Creatives > Creative > Linear");
|
|
192
|
+
if (!d) throw new Error("VAST ad has no Linear creative");
|
|
193
|
+
const b = rt(d);
|
|
194
|
+
if (!b) throw new Error("VAST ad has no playable MediaFile");
|
|
195
|
+
return {
|
|
196
|
+
roll: r.roll,
|
|
197
|
+
mediaUrl: b.url,
|
|
198
|
+
mediaType: b.type,
|
|
199
|
+
clickThrough: k(d.querySelector("VideoClicks > ClickThrough")) ?? r.clickUrl,
|
|
200
|
+
duration: V(k(d.querySelector(":scope > Duration"))),
|
|
201
|
+
skipOffset: ot(
|
|
202
|
+
d.getAttribute("skipoffset"),
|
|
203
|
+
V(k(d.querySelector(":scope > Duration")))
|
|
204
|
+
),
|
|
205
|
+
impressions: s.impressions,
|
|
206
|
+
tracking: s.tracking,
|
|
207
|
+
adTitle: k(f.querySelector(":scope > AdTitle")) ?? void 0
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
function nt(r, t) {
|
|
211
|
+
var e, i;
|
|
212
|
+
for (const s of r.querySelectorAll("Impression")) {
|
|
213
|
+
const n = k(s);
|
|
214
|
+
n && t.impressions.push(n);
|
|
215
|
+
}
|
|
216
|
+
for (const s of r.querySelectorAll("Linear > TrackingEvents > Tracking")) {
|
|
217
|
+
const n = s.getAttribute("event"), o = k(s);
|
|
218
|
+
!n || !o || ["start", "firstQuartile", "midpoint", "thirdQuartile", "complete", "skip", "pause", "resume"].includes(n) && ((e = t.tracking)[n] ?? (e[n] = [])).push(o);
|
|
219
|
+
}
|
|
220
|
+
for (const s of r.querySelectorAll("Linear > VideoClicks > ClickTracking")) {
|
|
221
|
+
const n = k(s);
|
|
222
|
+
n && ((i = t.tracking).click ?? (i.click = [])).push(n);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
function rt(r) {
|
|
226
|
+
const e = [...r.querySelectorAll("MediaFiles > MediaFile")].map((o) => ({
|
|
227
|
+
url: k(o) ?? "",
|
|
228
|
+
type: o.getAttribute("type") ?? void 0,
|
|
229
|
+
delivery: o.getAttribute("delivery") ?? "",
|
|
230
|
+
bitrate: Number(o.getAttribute("bitrate") ?? 0),
|
|
231
|
+
apiFramework: o.getAttribute("apiFramework") ?? ""
|
|
232
|
+
})).filter((o) => o.url && o.apiFramework.toUpperCase() !== "VPAID").filter(
|
|
233
|
+
(o) => !o.type || /(video\/(mp4|webm|ogg)|application\/(x-mpegurl|vnd\.apple\.mpegurl))/i.test(o.type)
|
|
234
|
+
);
|
|
235
|
+
if (e.length === 0) return null;
|
|
236
|
+
e.sort((o, a) => o.bitrate - a.bitrate);
|
|
237
|
+
const i = e.filter((o) => o.delivery !== "streaming"), s = i.length > 0 ? i : e, n = s[Math.floor(s.length / 2)];
|
|
238
|
+
return { url: n.url, type: n.type };
|
|
239
|
+
}
|
|
240
|
+
function V(r) {
|
|
241
|
+
if (!r) return;
|
|
242
|
+
const t = r.trim().match(/^(?:(\d+):)?(\d{1,2}):(\d{2})(?:\.(\d{1,3}))?$/);
|
|
243
|
+
if (t)
|
|
244
|
+
return (t[1] ? Number(t[1]) * 3600 : 0) + Number(t[2]) * 60 + Number(t[3]) + (t[4] ? Number(t[4].padEnd(3, "0")) / 1e3 : 0);
|
|
245
|
+
}
|
|
246
|
+
function ot(r, t) {
|
|
247
|
+
if (!r) return;
|
|
248
|
+
const e = r.trim().match(/^(\d+(?:\.\d+)?)%$/);
|
|
249
|
+
return e ? t !== void 0 ? Number(e[1]) / 100 * t : void 0 : V(r);
|
|
250
|
+
}
|
|
251
|
+
function C(r) {
|
|
252
|
+
if (r)
|
|
253
|
+
for (const t of r)
|
|
254
|
+
try {
|
|
255
|
+
new Image().src = t;
|
|
256
|
+
} catch {
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
function k(r) {
|
|
260
|
+
return r?.textContent?.trim().replace(/^<!\[CDATA\[|\]\]>$/g, "").trim() || null;
|
|
261
|
+
}
|
|
262
|
+
class lt {
|
|
263
|
+
constructor(t, e) {
|
|
264
|
+
this.host = t, this.playedRolls = /* @__PURE__ */ new Set(), this.sourcesSeen = 0, this.activeBreak = null, this.layer = null, this.adVideo = null, this.destroyed = !1, this.opts = {
|
|
265
|
+
skipDelay: 5,
|
|
266
|
+
maxWrapperDepth: 3,
|
|
267
|
+
requestTimeout: 8e3,
|
|
268
|
+
mediaTimeout: 1e4,
|
|
269
|
+
playOn: "every",
|
|
270
|
+
...e
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Create (once) and "bless" the ad <video> synchronously inside the user
|
|
275
|
+
* gesture that started playback. A bare `load()` during the gesture lets
|
|
276
|
+
* the later `play()` succeed even though VAST fetches happen in between —
|
|
277
|
+
* the same trick every ad SDK uses for iOS/strict autoplay policies.
|
|
278
|
+
*/
|
|
279
|
+
ensureAdVideo() {
|
|
280
|
+
if (!this.adVideo) {
|
|
281
|
+
this.adVideo = l("video", "imp-ad__video", { playsinline: "" });
|
|
282
|
+
try {
|
|
283
|
+
this.adVideo.load();
|
|
284
|
+
} catch {
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return this.adVideo;
|
|
288
|
+
}
|
|
289
|
+
get adPlaying() {
|
|
290
|
+
return this.activeBreak !== null;
|
|
291
|
+
}
|
|
292
|
+
/** True while a pre-roll is still due for the current source. */
|
|
293
|
+
get hasPendingPreRoll() {
|
|
294
|
+
return this.pending("preRoll").length > 0;
|
|
295
|
+
}
|
|
296
|
+
/** Called on every source change. Honors the `playOn` frequency setting. */
|
|
297
|
+
resetForNewSource() {
|
|
298
|
+
if (this.sourcesSeen++, this.opts.playOn === "first" && this.sourcesSeen > 1) {
|
|
299
|
+
for (const t of this.opts.adList) this.playedRolls.add(t);
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
this.playedRolls.clear();
|
|
303
|
+
}
|
|
304
|
+
async playPreRoll() {
|
|
305
|
+
await this.playRolls(this.pending("preRoll"));
|
|
306
|
+
}
|
|
307
|
+
async playPostRoll() {
|
|
308
|
+
await this.playRolls(this.pending("postRoll"));
|
|
309
|
+
}
|
|
310
|
+
/** Called from timeupdate; fires due mid-rolls. */
|
|
311
|
+
checkMidRolls(t) {
|
|
312
|
+
if (this.adPlaying) return;
|
|
313
|
+
const e = this.pending("midRoll").filter((i) => i.timer !== void 0 && t >= i.timer);
|
|
314
|
+
e.length > 0 && this.playRolls(e);
|
|
315
|
+
}
|
|
316
|
+
pending(t) {
|
|
317
|
+
return this.opts.adList.filter((e) => e.roll === t && !this.playedRolls.has(e));
|
|
318
|
+
}
|
|
319
|
+
async playRolls(t) {
|
|
320
|
+
if (t.length === 0 || this.destroyed) return;
|
|
321
|
+
if (this.activeBreak) return this.activeBreak;
|
|
322
|
+
this.ensureAdVideo();
|
|
323
|
+
const e = (async () => {
|
|
324
|
+
const i = this.host.contentVideo, s = !i.paused && !i.ended;
|
|
325
|
+
i.pause();
|
|
326
|
+
for (const n of t) {
|
|
327
|
+
this.playedRolls.add(n);
|
|
328
|
+
try {
|
|
329
|
+
const o = await st(n, this.opts);
|
|
330
|
+
await this.playAd(o);
|
|
331
|
+
} catch (o) {
|
|
332
|
+
const a = o instanceof Error ? o : new Error(String(o));
|
|
333
|
+
this.host.emitter.emit("aderror", { roll: n, error: a });
|
|
334
|
+
}
|
|
335
|
+
if (this.destroyed) return;
|
|
336
|
+
}
|
|
337
|
+
(s || t[0].roll === "preRoll") && i.play().catch(() => {
|
|
338
|
+
});
|
|
339
|
+
})();
|
|
340
|
+
return this.activeBreak = e.finally(() => {
|
|
341
|
+
this.activeBreak = null;
|
|
342
|
+
}), this.activeBreak;
|
|
343
|
+
}
|
|
344
|
+
playAd(t) {
|
|
345
|
+
return new Promise((e) => {
|
|
346
|
+
const { labels: i } = this.host, s = l("div", "imp-ad"), n = this.ensureAdVideo(), o = new AbortController(), a = o.signal;
|
|
347
|
+
n.src = t.mediaUrl, n.muted = this.host.contentVideo.muted, n.volume = this.host.contentVideo.volume;
|
|
348
|
+
const h = l("div", "imp-spinner imp-ad__spinner"), c = l("div", "imp-ad__hud"), u = l("span", "imp-ad__badge");
|
|
349
|
+
u.textContent = t.adTitle ? `${i.adLabel} · ${t.adTitle}` : i.adLabel;
|
|
350
|
+
const f = l("span", "imp-ad__countdown");
|
|
351
|
+
c.append(u, f);
|
|
352
|
+
const d = l("div", "imp-ad__actions");
|
|
353
|
+
let b = null;
|
|
354
|
+
t.clickThrough && (b = l("button", "imp-ad__visit", { type: "button" }), b.textContent = i.visitAdvertiser, d.append(b));
|
|
355
|
+
const v = l("button", "imp-ad__skip", { type: "button" });
|
|
356
|
+
v.hidden = !0, d.append(v), s.append(n, h, c, d), this.host.container.append(s), this.layer = s;
|
|
357
|
+
const H = t.skipOffset ?? this.opts.skipDelay, N = /* @__PURE__ */ new Set(), L = (g) => {
|
|
358
|
+
N.has(g) || (N.add(g), C(t.tracking[g]));
|
|
359
|
+
};
|
|
360
|
+
let P = Date.now(), Z = -1;
|
|
361
|
+
const X = setInterval(() => {
|
|
362
|
+
if (n.paused) {
|
|
363
|
+
P = Date.now();
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
if (n.currentTime !== Z) {
|
|
367
|
+
Z = n.currentTime, P = Date.now();
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
Date.now() - P >= this.opts.mediaTimeout && (this.host.emitter.emit("aderror", {
|
|
371
|
+
roll: { roll: t.roll },
|
|
372
|
+
error: new Error(`Ad media stalled for ${this.opts.mediaTimeout} ms — skipping`)
|
|
373
|
+
}), A());
|
|
374
|
+
}, 500), A = () => {
|
|
375
|
+
clearInterval(X), o.abort(), n.pause(), n.removeAttribute("src"), n.load(), s.remove(), this.layer = null, e();
|
|
376
|
+
}, O = (g) => {
|
|
377
|
+
g ? (L("skip"), this.host.emitter.emit("adskip", { ad: t })) : L("complete"), this.host.emitter.emit("adend", { ad: t }), A();
|
|
378
|
+
};
|
|
379
|
+
n.addEventListener("playing", () => {
|
|
380
|
+
C(t.impressions), L("start"), this.host.emitter.emit("adstart", { ad: t });
|
|
381
|
+
}, { once: !0, signal: a }), n.addEventListener("playing", () => {
|
|
382
|
+
h.hidden = !0;
|
|
383
|
+
}, { signal: a }), n.addEventListener("waiting", () => {
|
|
384
|
+
h.hidden = !1;
|
|
385
|
+
}, { signal: a }), n.addEventListener("timeupdate", () => {
|
|
386
|
+
const g = n.currentTime, x = n.duration;
|
|
387
|
+
if (Number.isFinite(x) && x > 0 && (f.textContent = w(Math.max(0, x - g)), g / x >= 0.25 && L("firstQuartile"), g / x >= 0.5 && L("midpoint"), g / x >= 0.75 && L("thirdQuartile")), H >= 0) {
|
|
388
|
+
const z = Math.ceil(H - g);
|
|
389
|
+
z > 0 ? (v.hidden = !1, v.disabled = !0, v.textContent = `${i.skipAdIn} ${z}`) : (v.hidden = !1, v.disabled = !1, v.textContent = i.skipAd);
|
|
390
|
+
}
|
|
391
|
+
}, { signal: a }), n.addEventListener("ended", () => O(!1), { signal: a }), n.addEventListener("error", () => {
|
|
392
|
+
this.host.emitter.emit("aderror", {
|
|
393
|
+
roll: { roll: t.roll },
|
|
394
|
+
error: new Error("Ad media failed to play")
|
|
395
|
+
}), A();
|
|
396
|
+
}, { signal: a }), v.addEventListener("click", () => O(!0));
|
|
397
|
+
const D = () => {
|
|
398
|
+
t.clickThrough && (L("click"), this.host.emitter.emit("adclick", { ad: t }), window.open(t.clickThrough, "_blank", "noopener"), n.pause());
|
|
399
|
+
};
|
|
400
|
+
n.addEventListener("click", D, { signal: a }), b?.addEventListener("click", D);
|
|
401
|
+
let W = !1, B = !1;
|
|
402
|
+
n.addEventListener("playing", () => {
|
|
403
|
+
W = !0;
|
|
404
|
+
}, { once: !0, signal: a }), n.addEventListener("pause", () => {
|
|
405
|
+
n.ended || !s.isConnected || (s.classList.add("imp-ad--paused"), W && !B && (B = !0, C(t.tracking.pause), this.host.emitter.emit("adpause", { ad: t })));
|
|
406
|
+
}, { signal: a }), n.addEventListener("play", () => {
|
|
407
|
+
s.classList.remove("imp-ad--paused"), B && (B = !1, C(t.tracking.resume), this.host.emitter.emit("adresume", { ad: t }));
|
|
408
|
+
}, { signal: a }), s.addEventListener("click", (g) => {
|
|
409
|
+
(g.target === s || s.classList.contains("imp-ad--paused")) && n.paused && n.play().catch(() => {
|
|
410
|
+
});
|
|
411
|
+
}), n.play().catch(() => {
|
|
412
|
+
s.classList.add("imp-ad--paused");
|
|
413
|
+
});
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
/** Skip-button icon access for potential theming. */
|
|
417
|
+
get closeIcon() {
|
|
418
|
+
return this.host.closeIcon;
|
|
419
|
+
}
|
|
420
|
+
destroy() {
|
|
421
|
+
this.destroyed = !0, this.adVideo?.pause(), this.layer?.remove(), this.layer = null, this.adVideo = null;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
function at(r, t) {
|
|
425
|
+
const e = [...r].sort((s, n) => s.start - n.start), i = [];
|
|
426
|
+
for (let s = 0; s < e.length; s++) {
|
|
427
|
+
const n = Math.max(0, e[s].start);
|
|
428
|
+
if (Number.isFinite(t) && n >= t) continue;
|
|
429
|
+
let o = e[s].end ?? e[s + 1]?.start ?? t;
|
|
430
|
+
Number.isFinite(t) && (o = Math.min(o, t)), !(o <= n) && i.push({ start: n, end: o, title: e[s].title });
|
|
431
|
+
}
|
|
432
|
+
return i;
|
|
433
|
+
}
|
|
434
|
+
async function ht(r) {
|
|
435
|
+
const t = await fetch(r);
|
|
436
|
+
if (!t.ok) throw new Error(`Failed to load chapters VTT (${t.status})`);
|
|
437
|
+
const e = await t.text();
|
|
438
|
+
return U(e).map((i) => ({ start: i.start, end: i.end, title: i.text }));
|
|
439
|
+
}
|
|
440
|
+
function K(r, t) {
|
|
441
|
+
for (const e of r)
|
|
442
|
+
if (t >= e.start && t < e.end) return e;
|
|
443
|
+
return null;
|
|
444
|
+
}
|
|
445
|
+
function ct(r, t) {
|
|
446
|
+
return t ? /application\/(x-mpegurl|vnd\.apple\.mpegurl)/i.test(t) : /\.m3u8(\?|#|$)/i.test(r);
|
|
447
|
+
}
|
|
448
|
+
let S;
|
|
449
|
+
async function dt() {
|
|
450
|
+
if (S !== void 0) return S;
|
|
451
|
+
const r = globalThis.Hls;
|
|
452
|
+
if (r)
|
|
453
|
+
return S = r, S;
|
|
454
|
+
try {
|
|
455
|
+
S = (await import("hls.js")).default;
|
|
456
|
+
} catch {
|
|
457
|
+
S = null;
|
|
458
|
+
}
|
|
459
|
+
return S;
|
|
460
|
+
}
|
|
461
|
+
async function ut(r, t, e, i) {
|
|
462
|
+
if (ct(t, e)) {
|
|
463
|
+
const s = r.canPlayType("application/vnd.apple.mpegurl"), n = s ? null : await dt();
|
|
464
|
+
return n && n.isSupported() ? pt(n, r, t, i) : s ? (r.src = t, $(r)) : (i.onError("HLS is not supported in this browser and hls.js could not be loaded."), $(r));
|
|
465
|
+
}
|
|
466
|
+
return r.src = t, $(r);
|
|
467
|
+
}
|
|
468
|
+
function $(r) {
|
|
469
|
+
return {
|
|
470
|
+
kind: "native",
|
|
471
|
+
levels: [],
|
|
472
|
+
selected: -1,
|
|
473
|
+
setLevel() {
|
|
474
|
+
},
|
|
475
|
+
destroy() {
|
|
476
|
+
r.removeAttribute("src"), r.load();
|
|
477
|
+
}
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
function pt(r, t, e, i) {
|
|
481
|
+
const s = new r({ enableWorker: !0 }), n = {
|
|
482
|
+
kind: "hls",
|
|
483
|
+
levels: [],
|
|
484
|
+
selected: -1,
|
|
485
|
+
setLevel(h) {
|
|
486
|
+
n.selected = h, s.currentLevel = h;
|
|
487
|
+
},
|
|
488
|
+
destroy() {
|
|
489
|
+
s.destroy(), t.removeAttribute("src"), t.load();
|
|
490
|
+
}
|
|
491
|
+
};
|
|
492
|
+
s.on(r.Events.MANIFEST_PARSED, () => {
|
|
493
|
+
n.levels = s.levels.map((h, c) => ({
|
|
494
|
+
index: c,
|
|
495
|
+
label: h.height ? `${h.height}p` : `${Math.round(h.bitrate / 1e3)} kbps`
|
|
496
|
+
})).reverse(), i.onLevels(n.levels);
|
|
497
|
+
}), s.on(r.Events.LEVEL_SWITCHED, (h, c) => {
|
|
498
|
+
const u = s.levels[c.level];
|
|
499
|
+
u && i.onLevelSwitch(u.height ? `${u.height}p` : `${Math.round(u.bitrate / 1e3)} kbps`);
|
|
500
|
+
});
|
|
501
|
+
let o = 0, a = !1;
|
|
502
|
+
return s.on(r.Events.ERROR, (h, c) => {
|
|
503
|
+
if (c.fatal)
|
|
504
|
+
switch (c.type) {
|
|
505
|
+
case r.ErrorTypes.NETWORK_ERROR:
|
|
506
|
+
s.startLoad();
|
|
507
|
+
break;
|
|
508
|
+
case r.ErrorTypes.MEDIA_ERROR:
|
|
509
|
+
o < 3 ? (o++, s.recoverMediaError()) : (i.onError(`HLS media error: ${c.details}`, c), s.destroy());
|
|
510
|
+
break;
|
|
511
|
+
default:
|
|
512
|
+
if (a)
|
|
513
|
+
i.onError(`HLS fatal error: ${c.details}`, c), s.destroy();
|
|
514
|
+
else {
|
|
515
|
+
a = !0;
|
|
516
|
+
try {
|
|
517
|
+
s.loadSource(e), s.startLoad();
|
|
518
|
+
} catch {
|
|
519
|
+
i.onError(`HLS fatal error: ${c.details}`, c), s.destroy();
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}), s.loadSource(e), s.attachMedia(t), n;
|
|
524
|
+
}
|
|
525
|
+
class q {
|
|
526
|
+
constructor(t) {
|
|
527
|
+
this.cues = t;
|
|
528
|
+
}
|
|
529
|
+
static async load(t) {
|
|
530
|
+
const e = await fetch(t);
|
|
531
|
+
if (!e.ok) throw new Error(`Failed to load thumbnails VTT (${e.status})`);
|
|
532
|
+
const i = await e.text(), s = [];
|
|
533
|
+
for (const n of U(i)) {
|
|
534
|
+
const [o, a] = n.text.trim().split("#");
|
|
535
|
+
if (!o) continue;
|
|
536
|
+
const h = {
|
|
537
|
+
start: n.start,
|
|
538
|
+
end: n.end,
|
|
539
|
+
src: G(o, t)
|
|
540
|
+
}, c = a?.match(/xywh=(\d+),(\d+),(\d+),(\d+)/);
|
|
541
|
+
c && (h.xywh = { x: Number(c[1]), y: Number(c[2]), w: Number(c[3]), h: Number(c[4]) }), s.push(h);
|
|
542
|
+
}
|
|
543
|
+
return s.sort((n, o) => n.start - o.start), new q(s);
|
|
544
|
+
}
|
|
545
|
+
cueAt(t) {
|
|
546
|
+
let e = 0, i = this.cues.length - 1;
|
|
547
|
+
for (; e <= i; ) {
|
|
548
|
+
const s = e + i >> 1, n = this.cues[s];
|
|
549
|
+
if (t < n.start) i = s - 1;
|
|
550
|
+
else if (t >= n.end) e = s + 1;
|
|
551
|
+
else return n;
|
|
552
|
+
}
|
|
553
|
+
return null;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
class M {
|
|
557
|
+
constructor(t) {
|
|
558
|
+
this.anchor = t, this.outsideListener = null, this.root = l("div", "imp-menu"), this.root.hidden = !0, this.backdrop = l("div", "imp-menu__backdrop"), this.backdrop.hidden = !0, this.backdrop.addEventListener("pointerdown", (e) => {
|
|
559
|
+
e.preventDefault(), this.close();
|
|
560
|
+
}), t.append(this.backdrop);
|
|
561
|
+
}
|
|
562
|
+
get open() {
|
|
563
|
+
return !this.root.hidden;
|
|
564
|
+
}
|
|
565
|
+
toggle(t) {
|
|
566
|
+
this.open ? this.close() : this.show(t);
|
|
567
|
+
}
|
|
568
|
+
show(t) {
|
|
569
|
+
this.root.textContent = "";
|
|
570
|
+
for (const e of t) {
|
|
571
|
+
if (e.items.length === 0) continue;
|
|
572
|
+
const i = l("div", "imp-menu__section");
|
|
573
|
+
if (e.title) {
|
|
574
|
+
const s = l("div", "imp-menu__title");
|
|
575
|
+
s.textContent = e.title, i.append(s);
|
|
576
|
+
}
|
|
577
|
+
for (const s of e.items) {
|
|
578
|
+
const n = l("button", "imp-menu__item", { type: "button", role: "menuitemradio" });
|
|
579
|
+
if (n.setAttribute("aria-checked", String(s.active)), s.active && n.classList.add("imp-menu__item--active"), s.icon) {
|
|
580
|
+
const a = l("span", "imp-menu__icon");
|
|
581
|
+
s.icon.trimStart().startsWith("<svg") ? a.innerHTML = s.icon : a.append(l("img", "", { src: s.icon, alt: "" })), n.append(a);
|
|
582
|
+
}
|
|
583
|
+
const o = l("span", "imp-menu__label");
|
|
584
|
+
o.textContent = s.label, n.append(o), n.addEventListener("click", () => {
|
|
585
|
+
e.onSelect(s.value), this.close();
|
|
586
|
+
}), i.append(n);
|
|
587
|
+
}
|
|
588
|
+
this.root.append(i);
|
|
589
|
+
}
|
|
590
|
+
this.root.hidden = !1, this.backdrop.hidden = !1, this.outsideListener = (e) => {
|
|
591
|
+
const i = e.target;
|
|
592
|
+
!this.root.contains(i) && !this.anchor.contains(i) && this.close();
|
|
593
|
+
}, setTimeout(() => {
|
|
594
|
+
this.outsideListener && document.addEventListener("pointerdown", this.outsideListener);
|
|
595
|
+
}, 0);
|
|
596
|
+
}
|
|
597
|
+
close() {
|
|
598
|
+
this.root.hidden = !0, this.backdrop.hidden = !0, this.outsideListener && (document.removeEventListener("pointerdown", this.outsideListener), this.outsideListener = null);
|
|
599
|
+
}
|
|
600
|
+
destroy() {
|
|
601
|
+
this.close(), this.root.remove();
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
class mt {
|
|
605
|
+
constructor(t) {
|
|
606
|
+
this.cb = t, this.segments = [], this.duration = 0, this.chapters = [], this.thumbnails = null, this.scrubbing = !1, this.root = l("div", "imp-progress", { role: "slider", "aria-label": "Seek", tabindex: "-1" }), this.buffered = l("div", "imp-progress__buffered"), this.track = l("div", "imp-progress__track"), this.handle = l("div", "imp-progress__handle"), this.tooltipThumb = l("div", "imp-progress__thumb"), this.tooltipChapter = l("div", "imp-progress__tooltip-chapter"), this.tooltipTime = l("div", "imp-progress__tooltip-time"), this.tooltip = l("div", "imp-progress__tooltip"), this.tooltip.append(this.tooltipThumb, this.tooltipChapter, this.tooltipTime), this.heatmap = l("div", "imp-progress__heatmap"), this.heatmap.hidden = !0, this.root.append(this.heatmap, this.buffered, this.track, this.handle, this.tooltip), this.setChapters([]), this.bindPointer();
|
|
607
|
+
}
|
|
608
|
+
/** Render the popularity curve (empty array hides it). */
|
|
609
|
+
setHeatmap(t) {
|
|
610
|
+
if (t.length === 0) {
|
|
611
|
+
this.heatmap.hidden = !0, this.heatmap.textContent = "";
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
this.heatmap.innerHTML = `<svg viewBox="0 0 1000 100" preserveAspectRatio="none" aria-hidden="true"><path d="${it(t)}"/></svg>`, this.heatmap.hidden = !1;
|
|
615
|
+
}
|
|
616
|
+
setDuration(t) {
|
|
617
|
+
this.duration = Number.isFinite(t) ? t : 0, this.root.classList.toggle("imp-progress--live", !Number.isFinite(t)), this.chapters.length === 0 && this.setChapters([]);
|
|
618
|
+
}
|
|
619
|
+
setChapters(t) {
|
|
620
|
+
this.chapters = t, this.track.textContent = "", this.segments = [];
|
|
621
|
+
const e = t.length > 0 ? t : [{ start: 0, end: this.duration || 1, title: "" }], i = [];
|
|
622
|
+
let s = 0;
|
|
623
|
+
for (const n of e)
|
|
624
|
+
n.start > s && i.push({ start: s, end: n.start, title: "" }), i.push(n), s = n.end;
|
|
625
|
+
this.duration > 0 && s < this.duration && i.push({ start: s, end: this.duration, title: "" });
|
|
626
|
+
for (const n of i) {
|
|
627
|
+
const o = l("div", "imp-progress__segment");
|
|
628
|
+
o.style.flexGrow = String(Math.max(n.end - n.start, 0.01));
|
|
629
|
+
const a = l("div", "imp-progress__fill");
|
|
630
|
+
o.append(a), this.track.append(o), this.segments.push({ chapter: n, root: o, fill: a });
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
setThumbnails(t) {
|
|
634
|
+
this.thumbnails = t;
|
|
635
|
+
}
|
|
636
|
+
update(t, e, i) {
|
|
637
|
+
if (e !== this.duration && Number.isFinite(e) && this.setDuration(e), this.scrubbing) return;
|
|
638
|
+
this.render(t);
|
|
639
|
+
const s = this.duration > 0 ? y(i / this.duration, 0, 1) : 0;
|
|
640
|
+
this.buffered.style.width = `${s * 100}%`, this.root.setAttribute("aria-valuemin", "0"), this.root.setAttribute("aria-valuemax", String(Math.round(this.duration))), this.root.setAttribute("aria-valuenow", String(Math.round(t))), this.root.setAttribute("aria-valuetext", `${w(t)} / ${w(this.duration)}`);
|
|
641
|
+
}
|
|
642
|
+
render(t) {
|
|
643
|
+
for (const { chapter: i, fill: s } of this.segments) {
|
|
644
|
+
const n = i.end - i.start, o = n > 0 ? y((t - i.start) / n, 0, 1) : 0;
|
|
645
|
+
s.style.transform = `scaleX(${o})`;
|
|
646
|
+
}
|
|
647
|
+
const e = this.duration > 0 ? y(t / this.duration, 0, 1) : 0;
|
|
648
|
+
this.handle.style.left = `${e * 100}%`;
|
|
649
|
+
}
|
|
650
|
+
timeFromEvent(t) {
|
|
651
|
+
const e = this.root.getBoundingClientRect();
|
|
652
|
+
return (e.width > 0 ? y((t.clientX - e.left) / e.width, 0, 1) : 0) * this.duration;
|
|
653
|
+
}
|
|
654
|
+
bindPointer() {
|
|
655
|
+
this.root.addEventListener("pointerdown", (t) => {
|
|
656
|
+
this.duration <= 0 || (t.preventDefault(), this.scrubbing = !0, this.root.classList.add("imp-progress--scrubbing"), this.root.setPointerCapture(t.pointerId), this.cb.onScrubStart(), this.render(this.timeFromEvent(t)), this.showTooltip(t));
|
|
657
|
+
}), this.root.addEventListener("pointermove", (t) => {
|
|
658
|
+
this.duration <= 0 || (this.showTooltip(t), this.scrubbing && this.render(this.timeFromEvent(t)));
|
|
659
|
+
}), this.root.addEventListener("pointerup", (t) => {
|
|
660
|
+
this.scrubbing && (this.scrubbing = !1, this.root.classList.remove("imp-progress--scrubbing"), this.cb.onSeek(this.timeFromEvent(t)), this.cb.onScrubEnd());
|
|
661
|
+
}), this.root.addEventListener("pointercancel", () => {
|
|
662
|
+
this.scrubbing = !1, this.root.classList.remove("imp-progress--scrubbing"), this.cb.onScrubEnd();
|
|
663
|
+
}), this.root.addEventListener("pointerleave", () => this.hideTooltip());
|
|
664
|
+
}
|
|
665
|
+
showTooltip(t) {
|
|
666
|
+
const e = this.timeFromEvent(t);
|
|
667
|
+
this.tooltipTime.textContent = w(e);
|
|
668
|
+
const i = K(this.chapters, e);
|
|
669
|
+
this.tooltipChapter.textContent = i?.title ?? "", this.tooltipChapter.hidden = !i?.title;
|
|
670
|
+
const s = this.thumbnails?.cueAt(e) ?? null;
|
|
671
|
+
s ? (this.tooltipThumb.hidden = !1, this.tooltipThumb.style.backgroundImage = `url("${s.src}")`, s.xywh ? (this.tooltipThumb.style.width = `${s.xywh.w}px`, this.tooltipThumb.style.height = `${s.xywh.h}px`, this.tooltipThumb.style.backgroundPosition = `-${s.xywh.x}px -${s.xywh.y}px`, this.tooltipThumb.style.backgroundSize = "auto") : (this.tooltipThumb.style.width = "160px", this.tooltipThumb.style.height = "90px", this.tooltipThumb.style.backgroundPosition = "center", this.tooltipThumb.style.backgroundSize = "cover")) : this.tooltipThumb.hidden = !0, this.tooltip.classList.add("imp-progress__tooltip--visible");
|
|
672
|
+
const n = this.root.getBoundingClientRect(), o = y(t.clientX - n.left, 0, n.width), a = this.tooltip.offsetWidth / 2;
|
|
673
|
+
this.tooltip.style.left = `${y(o, a, Math.max(a, n.width - a))}px`;
|
|
674
|
+
}
|
|
675
|
+
hideTooltip() {
|
|
676
|
+
this.tooltip.classList.remove("imp-progress__tooltip--visible");
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
const _ = class _ {
|
|
680
|
+
constructor(t) {
|
|
681
|
+
this.player = t, this.subtitlesBtn = null, this.settingsBtn = null, this.settingsMenu = null, this.subtitlesMenu = null, this.moreMenu = null, this.moreWrap = null, this.qualityBtn = null, this.qualityMenu = null, this.scenesBtn = null, this.likeBtn = null, this.dislikeBtn = null, this.likeCountEl = null, this.dislikeCountEl = null, this.prevBtn = null, this.nextBtn = null, this.seekBackBtn = null, this.seekFwdBtn = null, this.centerPlayBtn = null, this.centerPrevBtn = null, this.centerNextBtn = null, this.collapsibles = [], this.overflowed = /* @__PURE__ */ new Set(), this.resizeObserver = null, this.reflowScheduled = !1, this.actionItems = [], this.wasPlayingBeforeScrub = !1, this.disposers = [], this.onWindowResize = null;
|
|
682
|
+
const { labels: e, icons: i } = t, s = t.controlsOptions;
|
|
683
|
+
this.root = l("div", "imp-controls"), this.progress = new mt({
|
|
684
|
+
onSeek: (d) => t.seek(d),
|
|
685
|
+
onScrubStart: () => {
|
|
686
|
+
this.wasPlayingBeforeScrub = !t.paused, t.pause();
|
|
687
|
+
},
|
|
688
|
+
onScrubEnd: () => {
|
|
689
|
+
this.wasPlayingBeforeScrub && t.play();
|
|
690
|
+
}
|
|
691
|
+
}), s.progress && this.root.append(this.progress.root);
|
|
692
|
+
const n = l("div", "imp-controls__row");
|
|
693
|
+
this.row = n, this.root.append(n);
|
|
694
|
+
const o = l("div", "imp-controls__group"), a = l("div", "imp-controls__group");
|
|
695
|
+
this.rightGroup = a;
|
|
696
|
+
const h = l("div", "imp-controls__spacer");
|
|
697
|
+
this.chapterLabel = l("div", "imp-controls__chapter"), n.append(o, h, a), h.append(this.chapterLabel);
|
|
698
|
+
const c = typeof s.seekButtons == "object" ? s.seekButtons : {}, u = c.back ?? t.seekStep, f = c.forward ?? t.seekStep;
|
|
699
|
+
if (s.playlist && t.hasPlaylist && (this.prevBtn = m("imp-btn--prev", e.previous, i.previous), this.prevBtn.addEventListener("click", () => t.previous()), o.append(this.prevBtn)), s.seekButtons && (this.seekBackBtn = m("imp-btn--seek-back", `${e.seekBack} ${u}s`, i.seekBack), this.seekBackBtn.addEventListener("click", () => t.skip(-u)), o.append(this.seekBackBtn)), s.play && (this.playBtn = m("imp-btn--play", e.play, i.play), this.playBtn.addEventListener("click", () => t.togglePlay()), o.append(this.playBtn)), s.seekButtons && (this.seekFwdBtn = m("imp-btn--seek-forward", `${e.seekForward} ${f}s`, i.seekForward), this.seekFwdBtn.addEventListener("click", () => t.skip(f)), o.append(this.seekFwdBtn)), s.playlist && t.hasPlaylist && (this.nextBtn = m("imp-btn--next", e.next, i.next), this.nextBtn.addEventListener("click", () => t.next()), o.append(this.nextBtn)), s.volume) {
|
|
700
|
+
const d = l("div", "imp-volume");
|
|
701
|
+
this.muteBtn = m("imp-btn--mute", e.mute, i.volumeHigh), this.muteBtn.addEventListener("click", () => t.toggleMute()), this.volumeSlider = l("input", "imp-volume__slider", {
|
|
702
|
+
type: "range",
|
|
703
|
+
min: "0",
|
|
704
|
+
max: "1",
|
|
705
|
+
step: "0.05",
|
|
706
|
+
"aria-label": "Volume"
|
|
707
|
+
}), this.volumeSlider.addEventListener("input", () => {
|
|
708
|
+
t.setVolume(Number(this.volumeSlider.value));
|
|
709
|
+
}), d.append(this.muteBtn, this.volumeSlider), o.append(d);
|
|
710
|
+
}
|
|
711
|
+
if (s.time && (this.timeLabel = l("div", "imp-controls__time"), this.liveBadge = l("span", "imp-controls__live"), this.liveBadge.textContent = e.live, this.liveBadge.hidden = !0, o.append(this.timeLabel, this.liveBadge)), this.buildLikeDislike(a), s.subtitles) {
|
|
712
|
+
this.subtitlesBtn = m("imp-btn--subtitles", e.subtitles, i.subtitles), this.subtitlesMenu = new M(this.subtitlesBtn);
|
|
713
|
+
const d = l("div", "imp-controls__menu-anchor");
|
|
714
|
+
d.append(this.subtitlesBtn, this.subtitlesMenu.root), this.subtitlesBtn.addEventListener("click", () => this.toggleSubtitlesMenu()), a.append(d), this.registerCollapsible({
|
|
715
|
+
key: "subtitles",
|
|
716
|
+
el: d,
|
|
717
|
+
priority: 30,
|
|
718
|
+
available: () => t.subtitleTracks.length > 0,
|
|
719
|
+
section: () => this.subtitlesSection()
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
if (s.settings) {
|
|
723
|
+
this.settingsBtn = m("imp-btn--settings", e.settings, i.speed), this.settingsMenu = new M(this.settingsBtn);
|
|
724
|
+
const d = l("div", "imp-controls__menu-anchor");
|
|
725
|
+
d.append(this.settingsBtn, this.settingsMenu.root), this.settingsBtn.addEventListener("click", () => this.toggleSettingsMenu()), a.append(d), this.registerCollapsible({
|
|
726
|
+
key: "settings",
|
|
727
|
+
el: d,
|
|
728
|
+
priority: 40,
|
|
729
|
+
available: () => !0,
|
|
730
|
+
section: () => this.speedSection()
|
|
731
|
+
});
|
|
732
|
+
}
|
|
733
|
+
if (s.quality) {
|
|
734
|
+
this.qualityBtn = m("imp-btn--quality", e.quality, i.settings), this.qualityMenu = new M(this.qualityBtn);
|
|
735
|
+
const d = l("div", "imp-controls__menu-anchor");
|
|
736
|
+
d.append(this.qualityBtn, this.qualityMenu.root), this.qualityBtn.addEventListener("click", () => this.toggleQualityMenu()), a.append(d), this.registerCollapsible({
|
|
737
|
+
key: "quality",
|
|
738
|
+
el: d,
|
|
739
|
+
priority: 10,
|
|
740
|
+
available: () => t.qualityLevels.length > 0,
|
|
741
|
+
section: () => this.qualitySection()
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
if (s.scenes && (this.scenesBtn = m("imp-btn--scenes", e.scenes, i.scenes), this.scenesBtn.addEventListener("click", () => t.toggleScenesPanel()), a.append(this.scenesBtn), this.registerCollapsible({
|
|
745
|
+
key: "scenes",
|
|
746
|
+
el: this.scenesBtn,
|
|
747
|
+
priority: 25,
|
|
748
|
+
available: () => t.chapterList.length > 0,
|
|
749
|
+
section: () => this.simpleSection("scenes", e.scenes, i.scenes, () => t.toggleScenesPanel())
|
|
750
|
+
})), s.playlist && t.hasPlaylist) {
|
|
751
|
+
const d = m("imp-btn--list", e.playlist, i.list);
|
|
752
|
+
d.addEventListener("click", () => t.togglePlaylistPanel()), a.append(d), this.registerCollapsible({
|
|
753
|
+
key: "list",
|
|
754
|
+
el: d,
|
|
755
|
+
priority: 50,
|
|
756
|
+
available: () => !0,
|
|
757
|
+
section: () => this.simpleSection("list", e.playlist, i.list, () => t.togglePlaylistPanel())
|
|
758
|
+
});
|
|
759
|
+
}
|
|
760
|
+
if (s.pip && "requestPictureInPicture" in HTMLVideoElement.prototype) {
|
|
761
|
+
const d = m("imp-btn--pip", e.pip, i.pip);
|
|
762
|
+
d.addEventListener("click", () => void t.togglePip()), a.append(d), this.registerCollapsible({
|
|
763
|
+
key: "pip",
|
|
764
|
+
el: d,
|
|
765
|
+
priority: 20,
|
|
766
|
+
available: () => !0,
|
|
767
|
+
section: () => this.simpleSection("pip", e.pip, i.pip, () => void t.togglePip())
|
|
768
|
+
});
|
|
769
|
+
}
|
|
770
|
+
if (s.fullscreen && (this.fullscreenBtn = m("imp-btn--fullscreen", e.fullscreen, i.fullscreen), this.fullscreenBtn.addEventListener("click", () => void t.toggleFullscreen()), a.append(this.fullscreenBtn)), this.prevBtn && this.registerCollapsible({
|
|
771
|
+
key: "prev",
|
|
772
|
+
el: this.prevBtn,
|
|
773
|
+
priority: 70,
|
|
774
|
+
available: () => !0,
|
|
775
|
+
section: () => t.hasPrevious ? this.simpleSection("prev", e.previous, i.previous, () => t.previous()) : null
|
|
776
|
+
}), this.nextBtn && this.registerCollapsible({
|
|
777
|
+
key: "next",
|
|
778
|
+
el: this.nextBtn,
|
|
779
|
+
priority: 72,
|
|
780
|
+
available: () => !0,
|
|
781
|
+
section: () => t.hasNext ? this.simpleSection("next", e.next, i.next, () => t.next()) : null
|
|
782
|
+
}), this.seekBackBtn) {
|
|
783
|
+
const d = this.seekBackBtn;
|
|
784
|
+
this.registerCollapsible({
|
|
785
|
+
key: "seekBack",
|
|
786
|
+
el: d,
|
|
787
|
+
priority: 80,
|
|
788
|
+
available: () => Number.isFinite(t.duration) && t.duration > 0,
|
|
789
|
+
section: () => this.simpleSection("seekBack", `${e.seekBack} ${u}s`, i.seekBack, () => t.skip(-u))
|
|
790
|
+
});
|
|
791
|
+
}
|
|
792
|
+
if (this.seekFwdBtn) {
|
|
793
|
+
const d = this.seekFwdBtn;
|
|
794
|
+
this.registerCollapsible({
|
|
795
|
+
key: "seekFwd",
|
|
796
|
+
el: d,
|
|
797
|
+
priority: 82,
|
|
798
|
+
available: () => Number.isFinite(t.duration) && t.duration > 0,
|
|
799
|
+
section: () => this.simpleSection("seekFwd", `${e.seekForward} ${f}s`, i.seekForward, () => t.skip(f))
|
|
800
|
+
});
|
|
801
|
+
}
|
|
802
|
+
if (this.center = l("div", "imp-center-controls"), s.playlist && t.hasPlaylist && (this.centerPrevBtn = this.makeCenterButton("prev", e.previous, i.previous), this.centerPrevBtn.addEventListener("click", () => t.previous()), this.center.append(this.centerPrevBtn)), s.seekButtons) {
|
|
803
|
+
const d = this.makeCenterButton("seek-back", `${e.seekBack} ${u}s`, i.seekBack);
|
|
804
|
+
d.addEventListener("click", () => t.skip(-u)), this.center.append(d);
|
|
805
|
+
}
|
|
806
|
+
if (s.play && (this.centerPlayBtn = this.makeCenterButton("play", e.play, i.play), this.centerPlayBtn.addEventListener("click", () => t.togglePlay()), this.center.append(this.centerPlayBtn)), s.seekButtons) {
|
|
807
|
+
const d = this.makeCenterButton("seek-forward", `${e.seekForward} ${f}s`, i.seekForward);
|
|
808
|
+
d.addEventListener("click", () => t.skip(f)), this.center.append(d);
|
|
809
|
+
}
|
|
810
|
+
s.playlist && t.hasPlaylist && (this.centerNextBtn = this.makeCenterButton("next", e.next, i.next), this.centerNextBtn.addEventListener("click", () => t.next()), this.center.append(this.centerNextBtn)), this.center.style.display = "none", this.buildMoreDropdown(a), this.bind(), this.syncVolume(), this.syncPlayState(), this.setLikeState(t.actionsOptions.likeState ?? null), typeof ResizeObserver < "u" && (this.resizeObserver = new ResizeObserver(() => this.scheduleReflow()), this.resizeObserver.observe(t.container)), this.onWindowResize = () => this.scheduleReflow(), window.addEventListener("resize", this.onWindowResize);
|
|
811
|
+
}
|
|
812
|
+
registerCollapsible(t) {
|
|
813
|
+
this.collapsibles.push(t);
|
|
814
|
+
}
|
|
815
|
+
/** Center-cluster button — deliberately NOT `.imp-btn` (own sizing/look). */
|
|
816
|
+
makeCenterButton(t, e, i) {
|
|
817
|
+
const s = l("button", `imp-center-btn imp-center-btn--${t}`, { type: "button", "aria-label": e, title: e });
|
|
818
|
+
return s.innerHTML = i, s;
|
|
819
|
+
}
|
|
820
|
+
/** like/dislike — visible buttons (collapsible), highlightable via `setLikeState`. */
|
|
821
|
+
buildLikeDislike(t) {
|
|
822
|
+
const e = this.player, i = e.actionsOptions, { labels: s, icons: n } = e;
|
|
823
|
+
if (i.like) {
|
|
824
|
+
this.likeBtn = m("imp-btn--like", s.like, n.like), this.likeBtn.addEventListener("click", () => e.emit("action", { id: "like" })), this.likeCountEl = this.attachCountTooltip(this.likeBtn, i.likeCount);
|
|
825
|
+
const o = this.wrapWithTooltip(this.likeBtn, this.likeCountEl);
|
|
826
|
+
t.append(o), this.registerCollapsible({
|
|
827
|
+
key: "like",
|
|
828
|
+
el: o,
|
|
829
|
+
priority: 62,
|
|
830
|
+
available: () => !0,
|
|
831
|
+
section: () => this.simpleSection("like", s.like, n.like, () => e.emit("action", { id: "like" }))
|
|
832
|
+
});
|
|
833
|
+
}
|
|
834
|
+
if (i.dislike) {
|
|
835
|
+
this.dislikeBtn = m("imp-btn--dislike", s.dislike, n.dislike), this.dislikeBtn.addEventListener("click", () => e.emit("action", { id: "dislike" })), this.dislikeCountEl = this.attachCountTooltip(this.dislikeBtn, i.dislikeCount);
|
|
836
|
+
const o = this.wrapWithTooltip(this.dislikeBtn, this.dislikeCountEl);
|
|
837
|
+
t.append(o), this.registerCollapsible({
|
|
838
|
+
key: "dislike",
|
|
839
|
+
el: o,
|
|
840
|
+
priority: 60,
|
|
841
|
+
available: () => !0,
|
|
842
|
+
section: () => this.simpleSection("dislike", s.dislike, n.dislike, () => e.emit("action", { id: "dislike" }))
|
|
843
|
+
});
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
attachCountTooltip(t, e) {
|
|
847
|
+
const i = l("span", "imp-count-tooltip");
|
|
848
|
+
return this.setCountText(i, e), i;
|
|
849
|
+
}
|
|
850
|
+
wrapWithTooltip(t, e) {
|
|
851
|
+
const i = l("div", "imp-action");
|
|
852
|
+
return i.append(t, e), i;
|
|
853
|
+
}
|
|
854
|
+
setCountText(t, e) {
|
|
855
|
+
const i = e == null || e === "" ? "" : String(e);
|
|
856
|
+
t.textContent = i, t.hidden = i === "";
|
|
857
|
+
}
|
|
858
|
+
setLikeCounts(t, e) {
|
|
859
|
+
this.likeCountEl && t !== void 0 && this.setCountText(this.likeCountEl, t), this.dislikeCountEl && e !== void 0 && this.setCountText(this.dislikeCountEl, e);
|
|
860
|
+
}
|
|
861
|
+
setLikeState(t) {
|
|
862
|
+
this.likeBtn?.classList.toggle("imp-btn--active", t === "like"), this.dislikeBtn?.classList.toggle("imp-btn--active", t === "dislike");
|
|
863
|
+
}
|
|
864
|
+
// === ⋯ menu ===========================================================
|
|
865
|
+
buildMoreDropdown(t) {
|
|
866
|
+
const e = this.player, i = e.actionsOptions, { labels: s, icons: n } = e;
|
|
867
|
+
i.addTo && this.actionItems.push({ value: "addTo", label: s.addTo, icon: n.addTo, run: () => e.emit("action", { id: "addTo" }) }), i.share && this.actionItems.push({ value: "share", label: s.share, icon: n.share, run: () => void e.share() }), i.report && this.actionItems.push({ value: "report", label: s.report, icon: n.report, run: () => e.emit("action", { id: "report" }) });
|
|
868
|
+
for (const h of i.custom ?? [])
|
|
869
|
+
this.actionItems.push({ value: `custom:${h.id}`, label: h.title, icon: h.icon, run: () => e.emit("customaction", { id: h.id }) });
|
|
870
|
+
const o = m("imp-btn--more", s.more, n.more);
|
|
871
|
+
this.moreMenu = new M(o);
|
|
872
|
+
const a = l("div", "imp-controls__menu-anchor imp-controls__more");
|
|
873
|
+
a.append(o, this.moreMenu.root), o.addEventListener("click", () => {
|
|
874
|
+
this.closeMenus(this.moreMenu), this.moreMenu?.toggle(this.buildMoreSections());
|
|
875
|
+
}), t.append(a), this.moreWrap = a;
|
|
876
|
+
}
|
|
877
|
+
/** ⋯ menu = overflowed controls (in display order) + consumer actions. */
|
|
878
|
+
buildMoreSections() {
|
|
879
|
+
const t = [], e = /* @__PURE__ */ new Map(), i = [], s = ["prev", "next", "seekBack", "seekFwd", "like", "dislike", "scenes", "pip", "list", "subtitles", "settings", "quality"], n = this.collapsibles.filter((o) => this.overflowed.has(o.key) && o.available()).sort((o, a) => s.indexOf(o.key) - s.indexOf(a.key));
|
|
880
|
+
for (const o of n) {
|
|
881
|
+
const a = o.section();
|
|
882
|
+
if (a)
|
|
883
|
+
if (!a.title && a.items.length === 1) {
|
|
884
|
+
const h = a.items[0];
|
|
885
|
+
i.push(h), e.set(h.value, () => a.onSelect(h.value));
|
|
886
|
+
} else
|
|
887
|
+
t.push(a);
|
|
888
|
+
}
|
|
889
|
+
if (i.length > 0 && t.unshift({ title: "", items: i, onSelect: (o) => e.get(o)?.() }), this.actionItems.length > 0) {
|
|
890
|
+
const o = new Map(this.actionItems.map((a) => [a.value, a.run]));
|
|
891
|
+
t.push({
|
|
892
|
+
title: "",
|
|
893
|
+
items: this.actionItems.map(({ value: a, label: h, icon: c }) => ({ value: a, label: h, icon: c, active: !1 })),
|
|
894
|
+
onSelect: (a) => o.get(a)?.()
|
|
895
|
+
});
|
|
896
|
+
}
|
|
897
|
+
return t;
|
|
898
|
+
}
|
|
899
|
+
simpleSection(t, e, i, s) {
|
|
900
|
+
return { title: "", items: [{ value: t, label: e, icon: i, active: !1 }], onSelect: () => s() };
|
|
901
|
+
}
|
|
902
|
+
speedSection() {
|
|
903
|
+
const t = this.player;
|
|
904
|
+
return {
|
|
905
|
+
title: t.labels.speed,
|
|
906
|
+
items: t.playbackRates.map((e) => ({
|
|
907
|
+
label: e === 1 ? "1× (normal)" : `${e}×`,
|
|
908
|
+
value: String(e),
|
|
909
|
+
active: t.playbackRate === e
|
|
910
|
+
})),
|
|
911
|
+
onSelect: (e) => t.setPlaybackRate(Number(e))
|
|
912
|
+
};
|
|
913
|
+
}
|
|
914
|
+
qualitySection() {
|
|
915
|
+
const t = this.player;
|
|
916
|
+
return {
|
|
917
|
+
title: t.labels.quality,
|
|
918
|
+
items: [
|
|
919
|
+
...t.qualityAutoAvailable ? [{ label: t.labels.qualityAuto, value: "-1", active: t.currentQuality === -1 }] : [],
|
|
920
|
+
...t.qualityLevels.map((e) => ({ label: e.label, value: String(e.index), active: t.currentQuality === e.index }))
|
|
921
|
+
],
|
|
922
|
+
onSelect: (e) => t.setQuality(Number(e))
|
|
923
|
+
};
|
|
924
|
+
}
|
|
925
|
+
subtitlesSection() {
|
|
926
|
+
const t = this.player;
|
|
927
|
+
return {
|
|
928
|
+
title: t.labels.subtitles,
|
|
929
|
+
items: [
|
|
930
|
+
{ label: t.labels.subtitlesOff, value: "-1", active: t.activeSubtitle === -1 },
|
|
931
|
+
...t.subtitleTracks.map((e, i) => ({ label: e.label, value: String(i), active: t.activeSubtitle === i }))
|
|
932
|
+
],
|
|
933
|
+
onSelect: (e) => t.setSubtitle(Number(e))
|
|
934
|
+
};
|
|
935
|
+
}
|
|
936
|
+
// === dynamic overflow =================================================
|
|
937
|
+
scheduleReflow() {
|
|
938
|
+
this.reflowScheduled || (this.reflowScheduled = !0, requestAnimationFrame(() => {
|
|
939
|
+
this.reflowScheduled = !1, this.reflow();
|
|
940
|
+
}));
|
|
941
|
+
}
|
|
942
|
+
/**
|
|
943
|
+
* Hide controls that don't fit (lowest priority first) and remember them so
|
|
944
|
+
* the ⋯ menu can offer them. Unavailable controls are hidden outright.
|
|
945
|
+
*/
|
|
946
|
+
reflow() {
|
|
947
|
+
const t = window.innerWidth <= 767;
|
|
948
|
+
this.center.style.display = t ? "flex" : "none", this.playBtn && (this.playBtn.style.display = t ? "none" : ""), this.overflowed.clear();
|
|
949
|
+
for (const s of this.collapsibles) {
|
|
950
|
+
if (t && _.CENTER_KEYS.has(s.key)) {
|
|
951
|
+
s.el.style.display = "none";
|
|
952
|
+
continue;
|
|
953
|
+
}
|
|
954
|
+
const n = s.available();
|
|
955
|
+
s.el.style.display = n ? "" : "none", s.el.classList.remove("imp-collapsed");
|
|
956
|
+
}
|
|
957
|
+
const e = this.collapsibles.filter((s) => s.available() && !(t && _.CENTER_KEYS.has(s.key))).sort((s, n) => s.priority - n.priority);
|
|
958
|
+
let i = e.length;
|
|
959
|
+
for (; i-- > 0 && this.overflowsRow(); ) {
|
|
960
|
+
const s = e.find((n) => !this.overflowed.has(n.key));
|
|
961
|
+
if (!s) break;
|
|
962
|
+
s.el.style.display = "none", s.el.classList.add("imp-collapsed"), this.overflowed.add(s.key);
|
|
963
|
+
}
|
|
964
|
+
if (this.moreWrap) {
|
|
965
|
+
const s = this.actionItems.length > 0 || this.overflowed.size > 0;
|
|
966
|
+
this.moreWrap.style.display = s ? "" : "none";
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
overflowsRow() {
|
|
970
|
+
const t = this.row.getBoundingClientRect().right;
|
|
971
|
+
return this.rightGroup.getBoundingClientRect().right > t + 1;
|
|
972
|
+
}
|
|
973
|
+
bind() {
|
|
974
|
+
const t = this.player;
|
|
975
|
+
this.disposers.push(
|
|
976
|
+
t.on("timeupdate", ({ currentTime: e, duration: i }) => {
|
|
977
|
+
this.progress.update(e, i, t.bufferedEnd), this.timeLabel && (t.live ? (this.timeLabel.hidden = !0, this.liveBadge.hidden = !1) : (this.timeLabel.hidden = !1, this.liveBadge.hidden = !0, this.timeLabel.textContent = `${w(e)} / ${w(i)}`));
|
|
978
|
+
}),
|
|
979
|
+
t.on("play", () => this.syncPlayState()),
|
|
980
|
+
t.on("pause", () => this.syncPlayState()),
|
|
981
|
+
t.on("ended", () => this.syncPlayState()),
|
|
982
|
+
t.on("volumechange", () => this.syncVolume()),
|
|
983
|
+
t.on("chapterchange", ({ chapter: e }) => {
|
|
984
|
+
this.chapterLabel.textContent = e?.title ?? "";
|
|
985
|
+
}),
|
|
986
|
+
t.on("fullscreenchange", ({ active: e }) => {
|
|
987
|
+
this.fullscreenBtn && E(this.fullscreenBtn, e ? t.icons.fullscreenExit : t.icons.fullscreen, e ? t.labels.exitFullscreen : t.labels.fullscreen), this.scheduleReflow();
|
|
988
|
+
}),
|
|
989
|
+
t.on("playlistitemchange", () => {
|
|
990
|
+
this.syncPlaylistButtons(), this.scheduleReflow();
|
|
991
|
+
}),
|
|
992
|
+
t.on("sourcechange", () => {
|
|
993
|
+
this.syncPlaylistButtons(), this.scheduleReflow();
|
|
994
|
+
})
|
|
995
|
+
), this.syncPlaylistButtons(), this.reflow();
|
|
996
|
+
}
|
|
997
|
+
syncPlayState() {
|
|
998
|
+
const t = this.player, [e, i] = t.ended ? [t.icons.replay, t.labels.replay] : t.paused ? [t.icons.play, t.labels.play] : [t.icons.pause, t.labels.pause];
|
|
999
|
+
this.playBtn && E(this.playBtn, e, i), this.centerPlayBtn && E(this.centerPlayBtn, e, i);
|
|
1000
|
+
}
|
|
1001
|
+
syncVolume() {
|
|
1002
|
+
if (!this.muteBtn) return;
|
|
1003
|
+
const t = this.player, e = t.muted ? 0 : t.volume, i = e === 0 ? t.icons.volumeMute : e < 0.5 ? t.icons.volumeLow : t.icons.volumeHigh;
|
|
1004
|
+
E(this.muteBtn, i, t.muted ? t.labels.unmute : t.labels.mute), this.volumeSlider.value = String(e), this.volumeSlider.style.setProperty("--imp-volume-fill", `${e * 100}%`);
|
|
1005
|
+
}
|
|
1006
|
+
/** Called by the player when per-source data (chapters/quality/subtitles) changes. */
|
|
1007
|
+
syncFeatureButtons() {
|
|
1008
|
+
this.scheduleReflow();
|
|
1009
|
+
}
|
|
1010
|
+
syncPlaylistButtons() {
|
|
1011
|
+
const t = this.player;
|
|
1012
|
+
this.prevBtn && (this.prevBtn.disabled = !t.hasPrevious), this.nextBtn && (this.nextBtn.disabled = !t.hasNext), this.centerPrevBtn && (this.centerPrevBtn.disabled = !t.hasPrevious), this.centerNextBtn && (this.centerNextBtn.disabled = !t.hasNext);
|
|
1013
|
+
}
|
|
1014
|
+
closeMenus(t) {
|
|
1015
|
+
for (const e of [this.settingsMenu, this.subtitlesMenu, this.qualityMenu, this.moreMenu])
|
|
1016
|
+
e && e !== t && e.close();
|
|
1017
|
+
}
|
|
1018
|
+
toggleSettingsMenu() {
|
|
1019
|
+
this.settingsMenu && (this.closeMenus(this.settingsMenu), this.settingsMenu.toggle([this.speedSection()]));
|
|
1020
|
+
}
|
|
1021
|
+
toggleQualityMenu() {
|
|
1022
|
+
!this.qualityMenu || this.player.qualityLevels.length === 0 || (this.closeMenus(this.qualityMenu), this.qualityMenu.toggle([this.qualitySection()]));
|
|
1023
|
+
}
|
|
1024
|
+
toggleSubtitlesMenu() {
|
|
1025
|
+
!this.subtitlesMenu || this.player.subtitleTracks.length === 0 || (this.closeMenus(this.subtitlesMenu), this.subtitlesMenu.toggle([this.subtitlesSection()]));
|
|
1026
|
+
}
|
|
1027
|
+
destroy() {
|
|
1028
|
+
for (const t of this.disposers) t();
|
|
1029
|
+
this.disposers = [], this.resizeObserver?.disconnect(), this.onWindowResize && window.removeEventListener("resize", this.onWindowResize), this.settingsMenu?.destroy(), this.subtitlesMenu?.destroy(), this.qualityMenu?.destroy(), this.moreMenu?.destroy(), this.root.remove();
|
|
1030
|
+
}
|
|
1031
|
+
};
|
|
1032
|
+
_.CENTER_KEYS = /* @__PURE__ */ new Set(["seekBack", "seekFwd", "prev", "next"]);
|
|
1033
|
+
let F = _;
|
|
1034
|
+
class ft {
|
|
1035
|
+
constructor(t) {
|
|
1036
|
+
this.root = l("div", "imp-poster"), this.image = l("div", "imp-poster__image");
|
|
1037
|
+
const e = m("imp-poster__play", t.labels.play, t.icons.bigPlay);
|
|
1038
|
+
e.addEventListener("click", () => void t.play()), this.root.append(this.image, e), this.root.addEventListener("click", (i) => {
|
|
1039
|
+
(i.target === this.root || i.target === this.image) && t.play();
|
|
1040
|
+
});
|
|
1041
|
+
}
|
|
1042
|
+
setSource(t) {
|
|
1043
|
+
this.image.style.backgroundImage = t?.poster ? `url("${t.poster}")` : "";
|
|
1044
|
+
}
|
|
1045
|
+
show() {
|
|
1046
|
+
this.root.hidden = !1;
|
|
1047
|
+
}
|
|
1048
|
+
hide() {
|
|
1049
|
+
this.root.hidden = !0;
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
class vt {
|
|
1053
|
+
constructor(t) {
|
|
1054
|
+
this.labels = t, this.channelUrl = null, this.custom = null, this.root = l("div", "imp-pause-screen"), this.root.hidden = !0, this.defaultContent = l("div", "imp-pause-screen__default"), this.channel = l("button", "imp-channel", { type: "button" }), this.channel.hidden = !0, this.channelAvatar = l("div", "imp-channel__avatar"), this.channelName = l("div", "imp-channel__name"), this.channel.append(this.channelName, this.channelAvatar), this.channel.addEventListener("click", () => {
|
|
1055
|
+
this.channelUrl && window.open(this.channelUrl, "_blank", "noopener");
|
|
1056
|
+
});
|
|
1057
|
+
const e = l("div", "imp-pause-screen__heading");
|
|
1058
|
+
this.title = l("div", "imp-pause-screen__title"), this.description = l("div", "imp-pause-screen__description"), this.sponsor = l("a", "imp-sponsor", { target: "_blank", rel: "nofollow noopener" }), this.sponsor.hidden = !0, this.sponsorLabel = l("span", "imp-sponsor__label"), this.sponsorText = l("span", "imp-sponsor__text"), this.sponsor.append(this.sponsorLabel, this.sponsorText), this.sponsor.addEventListener("click", (i) => i.stopPropagation()), e.append(this.title, this.description, this.sponsor), this.defaultContent.append(e, this.channel), this.root.append(this.defaultContent);
|
|
1059
|
+
}
|
|
1060
|
+
setCustomContent(t) {
|
|
1061
|
+
this.custom?.remove(), this.custom = t, t && (t.classList.add("imp-pause-screen__custom"), this.root.append(t)), this.defaultContent.hidden = t !== null, this.root.classList.toggle("imp-pause-screen--custom", t !== null);
|
|
1062
|
+
}
|
|
1063
|
+
/** True while consumer-provided custom content is mounted. */
|
|
1064
|
+
get hasCustom() {
|
|
1065
|
+
return this.custom !== null;
|
|
1066
|
+
}
|
|
1067
|
+
setSource(t) {
|
|
1068
|
+
this.title.textContent = t?.title ?? "", this.description.textContent = t?.description ?? "";
|
|
1069
|
+
const e = t?.sponsor;
|
|
1070
|
+
this.sponsor.hidden = !e, e && (this.sponsor.href = e.url, this.sponsorText.textContent = e.text, this.sponsorLabel.textContent = e.label ?? this.labels.sponsored);
|
|
1071
|
+
const i = t?.channel;
|
|
1072
|
+
this.channel.hidden = !i, this.channelUrl = i?.url ?? null, this.channel.classList.toggle("imp-channel--link", !!i?.url), i && (this.channelName.textContent = i.name, this.channelAvatar.className = `imp-channel__avatar imp-channel__avatar--${i.avatarShape ?? "circle"}`, i.avatar ? (this.channelAvatar.textContent = "", this.channelAvatar.style.backgroundImage = `url("${i.avatar}")`) : (this.channelAvatar.style.backgroundImage = "", this.channelAvatar.textContent = (i.name.trim()[0] ?? "?").toUpperCase()));
|
|
1073
|
+
}
|
|
1074
|
+
show() {
|
|
1075
|
+
this.root.hidden = !1;
|
|
1076
|
+
}
|
|
1077
|
+
hide() {
|
|
1078
|
+
this.root.hidden = !0;
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
class gt {
|
|
1082
|
+
constructor(t) {
|
|
1083
|
+
this.player = t, this.root = l("div", "imp-related"), this.root.hidden = !0, this.heading = l("div", "imp-related__title"), this.grid = l("div", "imp-related__grid");
|
|
1084
|
+
const e = m("imp-related__close", "Close", t.icons.close);
|
|
1085
|
+
e.addEventListener("click", () => this.hide()), this.root.append(e, this.heading, this.grid);
|
|
1086
|
+
}
|
|
1087
|
+
setOptions(t) {
|
|
1088
|
+
if (this.grid.textContent = "", !!t) {
|
|
1089
|
+
this.heading.textContent = t.title ?? this.player.labels.related;
|
|
1090
|
+
for (const e of t.items)
|
|
1091
|
+
this.grid.append(this.buildCard(e));
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
buildCard(t) {
|
|
1095
|
+
const e = l("button", "imp-related__card", { type: "button" }), i = l("div", "imp-related__thumb");
|
|
1096
|
+
if (t.poster && (i.style.backgroundImage = `url("${t.poster}")`), t.duration) {
|
|
1097
|
+
const n = l("span", "imp-related__duration");
|
|
1098
|
+
n.textContent = t.duration, i.append(n);
|
|
1099
|
+
}
|
|
1100
|
+
const s = l("div", "imp-related__card-title");
|
|
1101
|
+
return s.textContent = t.title, e.append(i, s), e.addEventListener("click", () => {
|
|
1102
|
+
this.player.emit("relatedclick", { item: t }), this.hide(), t.source ? (this.player.load(t.source), this.player.play()) : t.url && window.open(t.url, "_blank", "noopener");
|
|
1103
|
+
}), e;
|
|
1104
|
+
}
|
|
1105
|
+
get visible() {
|
|
1106
|
+
return !this.root.hidden;
|
|
1107
|
+
}
|
|
1108
|
+
show() {
|
|
1109
|
+
this.grid.childElementCount !== 0 && (this.root.hidden = !1, this.player.emit("relatedshow", void 0));
|
|
1110
|
+
}
|
|
1111
|
+
hide() {
|
|
1112
|
+
this.root.hidden = !0;
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
class yt {
|
|
1116
|
+
constructor(t, e = "sidebar", i) {
|
|
1117
|
+
this.player = t, this.root = l("div", `imp-playlist imp-playlist--${e}`), this.root.hidden = !0;
|
|
1118
|
+
const s = l("div", "imp-playlist__header"), n = l("div", "imp-playlist__heading");
|
|
1119
|
+
if (i) {
|
|
1120
|
+
const h = l("div", "imp-playlist__kicker");
|
|
1121
|
+
h.textContent = t.labels.playlist;
|
|
1122
|
+
const c = l("div", "imp-playlist__name");
|
|
1123
|
+
c.textContent = i, n.append(h, c);
|
|
1124
|
+
} else
|
|
1125
|
+
n.textContent = t.labels.playlist;
|
|
1126
|
+
const o = l("div", "imp-playlist__tools");
|
|
1127
|
+
this.shuffleBtn = m("imp-playlist__shuffle", t.labels.shuffle, t.icons.shuffle), this.shuffleBtn.addEventListener("click", () => {
|
|
1128
|
+
t.setShuffle(!t.shuffle), this.syncModes();
|
|
1129
|
+
}), this.repeatBtn = m("imp-playlist__repeat", t.labels.repeat, t.icons.repeat), this.repeatBtn.addEventListener("click", () => {
|
|
1130
|
+
t.setRepeat(!t.repeat), this.syncModes();
|
|
1131
|
+
});
|
|
1132
|
+
const a = m("imp-playlist__close", "Close", t.icons.close);
|
|
1133
|
+
a.addEventListener("click", () => this.hide()), o.append(this.shuffleBtn, this.repeatBtn, a), s.append(n, o), this.list = l("div", "imp-playlist__list"), this.root.append(s, this.list), this.rebuild(), this.syncModes();
|
|
1134
|
+
}
|
|
1135
|
+
syncModes() {
|
|
1136
|
+
this.shuffleBtn.classList.toggle("imp-btn--active", this.player.shuffle), this.repeatBtn.classList.toggle("imp-btn--active", this.player.repeat);
|
|
1137
|
+
}
|
|
1138
|
+
rebuild() {
|
|
1139
|
+
this.list.textContent = "", this.player.playlist.forEach((t, e) => {
|
|
1140
|
+
const i = l("button", "imp-playlist__item", { type: "button" });
|
|
1141
|
+
e === this.player.index && i.classList.add("imp-playlist__item--active");
|
|
1142
|
+
const s = l("div", "imp-playlist__thumb");
|
|
1143
|
+
t.poster && (s.style.backgroundImage = `url("${t.poster}")`);
|
|
1144
|
+
const n = l("div", "imp-playlist__meta"), o = l("div", "imp-playlist__title");
|
|
1145
|
+
if (o.textContent = t.title ?? `#${e + 1}`, n.append(o), t.duration) {
|
|
1146
|
+
const a = l("div", "imp-playlist__duration");
|
|
1147
|
+
a.textContent = w(t.duration), n.append(a);
|
|
1148
|
+
}
|
|
1149
|
+
i.append(s, n), i.addEventListener("click", () => {
|
|
1150
|
+
this.player.playItem(e);
|
|
1151
|
+
}), this.list.append(i);
|
|
1152
|
+
});
|
|
1153
|
+
}
|
|
1154
|
+
get visible() {
|
|
1155
|
+
return !this.root.hidden;
|
|
1156
|
+
}
|
|
1157
|
+
show() {
|
|
1158
|
+
this.rebuild(), this.root.hidden = !1;
|
|
1159
|
+
}
|
|
1160
|
+
hide() {
|
|
1161
|
+
this.root.hidden = !0;
|
|
1162
|
+
}
|
|
1163
|
+
toggle() {
|
|
1164
|
+
this.visible ? this.hide() : this.show();
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
class bt {
|
|
1168
|
+
constructor(t, e = "bottom") {
|
|
1169
|
+
this.player = t, this.items = [], this.root = l("div", `imp-playlist imp-scenes imp-playlist--${e}`), this.root.hidden = !0;
|
|
1170
|
+
const i = l("div", "imp-playlist__header"), s = l("span");
|
|
1171
|
+
s.textContent = t.labels.scenes;
|
|
1172
|
+
const n = m("imp-playlist__close", "Close", t.icons.close);
|
|
1173
|
+
n.addEventListener("click", () => this.hide()), i.append(s, n), this.list = l("div", "imp-playlist__list"), this.root.append(i, this.list), t.on("chapterchange", () => this.syncActive());
|
|
1174
|
+
}
|
|
1175
|
+
/** Re-render from the player's current chapters + thumbnail track. */
|
|
1176
|
+
rebuild() {
|
|
1177
|
+
this.list.textContent = "", this.items = [];
|
|
1178
|
+
const t = this.player.thumbnails;
|
|
1179
|
+
for (const e of this.player.chapterList) {
|
|
1180
|
+
const i = l("button", "imp-playlist__item", { type: "button" }), s = l("div", "imp-playlist__thumb"), n = t?.cueAt(e.start + 0.01) ?? null;
|
|
1181
|
+
if (n?.xywh) {
|
|
1182
|
+
const c = l("div", "imp-scenes__sprite");
|
|
1183
|
+
c.style.width = `${n.xywh.w}px`, c.style.height = `${n.xywh.h}px`, c.style.backgroundImage = `url("${n.src}")`, c.style.backgroundPosition = `-${n.xywh.x}px -${n.xywh.y}px`, c.style.setProperty("--imp-sprite-w", String(n.xywh.w)), s.append(c);
|
|
1184
|
+
} else n && (s.style.backgroundImage = `url("${n.src}")`);
|
|
1185
|
+
const o = l("div", "imp-playlist__meta"), a = l("div", "imp-playlist__title");
|
|
1186
|
+
a.textContent = e.title || w(e.start);
|
|
1187
|
+
const h = l("div", "imp-playlist__duration");
|
|
1188
|
+
h.textContent = w(e.start), o.append(a, h), i.append(s, o), i.addEventListener("click", () => {
|
|
1189
|
+
this.player.seek(e.start), this.player.play();
|
|
1190
|
+
}), this.list.append(i), this.items.push(i);
|
|
1191
|
+
}
|
|
1192
|
+
this.syncActive();
|
|
1193
|
+
}
|
|
1194
|
+
syncActive() {
|
|
1195
|
+
const t = this.player.chapter, e = this.player.chapterList;
|
|
1196
|
+
this.items.forEach((i, s) => {
|
|
1197
|
+
i.classList.toggle("imp-playlist__item--active", t !== null && e[s] === t);
|
|
1198
|
+
});
|
|
1199
|
+
}
|
|
1200
|
+
get visible() {
|
|
1201
|
+
return !this.root.hidden;
|
|
1202
|
+
}
|
|
1203
|
+
show() {
|
|
1204
|
+
this.rebuild(), this.root.hidden = !1;
|
|
1205
|
+
}
|
|
1206
|
+
hide() {
|
|
1207
|
+
this.root.hidden = !0;
|
|
1208
|
+
}
|
|
1209
|
+
toggle() {
|
|
1210
|
+
this.visible ? this.hide() : this.show();
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
function Q(r) {
|
|
1214
|
+
return r ? typeof r == "string" ? [{ src: r, label: "Subtitles" }] : Array.isArray(r) ? r : [r] : [];
|
|
1215
|
+
}
|
|
1216
|
+
const kt = {
|
|
1217
|
+
play: !0,
|
|
1218
|
+
progress: !0,
|
|
1219
|
+
time: !0,
|
|
1220
|
+
volume: !0,
|
|
1221
|
+
fullscreen: !0,
|
|
1222
|
+
pip: !0,
|
|
1223
|
+
settings: !0,
|
|
1224
|
+
quality: !1,
|
|
1225
|
+
scenes: !0,
|
|
1226
|
+
heatmap: !0,
|
|
1227
|
+
subtitles: !0,
|
|
1228
|
+
seekButtons: !0,
|
|
1229
|
+
playlist: !0,
|
|
1230
|
+
hideDelay: 2500
|
|
1231
|
+
};
|
|
1232
|
+
class _t {
|
|
1233
|
+
constructor(t, e = {}) {
|
|
1234
|
+
this.scrubbing = !1, this.emitter = new Y(), this.abort = new AbortController(), this.sources = [], this.currentIndex = -1, this.sourceController = null, this.loadToken = 0, this.chapters = [], this.currentChapter = null, this.progressiveQuality = -1, this.thumbTrack = null, this.shuffleMode = !1, this.adManager = null, this.playedOnce = !1, this.idleTimer = null, this.destroyed = !1;
|
|
1235
|
+
const i = typeof t == "string" ? document.querySelector(t) : t;
|
|
1236
|
+
if (!i) throw new Error(`[itube-player] mount target not found: ${String(t)}`);
|
|
1237
|
+
this.mount = i, this.options = e, this.labels = { ...I, ...tt(e.language), ...e.labels }, this.icons = { ...J, ...e.icons }, this.controlsOptions = { ...kt, ...e.controls }, this.actionsOptions = e.actions ?? {}, this.playbackRates = e.playbackRates ?? [0.5, 0.75, 1, 1.25, 1.5, 2], this.seekStep = e.seekStep ?? 10, this.playlistOptions = { title: "", autoAdvance: !0, loop: !1, shuffle: !1, startIndex: 0, layout: "sidebar", ...e.playlist }, this.shuffleMode = this.playlistOptions.shuffle, this.sources = e.source ? Array.isArray(e.source) ? [...e.source] : [e.source] : [], this.container = l("div", "imp-player", { tabindex: "0" }), e.className && this.container.classList.add(e.className), e.styling?.themeColor && this.container.style.setProperty("--imp-accent", e.styling.themeColor), e.styling?.likeColor && this.container.style.setProperty("--imp-like", e.styling.likeColor), e.styling?.dislikeColor && this.container.style.setProperty("--imp-dislike", e.styling.dislikeColor), this.video = l("video", "imp-video"), e.playsInline !== !1 && this.video.setAttribute("playsinline", ""), e.crossOrigin !== void 0 && (this.video.crossOrigin = e.crossOrigin), this.video.preload = "metadata", e.loop && (this.video.loop = !0), e.muted && (this.video.muted = !0), this.video.volume = y(e.volume ?? 1, 0, 1);
|
|
1238
|
+
const s = l("div", "imp-layer");
|
|
1239
|
+
this.spinner = l("div", "imp-spinner"), this.spinner.hidden = !0, this.errorBox = l("div", "imp-error"), this.errorBox.hidden = !0, this.pauseScreen = new vt(this.labels), this.poster = new ft(this), this.related = new gt(this), this.related.setOptions(e.related), this.controls = new F(this), this.playlistPanel = new yt(this, this.playlistOptions.layout, this.playlistOptions.title || void 0), this.scenesPanel = new bt(this, e.scenes?.layout ?? "bottom");
|
|
1240
|
+
const n = l("div", "imp-layer__middle");
|
|
1241
|
+
n.append(this.spinner, this.errorBox, this.controls.center);
|
|
1242
|
+
const o = l("div", "imp-layer__bottom");
|
|
1243
|
+
o.append(this.controls.root), s.append(this.pauseScreen.root, this.related.root, n, o), this.container.append(this.video, s, this.poster.root, this.playlistPanel.root, this.scenesPanel.root), this.mount.append(this.container), e.pauseScreen instanceof HTMLElement ? this.pauseScreen.setCustomContent(e.pauseScreen) : typeof e.pauseScreen == "function" && this.pauseScreen.setCustomContent(e.pauseScreen(this));
|
|
1244
|
+
const a = e.adConfig ?? e.ads;
|
|
1245
|
+
a && a.adList.length > 0 && (this.adManager = new lt(
|
|
1246
|
+
{ container: this.container, contentVideo: this.video, emitter: this.emitter, labels: this.labels, closeIcon: this.icons.close },
|
|
1247
|
+
a
|
|
1248
|
+
)), this.emitter.on("error", ({ message: h }) => {
|
|
1249
|
+
this.errorBox.textContent = h, this.errorBox.hidden = !1, this.spinner.hidden = !0, this.poster.hide();
|
|
1250
|
+
}), this.bindVideoEvents(), this.bindIdleHide(), e.keyboard !== !1 && this.bindKeyboard(), this.video.addEventListener("click", () => {
|
|
1251
|
+
this.playedOnce && !this.adPlaying && this.togglePlay();
|
|
1252
|
+
}, { signal: this.abort.signal }), this.pauseScreen.root.addEventListener("click", (h) => {
|
|
1253
|
+
this.pauseScreen.hasCustom || h.target === this.pauseScreen.root && this.togglePlay();
|
|
1254
|
+
}, { signal: this.abort.signal }), this.sources.length > 0 && this.loadItem(y(this.playlistOptions.startIndex, 0, this.sources.length - 1), !!e.autoplay), this.emitter.emit("ready", { player: this });
|
|
1255
|
+
}
|
|
1256
|
+
// === events ===========================================================
|
|
1257
|
+
on(t, e) {
|
|
1258
|
+
return this.emitter.on(t, e);
|
|
1259
|
+
}
|
|
1260
|
+
once(t, e) {
|
|
1261
|
+
return this.emitter.once(t, e);
|
|
1262
|
+
}
|
|
1263
|
+
off(t, e) {
|
|
1264
|
+
this.emitter.off(t, e);
|
|
1265
|
+
}
|
|
1266
|
+
/** @internal — UI modules emit through the player. */
|
|
1267
|
+
emit(t, e) {
|
|
1268
|
+
this.emitter.emit(t, e);
|
|
1269
|
+
}
|
|
1270
|
+
// === playback =========================================================
|
|
1271
|
+
get paused() {
|
|
1272
|
+
return this.video.paused;
|
|
1273
|
+
}
|
|
1274
|
+
get ended() {
|
|
1275
|
+
return this.video.ended;
|
|
1276
|
+
}
|
|
1277
|
+
get currentTime() {
|
|
1278
|
+
return this.video.currentTime;
|
|
1279
|
+
}
|
|
1280
|
+
get duration() {
|
|
1281
|
+
return this.video.duration || 0;
|
|
1282
|
+
}
|
|
1283
|
+
get live() {
|
|
1284
|
+
return this.video.duration === 1 / 0;
|
|
1285
|
+
}
|
|
1286
|
+
get bufferedEnd() {
|
|
1287
|
+
const t = this.video.buffered;
|
|
1288
|
+
return t.length > 0 ? t.end(t.length - 1) : 0;
|
|
1289
|
+
}
|
|
1290
|
+
get adPlaying() {
|
|
1291
|
+
return this.adManager?.adPlaying ?? !1;
|
|
1292
|
+
}
|
|
1293
|
+
async play() {
|
|
1294
|
+
this.destroyed || this.adPlaying || this.adManager && (await this.adManager.playPreRoll(), this.destroyed || !this.video.paused) || await this.video.play();
|
|
1295
|
+
}
|
|
1296
|
+
pause() {
|
|
1297
|
+
this.adPlaying || this.video.pause();
|
|
1298
|
+
}
|
|
1299
|
+
togglePlay() {
|
|
1300
|
+
this.video.paused || this.video.ended ? this.play() : this.pause();
|
|
1301
|
+
}
|
|
1302
|
+
seek(t) {
|
|
1303
|
+
Number.isFinite(this.video.duration) && (this.video.currentTime = y(t, 0, this.video.duration));
|
|
1304
|
+
}
|
|
1305
|
+
/** Relative seek, e.g. `skip(-10)`. */
|
|
1306
|
+
skip(t) {
|
|
1307
|
+
this.seek(this.video.currentTime + t);
|
|
1308
|
+
}
|
|
1309
|
+
// === volume ===========================================================
|
|
1310
|
+
get volume() {
|
|
1311
|
+
return this.video.volume;
|
|
1312
|
+
}
|
|
1313
|
+
get muted() {
|
|
1314
|
+
return this.video.muted;
|
|
1315
|
+
}
|
|
1316
|
+
setVolume(t) {
|
|
1317
|
+
this.video.volume = y(t, 0, 1), t > 0 && (this.video.muted = !1);
|
|
1318
|
+
}
|
|
1319
|
+
setMuted(t) {
|
|
1320
|
+
this.video.muted = t;
|
|
1321
|
+
}
|
|
1322
|
+
toggleMute() {
|
|
1323
|
+
this.video.muted = !this.video.muted;
|
|
1324
|
+
}
|
|
1325
|
+
// === rate / quality ===================================================
|
|
1326
|
+
get playbackRate() {
|
|
1327
|
+
return this.video.playbackRate;
|
|
1328
|
+
}
|
|
1329
|
+
setPlaybackRate(t) {
|
|
1330
|
+
this.video.playbackRate = t, this.emitter.emit("ratechange", { rate: t });
|
|
1331
|
+
}
|
|
1332
|
+
get qualityLevels() {
|
|
1333
|
+
if (this.sourceController?.kind === "hls") return this.sourceController.levels;
|
|
1334
|
+
const t = this.source?.qualities;
|
|
1335
|
+
return t ? t.map((e, i) => ({ index: i, label: e.label ?? String(e.quality ?? i) })) : [];
|
|
1336
|
+
}
|
|
1337
|
+
get qualityAutoAvailable() {
|
|
1338
|
+
return this.sourceController?.kind === "hls";
|
|
1339
|
+
}
|
|
1340
|
+
get currentQuality() {
|
|
1341
|
+
return this.sourceController?.kind === "hls" ? this.sourceController.selected : this.progressiveQuality;
|
|
1342
|
+
}
|
|
1343
|
+
setQuality(t) {
|
|
1344
|
+
if (this.sourceController?.kind === "hls") {
|
|
1345
|
+
this.sourceController.setLevel(t);
|
|
1346
|
+
const n = t === -1 ? this.labels.qualityAuto : this.sourceController.levels.find((o) => o.index === t)?.label ?? String(t);
|
|
1347
|
+
this.emitter.emit("qualitychange", { label: n });
|
|
1348
|
+
return;
|
|
1349
|
+
}
|
|
1350
|
+
const e = this.source?.qualities;
|
|
1351
|
+
if (!e || !e[t] || t === this.progressiveQuality) return;
|
|
1352
|
+
const i = this.video.currentTime, s = !this.video.paused && !this.video.ended;
|
|
1353
|
+
this.progressiveQuality = t, this.video.src = e[t].src, this.video.currentTime = i, s && this.video.play().catch(() => {
|
|
1354
|
+
}), this.emitter.emit("qualitychange", { label: e[t].label ?? String(e[t].quality ?? t) });
|
|
1355
|
+
}
|
|
1356
|
+
// === subtitles ========================================================
|
|
1357
|
+
get subtitleTracks() {
|
|
1358
|
+
return Q(this.source?.subtitles);
|
|
1359
|
+
}
|
|
1360
|
+
get activeSubtitle() {
|
|
1361
|
+
const t = this.video.textTracks;
|
|
1362
|
+
for (let e = 0; e < t.length; e++)
|
|
1363
|
+
if (t[e].mode === "showing") return e;
|
|
1364
|
+
return -1;
|
|
1365
|
+
}
|
|
1366
|
+
setSubtitle(t) {
|
|
1367
|
+
const e = this.video.textTracks;
|
|
1368
|
+
for (let i = 0; i < e.length; i++)
|
|
1369
|
+
e[i].mode = i === t ? "showing" : "disabled";
|
|
1370
|
+
this.emitter.emit("subtitlechange", { track: this.subtitleTracks[t] ?? null });
|
|
1371
|
+
}
|
|
1372
|
+
// === playlist =========================================================
|
|
1373
|
+
get playlist() {
|
|
1374
|
+
return this.sources;
|
|
1375
|
+
}
|
|
1376
|
+
get index() {
|
|
1377
|
+
return this.currentIndex;
|
|
1378
|
+
}
|
|
1379
|
+
get source() {
|
|
1380
|
+
return this.sources[this.currentIndex] ?? null;
|
|
1381
|
+
}
|
|
1382
|
+
get hasPlaylist() {
|
|
1383
|
+
return this.sources.length > 1;
|
|
1384
|
+
}
|
|
1385
|
+
get hasNext() {
|
|
1386
|
+
return this.shuffleMode || this.playlistOptions.loop ? this.sources.length > 1 : this.currentIndex < this.sources.length - 1;
|
|
1387
|
+
}
|
|
1388
|
+
get hasPrevious() {
|
|
1389
|
+
return this.playlistOptions.loop ? this.sources.length > 1 : this.currentIndex > 0;
|
|
1390
|
+
}
|
|
1391
|
+
/** Shuffle mode: `next()` and auto-advance pick a random item. */
|
|
1392
|
+
get shuffle() {
|
|
1393
|
+
return this.shuffleMode;
|
|
1394
|
+
}
|
|
1395
|
+
setShuffle(t) {
|
|
1396
|
+
this.shuffleMode = t;
|
|
1397
|
+
}
|
|
1398
|
+
/** Repeat mode: after the last item the list starts over instead of ending. */
|
|
1399
|
+
get repeat() {
|
|
1400
|
+
return this.playlistOptions.loop;
|
|
1401
|
+
}
|
|
1402
|
+
setRepeat(t) {
|
|
1403
|
+
this.playlistOptions.loop = t;
|
|
1404
|
+
}
|
|
1405
|
+
next() {
|
|
1406
|
+
if (this.hasNext) {
|
|
1407
|
+
if (this.shuffleMode && this.sources.length > 1) {
|
|
1408
|
+
let t = this.currentIndex;
|
|
1409
|
+
for (; t === this.currentIndex; ) t = Math.floor(Math.random() * this.sources.length);
|
|
1410
|
+
this.playItem(t);
|
|
1411
|
+
return;
|
|
1412
|
+
}
|
|
1413
|
+
this.playItem((this.currentIndex + 1) % this.sources.length);
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
previous() {
|
|
1417
|
+
this.hasPrevious && this.playItem((this.currentIndex - 1 + this.sources.length) % this.sources.length);
|
|
1418
|
+
}
|
|
1419
|
+
/** Jump to a playlist item and start playback. */
|
|
1420
|
+
playItem(t) {
|
|
1421
|
+
t < 0 || t >= this.sources.length || this.loadItem(t, !0).then(() => {
|
|
1422
|
+
this.emitter.emit("playlistitemchange", { source: this.sources[t], index: t });
|
|
1423
|
+
});
|
|
1424
|
+
}
|
|
1425
|
+
togglePlaylistPanel() {
|
|
1426
|
+
this.scenesPanel.hide(), this.playlistPanel.toggle();
|
|
1427
|
+
}
|
|
1428
|
+
toggleScenesPanel() {
|
|
1429
|
+
this.playlistPanel.hide(), this.scenesPanel.toggle();
|
|
1430
|
+
}
|
|
1431
|
+
/** Thumbnail track of the current source (used by the seek tooltip and the scenes panel). */
|
|
1432
|
+
get thumbnails() {
|
|
1433
|
+
return this.thumbTrack;
|
|
1434
|
+
}
|
|
1435
|
+
/**
|
|
1436
|
+
* Replace what the player is playing. A single source resets the playlist
|
|
1437
|
+
* to one item; an array installs a new playlist.
|
|
1438
|
+
*/
|
|
1439
|
+
load(t, e = 0) {
|
|
1440
|
+
this.sources = Array.isArray(t) ? [...t] : [t], this.playlistPanel.rebuild(), this.loadItem(y(e, 0, this.sources.length - 1), !1);
|
|
1441
|
+
}
|
|
1442
|
+
// === fullscreen / pip =================================================
|
|
1443
|
+
get isFullscreen() {
|
|
1444
|
+
return document.fullscreenElement === this.container;
|
|
1445
|
+
}
|
|
1446
|
+
async toggleFullscreen() {
|
|
1447
|
+
this.isFullscreen ? await document.exitFullscreen() : this.container.requestFullscreen ? await this.container.requestFullscreen() : this.video.webkitEnterFullscreen?.();
|
|
1448
|
+
}
|
|
1449
|
+
async togglePip() {
|
|
1450
|
+
document.pictureInPictureElement === this.video ? await document.exitPictureInPicture() : await this.video.requestPictureInPicture();
|
|
1451
|
+
}
|
|
1452
|
+
// === chapters =========================================================
|
|
1453
|
+
/** Normalized chapters of the current source (after metadata is known). */
|
|
1454
|
+
get chapterList() {
|
|
1455
|
+
return this.chapters;
|
|
1456
|
+
}
|
|
1457
|
+
get chapter() {
|
|
1458
|
+
return this.currentChapter;
|
|
1459
|
+
}
|
|
1460
|
+
// === actions ==========================================================
|
|
1461
|
+
/**
|
|
1462
|
+
* Share the current video: native share sheet when the device has one,
|
|
1463
|
+
* otherwise the `action` event with `id: "share"` so the app can show
|
|
1464
|
+
* its own dialog.
|
|
1465
|
+
*/
|
|
1466
|
+
async share() {
|
|
1467
|
+
const t = {
|
|
1468
|
+
title: this.source?.title ?? document.title,
|
|
1469
|
+
url: window.location.href
|
|
1470
|
+
};
|
|
1471
|
+
if (typeof navigator.share == "function") {
|
|
1472
|
+
try {
|
|
1473
|
+
await navigator.share(t);
|
|
1474
|
+
} catch {
|
|
1475
|
+
}
|
|
1476
|
+
return;
|
|
1477
|
+
}
|
|
1478
|
+
this.emitter.emit("action", { id: "share" });
|
|
1479
|
+
}
|
|
1480
|
+
/** Highlight the like or dislike button (`null` clears both). */
|
|
1481
|
+
setLikeState(t) {
|
|
1482
|
+
this.controls.setLikeState(t);
|
|
1483
|
+
}
|
|
1484
|
+
/** Update like/dislike counters shown as tooltips above the buttons (`undefined` leaves one untouched). */
|
|
1485
|
+
setLikeCounts(t, e) {
|
|
1486
|
+
this.controls.setLikeCounts(t, e);
|
|
1487
|
+
}
|
|
1488
|
+
// === pause screen =====================================================
|
|
1489
|
+
/** Inject custom pause-screen content (used by the Vue wrapper's slot). */
|
|
1490
|
+
setPauseScreenContent(t) {
|
|
1491
|
+
this.pauseScreen.setCustomContent(t);
|
|
1492
|
+
}
|
|
1493
|
+
// === teardown =========================================================
|
|
1494
|
+
destroy() {
|
|
1495
|
+
this.destroyed || (this.destroyed = !0, this.loadToken++, this.adManager?.destroy(), this.controls.destroy(), this.sourceController?.destroy(), this.abort.abort(), this.idleTimer && clearTimeout(this.idleTimer), this.container.remove(), this.emitter.emit("destroy", void 0), this.emitter.removeAll());
|
|
1496
|
+
}
|
|
1497
|
+
// === internals ========================================================
|
|
1498
|
+
async loadItem(t, e) {
|
|
1499
|
+
const i = ++this.loadToken, s = this.sources[t];
|
|
1500
|
+
if (!s) return;
|
|
1501
|
+
this.currentIndex = t, this.sourceController?.destroy(), this.sourceController = null, this.chapters = [], this.currentChapter = null, this.progressiveQuality = -1, this.playedOnce = !1;
|
|
1502
|
+
for (const h of [...this.video.querySelectorAll("track")]) h.remove();
|
|
1503
|
+
this.adManager?.resetForNewSource(), this.related.hide(), this.pauseScreen.hide(), this.scenesPanel.hide(), this.thumbTrack = null, this.errorBox.hidden = !0, this.video.poster = s.poster ?? "", this.poster.setSource(s), this.pauseScreen.setSource(s), this.controls.progress.setChapters([]), this.controls.progress.setThumbnails(null), this.controls.progress.setHeatmap([]), e ? this.poster.hide() : this.poster.show(), this.playlistPanel.rebuild();
|
|
1504
|
+
for (const h of Q(s.subtitles)) {
|
|
1505
|
+
const c = l("track", "", {
|
|
1506
|
+
kind: "subtitles",
|
|
1507
|
+
src: h.src,
|
|
1508
|
+
label: h.label,
|
|
1509
|
+
...h.srclang ? { srclang: h.srclang } : {}
|
|
1510
|
+
});
|
|
1511
|
+
h.default && c.setAttribute("default", ""), this.video.append(c);
|
|
1512
|
+
}
|
|
1513
|
+
let n = s.src, o = s.type;
|
|
1514
|
+
if (s.qualities && s.qualities.length > 0) {
|
|
1515
|
+
const h = Math.max(0, s.qualities.findIndex((c) => c.src === s.src));
|
|
1516
|
+
this.progressiveQuality = h, n = s.qualities[h].src, o = s.qualities[h].type ?? o;
|
|
1517
|
+
}
|
|
1518
|
+
this.video.preload = this.adManager?.hasPendingPreRoll ? "auto" : "metadata";
|
|
1519
|
+
const a = await ut(this.video, n, o, {
|
|
1520
|
+
onLevels: () => {
|
|
1521
|
+
i === this.loadToken && this.controls.syncFeatureButtons();
|
|
1522
|
+
},
|
|
1523
|
+
onLevelSwitch: (h) => this.emitter.emit("qualitychange", { label: h }),
|
|
1524
|
+
onError: (h, c) => this.emitter.emit("error", { message: h, cause: c })
|
|
1525
|
+
});
|
|
1526
|
+
if (i !== this.loadToken) {
|
|
1527
|
+
a.destroy();
|
|
1528
|
+
return;
|
|
1529
|
+
}
|
|
1530
|
+
if (this.sourceController = a, this.emitter.emit("sourcechange", { source: s, index: t }), s.thumbnails && q.load(s.thumbnails).then((h) => {
|
|
1531
|
+
i === this.loadToken && (this.thumbTrack = h, this.controls.progress.setThumbnails(h), this.scenesPanel.visible && this.scenesPanel.rebuild());
|
|
1532
|
+
}).catch((h) => this.emitter.emit("error", { message: "Failed to load thumbnails track", cause: h })), s.chapters) {
|
|
1533
|
+
const h = (c) => {
|
|
1534
|
+
const u = () => {
|
|
1535
|
+
i === this.loadToken && (this.chapters = at(c, this.video.duration), this.controls.progress.setChapters(this.chapters), this.controls.syncFeatureButtons(), this.scenesPanel.visible && this.scenesPanel.rebuild());
|
|
1536
|
+
};
|
|
1537
|
+
Number.isFinite(this.video.duration) && this.video.duration > 0 ? u() : this.video.addEventListener("loadedmetadata", u, { once: !0, signal: this.abort.signal });
|
|
1538
|
+
};
|
|
1539
|
+
typeof s.chapters == "string" ? ht(s.chapters).then((c) => h(c)).catch((c) => this.emitter.emit("error", { message: "Failed to load chapters track", cause: c })) : h(s.chapters);
|
|
1540
|
+
}
|
|
1541
|
+
if (s.heatmap && s.heatmap.length > 0 && this.controlsOptions.heatmap) {
|
|
1542
|
+
const h = s.heatmap, c = () => {
|
|
1543
|
+
i === this.loadToken && this.controls.progress.setHeatmap(et(h, this.video.duration));
|
|
1544
|
+
};
|
|
1545
|
+
Number.isFinite(this.video.duration) && this.video.duration > 0 ? c() : this.video.addEventListener("loadedmetadata", c, { once: !0, signal: this.abort.signal });
|
|
1546
|
+
}
|
|
1547
|
+
e && this.play().catch(() => {
|
|
1548
|
+
i === this.loadToken && this.poster.show();
|
|
1549
|
+
});
|
|
1550
|
+
}
|
|
1551
|
+
bindVideoEvents() {
|
|
1552
|
+
const { signal: t } = this.abort, e = this.video;
|
|
1553
|
+
e.addEventListener("play", () => {
|
|
1554
|
+
this.playedOnce = !0, this.poster.hide(), this.pauseScreen.hide(), this.related.visible || this.related.hide(), this.related.hide(), this.emitter.emit("play", void 0), this.scheduleIdle();
|
|
1555
|
+
}, { signal: t }), e.addEventListener("pause", () => {
|
|
1556
|
+
this.emitter.emit("pause", void 0), this.showControlsNow(), !(this.video.ended || this.scrubbing || this.adPlaying || !this.playedOnce) && (this.options.pauseScreen !== !1 && this.pauseScreen.show(), this.options.related?.showOn?.includes("pause") && this.related.show());
|
|
1557
|
+
}, { signal: t }), e.addEventListener("ended", () => {
|
|
1558
|
+
this.handleEnded();
|
|
1559
|
+
}, { signal: t }), e.addEventListener("timeupdate", () => {
|
|
1560
|
+
this.emitter.emit("timeupdate", { currentTime: e.currentTime, duration: e.duration || 0 }), this.adManager?.checkMidRolls(e.currentTime);
|
|
1561
|
+
const i = K(this.chapters, e.currentTime);
|
|
1562
|
+
i !== this.currentChapter && (this.currentChapter = i, this.emitter.emit("chapterchange", { chapter: i }));
|
|
1563
|
+
}, { signal: t }), e.addEventListener("progress", () => {
|
|
1564
|
+
this.emitter.emit("progress", { buffered: this.bufferedEnd });
|
|
1565
|
+
}, { signal: t }), e.addEventListener("volumechange", () => {
|
|
1566
|
+
this.emitter.emit("volumechange", { volume: e.volume, muted: e.muted });
|
|
1567
|
+
}, { signal: t }), e.addEventListener("seeking", () => this.emitter.emit("seeking", { currentTime: e.currentTime }), { signal: t }), e.addEventListener("seeked", () => this.emitter.emit("seeked", { currentTime: e.currentTime }), { signal: t }), e.addEventListener("waiting", () => {
|
|
1568
|
+
this.spinner.hidden = !1;
|
|
1569
|
+
}, { signal: t });
|
|
1570
|
+
for (const i of ["playing", "canplay", "seeked"])
|
|
1571
|
+
e.addEventListener(i, () => {
|
|
1572
|
+
this.spinner.hidden = !0;
|
|
1573
|
+
}, { signal: t });
|
|
1574
|
+
e.addEventListener("error", () => {
|
|
1575
|
+
const i = e.error;
|
|
1576
|
+
i && this.sourceController?.kind !== "hls" && (e.getAttribute("src") !== null || e.currentSrc) && this.emitter.emit("error", { message: i.message || `Media error (code ${i.code})`, cause: i });
|
|
1577
|
+
}, { signal: t }), e.addEventListener("enterpictureinpicture", () => this.emitter.emit("pipchange", { active: !0 }), { signal: t }), e.addEventListener("leavepictureinpicture", () => this.emitter.emit("pipchange", { active: !1 }), { signal: t }), document.addEventListener("fullscreenchange", () => {
|
|
1578
|
+
this.emitter.emit("fullscreenchange", { active: this.isFullscreen }), this.container.classList.toggle("imp-player--fullscreen", this.isFullscreen);
|
|
1579
|
+
}, { signal: t });
|
|
1580
|
+
}
|
|
1581
|
+
async handleEnded() {
|
|
1582
|
+
if (this.emitter.emit("ended", void 0), this.showControlsNow(), !(this.adManager && (await this.adManager.playPostRoll(), this.destroyed))) {
|
|
1583
|
+
if (this.playlistOptions.autoAdvance && this.hasNext) {
|
|
1584
|
+
this.next();
|
|
1585
|
+
return;
|
|
1586
|
+
}
|
|
1587
|
+
(this.options.related?.showOn?.includes("ended") ?? !!this.options.related) && this.related.show();
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
// === controls visibility ==============================================
|
|
1591
|
+
bindIdleHide() {
|
|
1592
|
+
const { signal: t } = this.abort, e = () => {
|
|
1593
|
+
this.showControlsNow(), this.scheduleIdle();
|
|
1594
|
+
};
|
|
1595
|
+
this.container.addEventListener("pointermove", e, { signal: t }), this.container.addEventListener("pointerdown", e, { signal: t }), this.container.addEventListener("keydown", e, { signal: t }), this.container.addEventListener("pointerleave", () => {
|
|
1596
|
+
this.video.paused || this.container.classList.add("imp-player--idle");
|
|
1597
|
+
}, { signal: t });
|
|
1598
|
+
}
|
|
1599
|
+
showControlsNow() {
|
|
1600
|
+
this.container.classList.remove("imp-player--idle");
|
|
1601
|
+
}
|
|
1602
|
+
scheduleIdle() {
|
|
1603
|
+
this.idleTimer && clearTimeout(this.idleTimer), this.idleTimer = setTimeout(() => {
|
|
1604
|
+
!this.video.paused && !this.destroyed && this.container.classList.add("imp-player--idle");
|
|
1605
|
+
}, this.controlsOptions.hideDelay);
|
|
1606
|
+
}
|
|
1607
|
+
// === keyboard =========================================================
|
|
1608
|
+
bindKeyboard() {
|
|
1609
|
+
this.container.addEventListener("keydown", (t) => {
|
|
1610
|
+
const e = t.target;
|
|
1611
|
+
if (e.closest("input, select, textarea, [contenteditable]")) return;
|
|
1612
|
+
const s = e.closest("button") !== null;
|
|
1613
|
+
switch (t.key) {
|
|
1614
|
+
case " ":
|
|
1615
|
+
case "k":
|
|
1616
|
+
if (t.key === " " && s) return;
|
|
1617
|
+
t.preventDefault(), this.togglePlay();
|
|
1618
|
+
break;
|
|
1619
|
+
case "ArrowLeft":
|
|
1620
|
+
t.preventDefault(), this.skip(-this.seekStep);
|
|
1621
|
+
break;
|
|
1622
|
+
case "ArrowRight":
|
|
1623
|
+
t.preventDefault(), this.skip(this.seekStep);
|
|
1624
|
+
break;
|
|
1625
|
+
case "ArrowUp":
|
|
1626
|
+
t.preventDefault(), this.setVolume(this.volume + 0.1);
|
|
1627
|
+
break;
|
|
1628
|
+
case "ArrowDown":
|
|
1629
|
+
t.preventDefault(), this.setVolume(this.volume - 0.1);
|
|
1630
|
+
break;
|
|
1631
|
+
case "m":
|
|
1632
|
+
this.toggleMute();
|
|
1633
|
+
break;
|
|
1634
|
+
case "f":
|
|
1635
|
+
this.toggleFullscreen();
|
|
1636
|
+
break;
|
|
1637
|
+
case "Home":
|
|
1638
|
+
this.seek(0);
|
|
1639
|
+
break;
|
|
1640
|
+
case "End":
|
|
1641
|
+
this.seek(this.duration);
|
|
1642
|
+
break;
|
|
1643
|
+
default:
|
|
1644
|
+
/^[0-9]$/.test(t.key) && Number.isFinite(this.video.duration) && this.seek(Number(t.key) / 10 * this.video.duration);
|
|
1645
|
+
}
|
|
1646
|
+
}, { signal: this.abort.signal });
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1649
|
+
export {
|
|
1650
|
+
Y as Emitter,
|
|
1651
|
+
_t as Player,
|
|
1652
|
+
q as ThumbnailTrack,
|
|
1653
|
+
et as buildHeatmapValues,
|
|
1654
|
+
K as chapterAt,
|
|
1655
|
+
J as defaultIcons,
|
|
1656
|
+
I as defaultLabels,
|
|
1657
|
+
w as formatTime,
|
|
1658
|
+
tt as getLocale,
|
|
1659
|
+
it as heatmapPath,
|
|
1660
|
+
ct as isHlsSource,
|
|
1661
|
+
ht as loadChaptersVtt,
|
|
1662
|
+
at as normalizeChapters,
|
|
1663
|
+
Lt as registerLocale,
|
|
1664
|
+
St as registerLocales,
|
|
1665
|
+
xt as registeredLanguages,
|
|
1666
|
+
st as resolveVast
|
|
1667
|
+
};
|
|
1668
|
+
//# sourceMappingURL=core.js.map
|