itube-modern-player 0.2.0 → 0.2.2
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/README.md +31 -0
- package/dist/core.cjs +4 -4
- package/dist/core.cjs.map +1 -1
- package/dist/core.js +290 -280
- package/dist/core.js.map +1 -1
- package/dist/itube-modern-player.iife.js +4 -4
- package/dist/itube-modern-player.iife.js.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +1 -1
package/dist/core.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { d as I } from "./labels-C3gAZEm-.js";
|
|
2
2
|
function l(r, t, e) {
|
|
3
|
-
const
|
|
4
|
-
if (t && (
|
|
5
|
-
for (const [
|
|
6
|
-
return
|
|
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
7
|
}
|
|
8
8
|
function m(r, t, e) {
|
|
9
|
-
const
|
|
10
|
-
return
|
|
9
|
+
const i = l("button", `imp-btn ${r}`, { type: "button", "aria-label": t, title: t });
|
|
10
|
+
return i.innerHTML = e, i;
|
|
11
11
|
}
|
|
12
12
|
function E(r, t, e) {
|
|
13
13
|
r.innerHTML = t, e !== void 0 && (r.setAttribute("aria-label", e), r.setAttribute("title", e));
|
|
@@ -17,27 +17,27 @@ function y(r, t, e) {
|
|
|
17
17
|
}
|
|
18
18
|
function w(r) {
|
|
19
19
|
if (!Number.isFinite(r) || r < 0) return "0:00";
|
|
20
|
-
const t = Math.floor(r % 60), e = Math.floor(r / 60 % 60),
|
|
21
|
-
return
|
|
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
22
|
}
|
|
23
23
|
function R(r) {
|
|
24
24
|
const t = r.trim().match(/^(?:(\d+):)?(\d{1,2}):(\d{2})(?:[.,](\d{1,3}))?$/);
|
|
25
25
|
if (!t) return null;
|
|
26
|
-
const e = t[1] ? Number(t[1]) : 0,
|
|
27
|
-
return e * 3600 +
|
|
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
28
|
}
|
|
29
29
|
function U(r) {
|
|
30
30
|
const t = [], e = r.replace(/\r\n?/g, `
|
|
31
31
|
`).split(/\n\n+/);
|
|
32
|
-
for (const
|
|
33
|
-
const
|
|
32
|
+
for (const i of e) {
|
|
33
|
+
const s = i.split(`
|
|
34
34
|
`).filter((f) => f.trim() !== "");
|
|
35
|
-
if (
|
|
36
|
-
let n =
|
|
35
|
+
if (s.length === 0) continue;
|
|
36
|
+
let n = s.findIndex((f) => f.includes("-->"));
|
|
37
37
|
if (n === -1) continue;
|
|
38
|
-
const [o, a] =
|
|
38
|
+
const [o, a] = s[n].split("-->"), c = R(o), h = R((a ?? "").split(" ")[1] ?? a ?? "") ?? R(a ?? "");
|
|
39
39
|
if (c === null || h === null) continue;
|
|
40
|
-
const u =
|
|
40
|
+
const u = s.slice(n + 1).join(`
|
|
41
41
|
`).trim();
|
|
42
42
|
u && t.push({ start: c, end: h, text: u });
|
|
43
43
|
}
|
|
@@ -56,24 +56,24 @@ class Y {
|
|
|
56
56
|
}
|
|
57
57
|
/** Subscribe. Returns an unsubscribe function. */
|
|
58
58
|
on(t, e) {
|
|
59
|
-
let
|
|
60
|
-
return
|
|
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
61
|
}
|
|
62
62
|
once(t, e) {
|
|
63
|
-
const
|
|
64
|
-
|
|
63
|
+
const i = this.on(t, (s) => {
|
|
64
|
+
i(), e(s);
|
|
65
65
|
});
|
|
66
|
-
return
|
|
66
|
+
return i;
|
|
67
67
|
}
|
|
68
68
|
off(t, e) {
|
|
69
69
|
this.listeners.get(t)?.delete(e);
|
|
70
70
|
}
|
|
71
71
|
emit(t, e) {
|
|
72
|
-
const
|
|
73
|
-
if (
|
|
74
|
-
for (const
|
|
72
|
+
const i = this.listeners.get(t);
|
|
73
|
+
if (i)
|
|
74
|
+
for (const s of [...i])
|
|
75
75
|
try {
|
|
76
|
-
|
|
76
|
+
s(e);
|
|
77
77
|
} catch (n) {
|
|
78
78
|
console.error("[itube-player] listener error", n);
|
|
79
79
|
}
|
|
@@ -127,18 +127,18 @@ function xt() {
|
|
|
127
127
|
}
|
|
128
128
|
function et(r, t, e = 100) {
|
|
129
129
|
if (r.length === 0 || !Number.isFinite(t) || t <= 0) return [];
|
|
130
|
-
const
|
|
131
|
-
let
|
|
130
|
+
const i = new Array(e).fill(0);
|
|
131
|
+
let s = !1;
|
|
132
132
|
for (const h of r) {
|
|
133
133
|
if (!Number.isFinite(h.time) || h.time < 0 || h.time > t) continue;
|
|
134
134
|
const u = Math.max(0, h.value);
|
|
135
135
|
if (u === 0) continue;
|
|
136
136
|
const f = Math.min(e - 1, Math.floor(h.time / t * e));
|
|
137
|
-
|
|
137
|
+
i[f] += u, s = !0;
|
|
138
138
|
}
|
|
139
|
-
if (!
|
|
140
|
-
const n = [0.06, 0.24, 0.4, 0.24, 0.06], o =
|
|
141
|
-
(h, u) => n.reduce((f, d, b) => f + d * (
|
|
139
|
+
if (!s) return [];
|
|
140
|
+
const n = [0.06, 0.24, 0.4, 0.24, 0.06], o = i.map(
|
|
141
|
+
(h, u) => n.reduce((f, d, b) => f + d * (i[u + b - 2] ?? 0), 0)
|
|
142
142
|
), a = Math.max(...o);
|
|
143
143
|
if (a <= 0) return [];
|
|
144
144
|
const c = 0.08;
|
|
@@ -146,11 +146,11 @@ function et(r, t, e = 100) {
|
|
|
146
146
|
}
|
|
147
147
|
function it(r, t = 1e3, e = 100) {
|
|
148
148
|
if (r.length === 0) return "";
|
|
149
|
-
const
|
|
150
|
-
let
|
|
149
|
+
const i = t / (r.length - 1 || 1);
|
|
150
|
+
let s = `M 0 ${e}`;
|
|
151
151
|
return r.forEach((n, o) => {
|
|
152
|
-
|
|
153
|
-
}),
|
|
152
|
+
s += ` L ${(o * i).toFixed(1)} ${(e - n * e).toFixed(1)}`;
|
|
153
|
+
}), s += ` L ${t} ${e} Z`, s;
|
|
154
154
|
}
|
|
155
155
|
async function st(r, t) {
|
|
156
156
|
if (r.src)
|
|
@@ -164,8 +164,8 @@ async function st(r, t) {
|
|
|
164
164
|
if (!r.vastTag) throw new Error('Ad roll has neither "vastTag" nor "src"');
|
|
165
165
|
return j(r, r.vastTag, t, 0, { impressions: [], tracking: {} });
|
|
166
166
|
}
|
|
167
|
-
async function j(r, t, e,
|
|
168
|
-
if (
|
|
167
|
+
async function j(r, t, e, i, s) {
|
|
168
|
+
if (i > e.maxWrapperDepth) throw new Error("VAST wrapper depth limit exceeded");
|
|
169
169
|
const n = new AbortController(), o = setTimeout(() => n.abort(), e.requestTimeout);
|
|
170
170
|
let a;
|
|
171
171
|
try {
|
|
@@ -179,12 +179,12 @@ async function j(r, t, e, s, i) {
|
|
|
179
179
|
if (c.querySelector("parsererror")) throw new Error("VAST response is not valid XML");
|
|
180
180
|
const h = c.querySelector("VAST > Ad");
|
|
181
181
|
if (!h) throw new Error("VAST response contains no ads");
|
|
182
|
-
nt(h,
|
|
182
|
+
nt(h, s);
|
|
183
183
|
const u = h.querySelector(":scope > Wrapper");
|
|
184
184
|
if (u) {
|
|
185
185
|
const v = k(u.querySelector("VASTAdTagURI"));
|
|
186
186
|
if (!v) throw new Error("VAST wrapper without VASTAdTagURI");
|
|
187
|
-
return j(r, v, e,
|
|
187
|
+
return j(r, v, e, i + 1, s);
|
|
188
188
|
}
|
|
189
189
|
const f = h.querySelector(":scope > InLine");
|
|
190
190
|
if (!f) throw new Error("VAST ad has neither InLine nor Wrapper");
|
|
@@ -202,24 +202,24 @@ async function j(r, t, e, s, i) {
|
|
|
202
202
|
d.getAttribute("skipoffset"),
|
|
203
203
|
V(k(d.querySelector(":scope > Duration")))
|
|
204
204
|
),
|
|
205
|
-
impressions:
|
|
206
|
-
tracking:
|
|
205
|
+
impressions: s.impressions,
|
|
206
|
+
tracking: s.tracking,
|
|
207
207
|
adTitle: k(f.querySelector(":scope > AdTitle")) ?? void 0
|
|
208
208
|
};
|
|
209
209
|
}
|
|
210
210
|
function nt(r, t) {
|
|
211
|
-
var e,
|
|
212
|
-
for (const
|
|
213
|
-
const n = k(
|
|
211
|
+
var e, i;
|
|
212
|
+
for (const s of r.querySelectorAll("Impression")) {
|
|
213
|
+
const n = k(s);
|
|
214
214
|
n && t.impressions.push(n);
|
|
215
215
|
}
|
|
216
|
-
for (const
|
|
217
|
-
const n =
|
|
216
|
+
for (const s of r.querySelectorAll("Linear > TrackingEvents > Tracking")) {
|
|
217
|
+
const n = s.getAttribute("event"), o = k(s);
|
|
218
218
|
!n || !o || ["start", "firstQuartile", "midpoint", "thirdQuartile", "complete", "skip", "pause", "resume"].includes(n) && ((e = t.tracking)[n] ?? (e[n] = [])).push(o);
|
|
219
219
|
}
|
|
220
|
-
for (const
|
|
221
|
-
const n = k(
|
|
222
|
-
n && ((
|
|
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
223
|
}
|
|
224
224
|
}
|
|
225
225
|
function rt(r) {
|
|
@@ -234,7 +234,7 @@ function rt(r) {
|
|
|
234
234
|
);
|
|
235
235
|
if (e.length === 0) return null;
|
|
236
236
|
e.sort((o, a) => o.bitrate - a.bitrate);
|
|
237
|
-
const
|
|
237
|
+
const i = e.filter((o) => o.delivery !== "streaming"), s = i.length > 0 ? i : e, n = s[Math.floor(s.length / 2)];
|
|
238
238
|
return { url: n.url, type: n.type };
|
|
239
239
|
}
|
|
240
240
|
function V(r) {
|
|
@@ -310,7 +310,7 @@ class lt {
|
|
|
310
310
|
/** Called from timeupdate; fires due mid-rolls. */
|
|
311
311
|
checkMidRolls(t) {
|
|
312
312
|
if (this.adPlaying) return;
|
|
313
|
-
const e = this.pending("midRoll").filter((
|
|
313
|
+
const e = this.pending("midRoll").filter((i) => i.timer !== void 0 && t >= i.timer);
|
|
314
314
|
e.length > 0 && this.playRolls(e);
|
|
315
315
|
}
|
|
316
316
|
pending(t) {
|
|
@@ -321,8 +321,8 @@ class lt {
|
|
|
321
321
|
if (this.activeBreak) return this.activeBreak;
|
|
322
322
|
this.ensureAdVideo();
|
|
323
323
|
const e = (async () => {
|
|
324
|
-
const
|
|
325
|
-
|
|
324
|
+
const i = this.host.contentVideo, s = !i.paused && !i.ended;
|
|
325
|
+
i.pause();
|
|
326
326
|
for (const n of t) {
|
|
327
327
|
this.playedRolls.add(n);
|
|
328
328
|
try {
|
|
@@ -334,7 +334,7 @@ class lt {
|
|
|
334
334
|
}
|
|
335
335
|
if (this.destroyed) return;
|
|
336
336
|
}
|
|
337
|
-
(
|
|
337
|
+
(s || t[0].roll === "preRoll") && i.play().catch(() => {
|
|
338
338
|
});
|
|
339
339
|
})();
|
|
340
340
|
return this.activeBreak = e.finally(() => {
|
|
@@ -343,17 +343,17 @@ class lt {
|
|
|
343
343
|
}
|
|
344
344
|
playAd(t) {
|
|
345
345
|
return new Promise((e) => {
|
|
346
|
-
const { labels:
|
|
346
|
+
const { labels: i } = this.host, s = l("div", "imp-ad"), n = this.ensureAdVideo(), o = new AbortController(), a = o.signal;
|
|
347
347
|
n.src = t.mediaUrl, n.muted = this.host.contentVideo.muted, n.volume = this.host.contentVideo.volume;
|
|
348
348
|
const c = l("div", "imp-spinner imp-ad__spinner"), h = l("div", "imp-ad__hud"), u = l("span", "imp-ad__badge");
|
|
349
|
-
u.textContent = t.adTitle ? `${
|
|
349
|
+
u.textContent = t.adTitle ? `${i.adLabel} · ${t.adTitle}` : i.adLabel;
|
|
350
350
|
const f = l("span", "imp-ad__countdown");
|
|
351
351
|
h.append(u, f);
|
|
352
352
|
const d = l("div", "imp-ad__actions");
|
|
353
353
|
let b = null;
|
|
354
|
-
t.clickThrough && (b = l("button", "imp-ad__visit", { type: "button" }), b.textContent =
|
|
354
|
+
t.clickThrough && (b = l("button", "imp-ad__visit", { type: "button" }), b.textContent = i.visitAdvertiser, d.append(b));
|
|
355
355
|
const v = l("button", "imp-ad__skip", { type: "button" });
|
|
356
|
-
v.hidden = !0, d.append(v),
|
|
356
|
+
v.hidden = !0, d.append(v), s.append(n, c, h, d), this.host.container.append(s), this.layer = s;
|
|
357
357
|
const H = t.skipOffset ?? this.opts.skipDelay, N = /* @__PURE__ */ new Set(), L = (g) => {
|
|
358
358
|
N.has(g) || (N.add(g), C(t.tracking[g]));
|
|
359
359
|
};
|
|
@@ -372,7 +372,7 @@ class lt {
|
|
|
372
372
|
error: new Error(`Ad media stalled for ${this.opts.mediaTimeout} ms — skipping`)
|
|
373
373
|
}), A());
|
|
374
374
|
}, 500), A = () => {
|
|
375
|
-
clearInterval(X), o.abort(), n.pause(), n.removeAttribute("src"), n.load(),
|
|
375
|
+
clearInterval(X), o.abort(), n.pause(), n.removeAttribute("src"), n.load(), s.remove(), this.layer = null, e();
|
|
376
376
|
}, O = (g) => {
|
|
377
377
|
g ? (L("skip"), this.host.emitter.emit("adskip", { ad: t })) : L("complete"), this.host.emitter.emit("adend", { ad: t }), A();
|
|
378
378
|
};
|
|
@@ -386,7 +386,7 @@ class lt {
|
|
|
386
386
|
const g = n.currentTime, x = n.duration;
|
|
387
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
388
|
const z = Math.ceil(H - g);
|
|
389
|
-
z > 0 ? (v.hidden = !1, v.disabled = !0, v.textContent = `${
|
|
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
390
|
}
|
|
391
391
|
}, { signal: a }), n.addEventListener("ended", () => O(!1), { signal: a }), n.addEventListener("error", () => {
|
|
392
392
|
this.host.emitter.emit("aderror", {
|
|
@@ -402,14 +402,14 @@ class lt {
|
|
|
402
402
|
n.addEventListener("playing", () => {
|
|
403
403
|
W = !0;
|
|
404
404
|
}, { once: !0, signal: a }), n.addEventListener("pause", () => {
|
|
405
|
-
n.ended || !
|
|
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
406
|
}, { signal: a }), n.addEventListener("play", () => {
|
|
407
|
-
|
|
408
|
-
}, { signal: a }),
|
|
409
|
-
(g.target ===
|
|
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
410
|
});
|
|
411
411
|
}), n.play().catch(() => {
|
|
412
|
-
|
|
412
|
+
s.classList.add("imp-ad--paused");
|
|
413
413
|
});
|
|
414
414
|
});
|
|
415
415
|
}
|
|
@@ -422,20 +422,20 @@ class lt {
|
|
|
422
422
|
}
|
|
423
423
|
}
|
|
424
424
|
function at(r, t) {
|
|
425
|
-
const e = [...r].sort((
|
|
426
|
-
for (let
|
|
427
|
-
const n = Math.max(0, e[
|
|
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
428
|
if (Number.isFinite(t) && n >= t) continue;
|
|
429
|
-
let o = e[
|
|
430
|
-
Number.isFinite(t) && (o = Math.min(o, t)), !(o <= n) &&
|
|
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
431
|
}
|
|
432
|
-
return
|
|
432
|
+
return i;
|
|
433
433
|
}
|
|
434
434
|
async function ht(r) {
|
|
435
435
|
const t = await fetch(r);
|
|
436
436
|
if (!t.ok) throw new Error(`Failed to load chapters VTT (${t.status})`);
|
|
437
437
|
const e = await t.text();
|
|
438
|
-
return U(e).map((
|
|
438
|
+
return U(e).map((i) => ({ start: i.start, end: i.end, title: i.text }));
|
|
439
439
|
}
|
|
440
440
|
function K(r, t) {
|
|
441
441
|
for (const e of r)
|
|
@@ -458,10 +458,10 @@ async function dt() {
|
|
|
458
458
|
}
|
|
459
459
|
return S;
|
|
460
460
|
}
|
|
461
|
-
async function ut(r, t, e,
|
|
461
|
+
async function ut(r, t, e, i) {
|
|
462
462
|
if (ct(t, e)) {
|
|
463
|
-
const
|
|
464
|
-
return n && n.isSupported() ? pt(n, r, t,
|
|
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
465
|
}
|
|
466
466
|
return r.src = t, $(r);
|
|
467
467
|
}
|
|
@@ -477,50 +477,50 @@ function $(r) {
|
|
|
477
477
|
}
|
|
478
478
|
};
|
|
479
479
|
}
|
|
480
|
-
function pt(r, t, e,
|
|
481
|
-
const
|
|
480
|
+
function pt(r, t, e, i) {
|
|
481
|
+
const s = new r({ enableWorker: !0 }), n = {
|
|
482
482
|
kind: "hls",
|
|
483
483
|
levels: [],
|
|
484
484
|
selected: -1,
|
|
485
485
|
setLevel(c) {
|
|
486
|
-
n.selected = c,
|
|
486
|
+
n.selected = c, s.currentLevel = c;
|
|
487
487
|
},
|
|
488
488
|
destroy() {
|
|
489
|
-
|
|
489
|
+
s.destroy(), t.removeAttribute("src"), t.load();
|
|
490
490
|
}
|
|
491
491
|
};
|
|
492
|
-
|
|
493
|
-
n.levels =
|
|
492
|
+
s.on(r.Events.MANIFEST_PARSED, () => {
|
|
493
|
+
n.levels = s.levels.map((c, h) => ({
|
|
494
494
|
index: h,
|
|
495
495
|
label: c.height ? `${c.height}p` : `${Math.round(c.bitrate / 1e3)} kbps`
|
|
496
|
-
})).reverse(),
|
|
497
|
-
}),
|
|
498
|
-
const u =
|
|
499
|
-
u &&
|
|
496
|
+
})).reverse(), i.onLevels(n.levels);
|
|
497
|
+
}), s.on(r.Events.LEVEL_SWITCHED, (c, h) => {
|
|
498
|
+
const u = s.levels[h.level];
|
|
499
|
+
u && i.onLevelSwitch(u.height ? `${u.height}p` : `${Math.round(u.bitrate / 1e3)} kbps`);
|
|
500
500
|
});
|
|
501
501
|
let o = 0, a = !1;
|
|
502
|
-
return
|
|
502
|
+
return s.on(r.Events.ERROR, (c, h) => {
|
|
503
503
|
if (h.fatal)
|
|
504
504
|
switch (h.type) {
|
|
505
505
|
case r.ErrorTypes.NETWORK_ERROR:
|
|
506
|
-
|
|
506
|
+
s.startLoad();
|
|
507
507
|
break;
|
|
508
508
|
case r.ErrorTypes.MEDIA_ERROR:
|
|
509
|
-
o < 3 ? (o++,
|
|
509
|
+
o < 3 ? (o++, s.recoverMediaError()) : (i.onError(`HLS media error: ${h.details}`, h), s.destroy());
|
|
510
510
|
break;
|
|
511
511
|
default:
|
|
512
512
|
if (a)
|
|
513
|
-
|
|
513
|
+
i.onError(`HLS fatal error: ${h.details}`, h), s.destroy();
|
|
514
514
|
else {
|
|
515
515
|
a = !0;
|
|
516
516
|
try {
|
|
517
|
-
|
|
517
|
+
s.loadSource(e), s.startLoad();
|
|
518
518
|
} catch {
|
|
519
|
-
|
|
519
|
+
i.onError(`HLS fatal error: ${h.details}`, h), s.destroy();
|
|
520
520
|
}
|
|
521
521
|
}
|
|
522
522
|
}
|
|
523
|
-
}),
|
|
523
|
+
}), s.loadSource(e), s.attachMedia(t), n;
|
|
524
524
|
}
|
|
525
525
|
class q {
|
|
526
526
|
constructor(t) {
|
|
@@ -529,8 +529,8 @@ class q {
|
|
|
529
529
|
static async load(t) {
|
|
530
530
|
const e = await fetch(t);
|
|
531
531
|
if (!e.ok) throw new Error(`Failed to load thumbnails VTT (${e.status})`);
|
|
532
|
-
const
|
|
533
|
-
for (const n of U(
|
|
532
|
+
const i = await e.text(), s = [];
|
|
533
|
+
for (const n of U(i)) {
|
|
534
534
|
const [o, a] = n.text.trim().split("#");
|
|
535
535
|
if (!o) continue;
|
|
536
536
|
const c = {
|
|
@@ -538,16 +538,16 @@ class q {
|
|
|
538
538
|
end: n.end,
|
|
539
539
|
src: G(o, t)
|
|
540
540
|
}, h = a?.match(/xywh=(\d+),(\d+),(\d+),(\d+)/);
|
|
541
|
-
h && (c.xywh = { x: Number(h[1]), y: Number(h[2]), w: Number(h[3]), h: Number(h[4]) }),
|
|
541
|
+
h && (c.xywh = { x: Number(h[1]), y: Number(h[2]), w: Number(h[3]), h: Number(h[4]) }), s.push(c);
|
|
542
542
|
}
|
|
543
|
-
return
|
|
543
|
+
return s.sort((n, o) => n.start - o.start), new q(s);
|
|
544
544
|
}
|
|
545
545
|
cueAt(t) {
|
|
546
|
-
let e = 0,
|
|
547
|
-
for (; e <=
|
|
548
|
-
const
|
|
549
|
-
if (t < n.start)
|
|
550
|
-
else if (t >= n.end) e =
|
|
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
551
|
else return n;
|
|
552
552
|
}
|
|
553
553
|
return null;
|
|
@@ -569,27 +569,27 @@ class M {
|
|
|
569
569
|
this.root.textContent = "";
|
|
570
570
|
for (const e of t) {
|
|
571
571
|
if (e.items.length === 0) continue;
|
|
572
|
-
const
|
|
572
|
+
const i = l("div", "imp-menu__section");
|
|
573
573
|
if (e.title) {
|
|
574
|
-
const
|
|
575
|
-
|
|
574
|
+
const s = l("div", "imp-menu__title");
|
|
575
|
+
s.textContent = e.title, i.append(s);
|
|
576
576
|
}
|
|
577
|
-
for (const
|
|
577
|
+
for (const s of e.items) {
|
|
578
578
|
const n = l("button", "imp-menu__item", { type: "button", role: "menuitemradio" });
|
|
579
|
-
if (n.setAttribute("aria-checked", String(
|
|
579
|
+
if (n.setAttribute("aria-checked", String(s.active)), s.active && n.classList.add("imp-menu__item--active"), s.icon) {
|
|
580
580
|
const a = l("span", "imp-menu__icon");
|
|
581
|
-
|
|
581
|
+
s.icon.trimStart().startsWith("<svg") ? a.innerHTML = s.icon : a.append(l("img", "", { src: s.icon, alt: "" })), n.append(a);
|
|
582
582
|
}
|
|
583
583
|
const o = l("span", "imp-menu__label");
|
|
584
|
-
o.textContent =
|
|
585
|
-
e.onSelect(
|
|
586
|
-
}),
|
|
584
|
+
o.textContent = s.label, n.append(o), n.addEventListener("click", () => {
|
|
585
|
+
e.onSelect(s.value), this.close();
|
|
586
|
+
}), i.append(n);
|
|
587
587
|
}
|
|
588
|
-
this.root.append(
|
|
588
|
+
this.root.append(i);
|
|
589
589
|
}
|
|
590
590
|
this.root.hidden = !1, this.backdrop.hidden = !1, this.outsideListener = (e) => {
|
|
591
|
-
const
|
|
592
|
-
!this.root.contains(
|
|
591
|
+
const i = e.target;
|
|
592
|
+
!this.root.contains(i) && !this.anchor.contains(i) && this.close();
|
|
593
593
|
}, setTimeout(() => {
|
|
594
594
|
this.outsideListener && document.addEventListener("pointerdown", this.outsideListener);
|
|
595
595
|
}, 0);
|
|
@@ -618,12 +618,12 @@ class mt {
|
|
|
618
618
|
}
|
|
619
619
|
setChapters(t) {
|
|
620
620
|
this.chapters = t, this.track.textContent = "", this.segments = [];
|
|
621
|
-
const e = t.length > 0 ? t : [{ start: 0, end: this.duration || 1, title: "" }],
|
|
622
|
-
let
|
|
621
|
+
const e = t.length > 0 ? t : [{ start: 0, end: this.duration || 1, title: "" }], i = [];
|
|
622
|
+
let s = 0;
|
|
623
623
|
for (const n of e)
|
|
624
|
-
n.start >
|
|
625
|
-
this.duration > 0 &&
|
|
626
|
-
for (const n of
|
|
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
627
|
const o = l("div", "imp-progress__segment");
|
|
628
628
|
o.style.flexGrow = String(Math.max(n.end - n.start, 0.01));
|
|
629
629
|
const a = l("div", "imp-progress__fill");
|
|
@@ -633,16 +633,16 @@ class mt {
|
|
|
633
633
|
setThumbnails(t) {
|
|
634
634
|
this.thumbnails = t;
|
|
635
635
|
}
|
|
636
|
-
update(t, e,
|
|
636
|
+
update(t, e, i) {
|
|
637
637
|
if (e !== this.duration && Number.isFinite(e) && this.setDuration(e), this.scrubbing) return;
|
|
638
638
|
this.render(t);
|
|
639
|
-
const
|
|
640
|
-
this.buffered.style.width = `${
|
|
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
641
|
}
|
|
642
642
|
render(t) {
|
|
643
|
-
for (const { chapter:
|
|
644
|
-
const n =
|
|
645
|
-
|
|
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
646
|
}
|
|
647
647
|
const e = this.duration > 0 ? y(t / this.duration, 0, 1) : 0;
|
|
648
648
|
this.handle.style.left = `${e * 100}%`;
|
|
@@ -665,10 +665,10 @@ class mt {
|
|
|
665
665
|
showTooltip(t) {
|
|
666
666
|
const e = this.timeFromEvent(t);
|
|
667
667
|
this.tooltipTime.textContent = w(e);
|
|
668
|
-
const
|
|
669
|
-
this.tooltipChapter.textContent =
|
|
670
|
-
const
|
|
671
|
-
|
|
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
672
|
const n = this.root.getBoundingClientRect(), o = y(t.clientX - n.left, 0, n.width), a = this.tooltip.offsetWidth / 2;
|
|
673
673
|
this.tooltip.style.left = `${y(o, a, Math.max(a, n.width - a))}px`;
|
|
674
674
|
}
|
|
@@ -679,7 +679,7 @@ class mt {
|
|
|
679
679
|
const _ = class _ {
|
|
680
680
|
constructor(t) {
|
|
681
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:
|
|
682
|
+
const { labels: e, icons: i } = t, s = t.controlsOptions;
|
|
683
683
|
this.root = l("div", "imp-controls"), this.progress = new mt({
|
|
684
684
|
onSeek: (d) => t.seek(d),
|
|
685
685
|
onScrubStart: () => {
|
|
@@ -688,17 +688,17 @@ const _ = class _ {
|
|
|
688
688
|
onScrubEnd: () => {
|
|
689
689
|
this.wasPlayingBeforeScrub && t.play();
|
|
690
690
|
}
|
|
691
|
-
}),
|
|
691
|
+
}), s.progress && this.root.append(this.progress.root);
|
|
692
692
|
const n = l("div", "imp-controls__row");
|
|
693
693
|
this.row = n, this.root.append(n);
|
|
694
694
|
const o = l("div", "imp-controls__group"), a = l("div", "imp-controls__group");
|
|
695
695
|
this.rightGroup = a;
|
|
696
696
|
const c = l("div", "imp-controls__spacer");
|
|
697
697
|
this.chapterLabel = l("div", "imp-controls__chapter"), n.append(o, c, a), c.append(this.chapterLabel);
|
|
698
|
-
const h = typeof
|
|
699
|
-
if (
|
|
698
|
+
const h = typeof s.seekButtons == "object" ? s.seekButtons : {}, u = h.back ?? t.seekStep, f = h.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
700
|
const d = l("div", "imp-volume");
|
|
701
|
-
this.muteBtn = m("imp-btn--mute", e.mute,
|
|
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
702
|
type: "range",
|
|
703
703
|
min: "0",
|
|
704
704
|
max: "1",
|
|
@@ -708,8 +708,8 @@ const _ = class _ {
|
|
|
708
708
|
t.setVolume(Number(this.volumeSlider.value));
|
|
709
709
|
}), d.append(this.muteBtn, this.volumeSlider), o.append(d);
|
|
710
710
|
}
|
|
711
|
-
if (
|
|
712
|
-
this.subtitlesBtn = m("imp-btn--subtitles", e.subtitles,
|
|
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
713
|
const d = l("div", "imp-controls__menu-anchor");
|
|
714
714
|
d.append(this.subtitlesBtn, this.subtitlesMenu.root), this.subtitlesBtn.addEventListener("click", () => this.toggleSubtitlesMenu()), a.append(d), this.registerCollapsible({
|
|
715
715
|
key: "subtitles",
|
|
@@ -719,8 +719,8 @@ const _ = class _ {
|
|
|
719
719
|
section: () => this.subtitlesSection()
|
|
720
720
|
});
|
|
721
721
|
}
|
|
722
|
-
if (
|
|
723
|
-
this.settingsBtn = m("imp-btn--settings", e.settings,
|
|
722
|
+
if (s.settings) {
|
|
723
|
+
this.settingsBtn = m("imp-btn--settings", e.settings, i.speed), this.settingsMenu = new M(this.settingsBtn);
|
|
724
724
|
const d = l("div", "imp-controls__menu-anchor");
|
|
725
725
|
d.append(this.settingsBtn, this.settingsMenu.root), this.settingsBtn.addEventListener("click", () => this.toggleSettingsMenu()), a.append(d), this.registerCollapsible({
|
|
726
726
|
key: "settings",
|
|
@@ -730,8 +730,8 @@ const _ = class _ {
|
|
|
730
730
|
section: () => this.speedSection()
|
|
731
731
|
});
|
|
732
732
|
}
|
|
733
|
-
if (
|
|
734
|
-
this.qualityBtn = m("imp-btn--quality", e.quality,
|
|
733
|
+
if (s.quality) {
|
|
734
|
+
this.qualityBtn = m("imp-btn--quality", e.quality, i.settings), this.qualityMenu = new M(this.qualityBtn);
|
|
735
735
|
const d = l("div", "imp-controls__menu-anchor");
|
|
736
736
|
d.append(this.qualityBtn, this.qualityMenu.root), this.qualityBtn.addEventListener("click", () => this.toggleQualityMenu()), a.append(d), this.registerCollapsible({
|
|
737
737
|
key: "quality",
|
|
@@ -741,44 +741,44 @@ const _ = class _ {
|
|
|
741
741
|
section: () => this.qualitySection()
|
|
742
742
|
});
|
|
743
743
|
}
|
|
744
|
-
if (
|
|
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
745
|
key: "scenes",
|
|
746
746
|
el: this.scenesBtn,
|
|
747
747
|
priority: 25,
|
|
748
748
|
available: () => t.chapterList.length > 0,
|
|
749
|
-
section: () => this.simpleSection("scenes", e.scenes,
|
|
750
|
-
})),
|
|
751
|
-
const d = m("imp-btn--list", e.playlist,
|
|
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
752
|
d.addEventListener("click", () => t.togglePlaylistPanel()), a.append(d), this.registerCollapsible({
|
|
753
753
|
key: "list",
|
|
754
754
|
el: d,
|
|
755
755
|
priority: 50,
|
|
756
756
|
available: () => !0,
|
|
757
|
-
section: () => this.simpleSection("list", e.playlist,
|
|
757
|
+
section: () => this.simpleSection("list", e.playlist, i.list, () => t.togglePlaylistPanel())
|
|
758
758
|
});
|
|
759
759
|
}
|
|
760
|
-
if (
|
|
761
|
-
const d = m("imp-btn--pip", e.pip,
|
|
760
|
+
if (s.pip && "requestPictureInPicture" in HTMLVideoElement.prototype) {
|
|
761
|
+
const d = m("imp-btn--pip", e.pip, i.pip);
|
|
762
762
|
d.addEventListener("click", () => void t.togglePip()), a.append(d), this.registerCollapsible({
|
|
763
763
|
key: "pip",
|
|
764
764
|
el: d,
|
|
765
765
|
priority: 20,
|
|
766
766
|
available: () => !0,
|
|
767
|
-
section: () => this.simpleSection("pip", e.pip,
|
|
767
|
+
section: () => this.simpleSection("pip", e.pip, i.pip, () => void t.togglePip())
|
|
768
768
|
});
|
|
769
769
|
}
|
|
770
|
-
if (
|
|
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
771
|
key: "prev",
|
|
772
772
|
el: this.prevBtn,
|
|
773
773
|
priority: 70,
|
|
774
774
|
available: () => !0,
|
|
775
|
-
section: () => t.hasPrevious ? this.simpleSection("prev", e.previous,
|
|
775
|
+
section: () => t.hasPrevious ? this.simpleSection("prev", e.previous, i.previous, () => t.previous()) : null
|
|
776
776
|
}), this.nextBtn && this.registerCollapsible({
|
|
777
777
|
key: "next",
|
|
778
778
|
el: this.nextBtn,
|
|
779
779
|
priority: 72,
|
|
780
780
|
available: () => !0,
|
|
781
|
-
section: () => t.hasNext ? this.simpleSection("next", e.next,
|
|
781
|
+
section: () => t.hasNext ? this.simpleSection("next", e.next, i.next, () => t.next()) : null
|
|
782
782
|
}), this.seekBackBtn) {
|
|
783
783
|
const d = this.seekBackBtn;
|
|
784
784
|
this.registerCollapsible({
|
|
@@ -786,7 +786,7 @@ const _ = class _ {
|
|
|
786
786
|
el: d,
|
|
787
787
|
priority: 80,
|
|
788
788
|
available: () => Number.isFinite(t.duration) && t.duration > 0,
|
|
789
|
-
section: () => this.simpleSection("seekBack", `${e.seekBack} ${u}s`,
|
|
789
|
+
section: () => this.simpleSection("seekBack", `${e.seekBack} ${u}s`, i.seekBack, () => t.skip(-u))
|
|
790
790
|
});
|
|
791
791
|
}
|
|
792
792
|
if (this.seekFwdBtn) {
|
|
@@ -796,64 +796,64 @@ const _ = class _ {
|
|
|
796
796
|
el: d,
|
|
797
797
|
priority: 82,
|
|
798
798
|
available: () => Number.isFinite(t.duration) && t.duration > 0,
|
|
799
|
-
section: () => this.simpleSection("seekFwd", `${e.seekForward} ${f}s`,
|
|
799
|
+
section: () => this.simpleSection("seekFwd", `${e.seekForward} ${f}s`, i.seekForward, () => t.skip(f))
|
|
800
800
|
});
|
|
801
801
|
}
|
|
802
|
-
if (this.center = l("div", "imp-center-controls"),
|
|
803
|
-
const d = this.makeCenterButton("seek-back", `${e.seekBack} ${u}s`,
|
|
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
804
|
d.addEventListener("click", () => t.skip(-u)), this.center.append(d);
|
|
805
805
|
}
|
|
806
|
-
if (
|
|
807
|
-
const d = this.makeCenterButton("seek-forward", `${e.seekForward} ${f}s`,
|
|
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
808
|
d.addEventListener("click", () => t.skip(f)), this.center.append(d);
|
|
809
809
|
}
|
|
810
|
-
|
|
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
811
|
}
|
|
812
812
|
registerCollapsible(t) {
|
|
813
813
|
this.collapsibles.push(t);
|
|
814
814
|
}
|
|
815
815
|
/** Center-cluster button — deliberately NOT `.imp-btn` (own sizing/look). */
|
|
816
|
-
makeCenterButton(t, e,
|
|
817
|
-
const
|
|
818
|
-
return
|
|
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
819
|
}
|
|
820
820
|
/** like/dislike — visible buttons (collapsible), highlightable via `setLikeState`. */
|
|
821
821
|
buildLikeDislike(t) {
|
|
822
|
-
const e = this.player,
|
|
823
|
-
if (
|
|
824
|
-
this.likeBtn = m("imp-btn--like",
|
|
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
825
|
const o = this.wrapWithTooltip(this.likeBtn, this.likeCountEl);
|
|
826
826
|
t.append(o), this.registerCollapsible({
|
|
827
827
|
key: "like",
|
|
828
828
|
el: o,
|
|
829
829
|
priority: 62,
|
|
830
830
|
available: () => !0,
|
|
831
|
-
section: () => this.simpleSection("like",
|
|
831
|
+
section: () => this.simpleSection("like", s.like, n.like, () => e.emit("action", { id: "like" }))
|
|
832
832
|
});
|
|
833
833
|
}
|
|
834
|
-
if (
|
|
835
|
-
this.dislikeBtn = m("imp-btn--dislike",
|
|
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
836
|
const o = this.wrapWithTooltip(this.dislikeBtn, this.dislikeCountEl);
|
|
837
837
|
t.append(o), this.registerCollapsible({
|
|
838
838
|
key: "dislike",
|
|
839
839
|
el: o,
|
|
840
840
|
priority: 60,
|
|
841
841
|
available: () => !0,
|
|
842
|
-
section: () => this.simpleSection("dislike",
|
|
842
|
+
section: () => this.simpleSection("dislike", s.dislike, n.dislike, () => e.emit("action", { id: "dislike" }))
|
|
843
843
|
});
|
|
844
844
|
}
|
|
845
845
|
}
|
|
846
846
|
attachCountTooltip(t, e) {
|
|
847
|
-
const
|
|
848
|
-
return this.setCountText(
|
|
847
|
+
const i = l("span", "imp-count-tooltip");
|
|
848
|
+
return this.setCountText(i, e), i;
|
|
849
849
|
}
|
|
850
850
|
wrapWithTooltip(t, e) {
|
|
851
|
-
const
|
|
852
|
-
return
|
|
851
|
+
const i = l("div", "imp-action");
|
|
852
|
+
return i.append(t, e), i;
|
|
853
853
|
}
|
|
854
854
|
setCountText(t, e) {
|
|
855
|
-
const
|
|
856
|
-
t.textContent =
|
|
855
|
+
const i = e == null || e === "" ? "" : String(e);
|
|
856
|
+
t.textContent = i, t.hidden = i === "";
|
|
857
857
|
}
|
|
858
858
|
setLikeCounts(t, e) {
|
|
859
859
|
this.likeCountEl && t !== void 0 && this.setCountText(this.likeCountEl, t), this.dislikeCountEl && e !== void 0 && this.setCountText(this.dislikeCountEl, e);
|
|
@@ -863,11 +863,11 @@ const _ = class _ {
|
|
|
863
863
|
}
|
|
864
864
|
// === ⋯ menu ===========================================================
|
|
865
865
|
buildMoreDropdown(t) {
|
|
866
|
-
const e = this.player,
|
|
867
|
-
|
|
868
|
-
for (const c of
|
|
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 c of i.custom ?? [])
|
|
869
869
|
this.actionItems.push({ value: `custom:${c.id}`, label: c.title, icon: c.icon, run: () => e.emit("customaction", { id: c.id }) });
|
|
870
|
-
const o = m("imp-btn--more",
|
|
870
|
+
const o = m("imp-btn--more", s.more, n.more);
|
|
871
871
|
this.moreMenu = new M(o);
|
|
872
872
|
const a = l("div", "imp-controls__menu-anchor imp-controls__more");
|
|
873
873
|
a.append(o, this.moreMenu.root), o.addEventListener("click", () => {
|
|
@@ -876,17 +876,17 @@ const _ = class _ {
|
|
|
876
876
|
}
|
|
877
877
|
/** ⋯ menu = overflowed controls (in display order) + consumer actions. */
|
|
878
878
|
buildMoreSections() {
|
|
879
|
-
const t = [], e = /* @__PURE__ */ new Map(),
|
|
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
880
|
for (const o of n) {
|
|
881
881
|
const a = o.section();
|
|
882
882
|
if (a)
|
|
883
883
|
if (!a.title && a.items.length === 1) {
|
|
884
884
|
const c = a.items[0];
|
|
885
|
-
|
|
885
|
+
i.push(c), e.set(c.value, () => a.onSelect(c.value));
|
|
886
886
|
} else
|
|
887
887
|
t.push(a);
|
|
888
888
|
}
|
|
889
|
-
if (
|
|
889
|
+
if (i.length > 0 && t.unshift({ title: "", items: i, onSelect: (o) => e.get(o)?.() }), this.actionItems.length > 0) {
|
|
890
890
|
const o = new Map(this.actionItems.map((a) => [a.value, a.run]));
|
|
891
891
|
t.push({
|
|
892
892
|
title: "",
|
|
@@ -896,8 +896,8 @@ const _ = class _ {
|
|
|
896
896
|
}
|
|
897
897
|
return t;
|
|
898
898
|
}
|
|
899
|
-
simpleSection(t, e,
|
|
900
|
-
return { title: "", items: [{ value: t, label: e, icon:
|
|
899
|
+
simpleSection(t, e, i, s) {
|
|
900
|
+
return { title: "", items: [{ value: t, label: e, icon: i, active: !1 }], onSelect: () => s() };
|
|
901
901
|
}
|
|
902
902
|
speedSection() {
|
|
903
903
|
const t = this.player;
|
|
@@ -928,7 +928,7 @@ const _ = class _ {
|
|
|
928
928
|
title: t.labels.subtitles,
|
|
929
929
|
items: [
|
|
930
930
|
{ label: t.labels.subtitlesOff, value: "-1", active: t.activeSubtitle === -1 },
|
|
931
|
-
...t.subtitleTracks.map((e,
|
|
931
|
+
...t.subtitleTracks.map((e, i) => ({ label: e.label, value: String(i), active: t.activeSubtitle === i }))
|
|
932
932
|
],
|
|
933
933
|
onSelect: (e) => t.setSubtitle(Number(e))
|
|
934
934
|
};
|
|
@@ -946,24 +946,24 @@ const _ = class _ {
|
|
|
946
946
|
reflow() {
|
|
947
947
|
const t = window.innerWidth <= 767;
|
|
948
948
|
this.center.style.display = t ? "flex" : "none", this.playBtn && (this.playBtn.style.display = t ? "none" : ""), this.overflowed.clear();
|
|
949
|
-
for (const
|
|
950
|
-
if (t && _.CENTER_KEYS.has(
|
|
951
|
-
|
|
949
|
+
for (const s of this.collapsibles) {
|
|
950
|
+
if (t && _.CENTER_KEYS.has(s.key)) {
|
|
951
|
+
s.el.style.display = "none";
|
|
952
952
|
continue;
|
|
953
953
|
}
|
|
954
|
-
const n =
|
|
955
|
-
|
|
954
|
+
const n = s.available();
|
|
955
|
+
s.el.style.display = n ? "" : "none", s.el.classList.remove("imp-collapsed");
|
|
956
956
|
}
|
|
957
|
-
const e = this.collapsibles.filter((
|
|
958
|
-
let
|
|
959
|
-
for (;
|
|
960
|
-
const
|
|
961
|
-
if (!
|
|
962
|
-
|
|
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
963
|
}
|
|
964
964
|
if (this.moreWrap) {
|
|
965
|
-
const
|
|
966
|
-
this.moreWrap.style.display =
|
|
965
|
+
const s = this.actionItems.length > 0 || this.overflowed.size > 0;
|
|
966
|
+
this.moreWrap.style.display = s ? "" : "none";
|
|
967
967
|
}
|
|
968
968
|
}
|
|
969
969
|
overflowsRow() {
|
|
@@ -973,8 +973,8 @@ const _ = class _ {
|
|
|
973
973
|
bind() {
|
|
974
974
|
const t = this.player;
|
|
975
975
|
this.disposers.push(
|
|
976
|
-
t.on("timeupdate", ({ currentTime: e, duration:
|
|
977
|
-
this.progress.update(e,
|
|
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
978
|
}),
|
|
979
979
|
t.on("play", () => this.syncPlayState()),
|
|
980
980
|
t.on("pause", () => this.syncPlayState()),
|
|
@@ -995,13 +995,13 @@ const _ = class _ {
|
|
|
995
995
|
), this.syncPlaylistButtons(), this.reflow();
|
|
996
996
|
}
|
|
997
997
|
syncPlayState() {
|
|
998
|
-
const t = this.player, [e,
|
|
999
|
-
this.playBtn && E(this.playBtn, e,
|
|
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
1000
|
}
|
|
1001
1001
|
syncVolume() {
|
|
1002
1002
|
if (!this.muteBtn) return;
|
|
1003
|
-
const t = this.player, e = t.muted ? 0 : t.volume,
|
|
1004
|
-
E(this.muteBtn,
|
|
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
1005
|
}
|
|
1006
1006
|
/** Called by the player when per-source data (chapters/quality/subtitles) changes. */
|
|
1007
1007
|
syncFeatureButtons() {
|
|
@@ -1035,8 +1035,8 @@ class ft {
|
|
|
1035
1035
|
constructor(t) {
|
|
1036
1036
|
this.root = l("div", "imp-poster"), this.image = l("img", "imp-poster__image", { alt: "", decoding: "async" }), this.image.hidden = !0;
|
|
1037
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", (
|
|
1039
|
-
(
|
|
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
1040
|
});
|
|
1041
1041
|
}
|
|
1042
1042
|
setSource(t) {
|
|
@@ -1055,7 +1055,7 @@ class vt {
|
|
|
1055
1055
|
this.channelUrl && window.open(this.channelUrl, "_blank", "noopener");
|
|
1056
1056
|
});
|
|
1057
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", (
|
|
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
1059
|
}
|
|
1060
1060
|
setCustomContent(t) {
|
|
1061
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);
|
|
@@ -1068,8 +1068,8 @@ class vt {
|
|
|
1068
1068
|
this.title.textContent = t?.title ?? "", this.description.textContent = t?.description ?? "";
|
|
1069
1069
|
const e = t?.sponsor;
|
|
1070
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
|
|
1072
|
-
this.channel.hidden = !
|
|
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
1073
|
}
|
|
1074
1074
|
show() {
|
|
1075
1075
|
this.root.hidden = !1;
|
|
@@ -1092,13 +1092,13 @@ class gt {
|
|
|
1092
1092
|
}
|
|
1093
1093
|
}
|
|
1094
1094
|
buildCard(t) {
|
|
1095
|
-
const e = l("button", "imp-related__card", { type: "button" }),
|
|
1096
|
-
if (t.poster && (
|
|
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
1097
|
const n = l("span", "imp-related__duration");
|
|
1098
|
-
n.textContent = t.duration,
|
|
1098
|
+
n.textContent = t.duration, i.append(n);
|
|
1099
1099
|
}
|
|
1100
|
-
const
|
|
1101
|
-
return
|
|
1100
|
+
const s = l("div", "imp-related__card-title");
|
|
1101
|
+
return s.textContent = t.title, e.append(i, s), e.addEventListener("click", () => {
|
|
1102
1102
|
this.player.emit("relatedclick", { item: t }), this.hide(), this.activate(t);
|
|
1103
1103
|
}), e;
|
|
1104
1104
|
}
|
|
@@ -1128,14 +1128,14 @@ class gt {
|
|
|
1128
1128
|
}
|
|
1129
1129
|
}
|
|
1130
1130
|
class yt {
|
|
1131
|
-
constructor(t, e = "sidebar",
|
|
1131
|
+
constructor(t, e = "sidebar", i) {
|
|
1132
1132
|
this.player = t, this.root = l("div", `imp-playlist imp-playlist--${e}`), this.root.hidden = !0;
|
|
1133
|
-
const
|
|
1134
|
-
if (
|
|
1133
|
+
const s = l("div", "imp-playlist__header"), n = l("div", "imp-playlist__heading");
|
|
1134
|
+
if (i) {
|
|
1135
1135
|
const c = l("div", "imp-playlist__kicker");
|
|
1136
1136
|
c.textContent = t.labels.playlist;
|
|
1137
1137
|
const h = l("div", "imp-playlist__name");
|
|
1138
|
-
h.textContent =
|
|
1138
|
+
h.textContent = i, n.append(c, h);
|
|
1139
1139
|
} else
|
|
1140
1140
|
n.textContent = t.labels.playlist;
|
|
1141
1141
|
const o = l("div", "imp-playlist__tools");
|
|
@@ -1145,25 +1145,25 @@ class yt {
|
|
|
1145
1145
|
t.setRepeat(!t.repeat), this.syncModes();
|
|
1146
1146
|
});
|
|
1147
1147
|
const a = m("imp-playlist__close", "Close", t.icons.close);
|
|
1148
|
-
a.addEventListener("click", () => this.hide()), o.append(this.shuffleBtn, this.repeatBtn, a),
|
|
1148
|
+
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();
|
|
1149
1149
|
}
|
|
1150
1150
|
syncModes() {
|
|
1151
1151
|
this.shuffleBtn.classList.toggle("imp-btn--active", this.player.shuffle), this.repeatBtn.classList.toggle("imp-btn--active", this.player.repeat);
|
|
1152
1152
|
}
|
|
1153
1153
|
rebuild() {
|
|
1154
1154
|
this.list.textContent = "", this.player.playlist.forEach((t, e) => {
|
|
1155
|
-
const
|
|
1156
|
-
e === this.player.index &&
|
|
1157
|
-
const
|
|
1158
|
-
t.poster && (
|
|
1155
|
+
const i = l("button", "imp-playlist__item", { type: "button" });
|
|
1156
|
+
e === this.player.index && i.classList.add("imp-playlist__item--active");
|
|
1157
|
+
const s = l("div", "imp-playlist__thumb");
|
|
1158
|
+
t.poster && (s.style.backgroundImage = `url("${t.poster}")`);
|
|
1159
1159
|
const n = l("div", "imp-playlist__meta"), o = l("div", "imp-playlist__title");
|
|
1160
1160
|
if (o.textContent = t.title ?? `#${e + 1}`, n.append(o), t.duration) {
|
|
1161
1161
|
const a = l("div", "imp-playlist__duration");
|
|
1162
1162
|
a.textContent = w(t.duration), n.append(a);
|
|
1163
1163
|
}
|
|
1164
|
-
|
|
1164
|
+
i.append(s, n), i.addEventListener("click", () => {
|
|
1165
1165
|
this.player.playItem(e);
|
|
1166
|
-
}), this.list.append(
|
|
1166
|
+
}), this.list.append(i);
|
|
1167
1167
|
});
|
|
1168
1168
|
}
|
|
1169
1169
|
get visible() {
|
|
@@ -1182,34 +1182,34 @@ class yt {
|
|
|
1182
1182
|
class bt {
|
|
1183
1183
|
constructor(t, e = "bottom") {
|
|
1184
1184
|
this.player = t, this.items = [], this.root = l("div", `imp-playlist imp-scenes imp-playlist--${e}`), this.root.hidden = !0;
|
|
1185
|
-
const
|
|
1186
|
-
|
|
1185
|
+
const i = l("div", "imp-playlist__header"), s = l("span");
|
|
1186
|
+
s.textContent = t.labels.scenes;
|
|
1187
1187
|
const n = m("imp-playlist__close", "Close", t.icons.close);
|
|
1188
|
-
n.addEventListener("click", () => this.hide()),
|
|
1188
|
+
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());
|
|
1189
1189
|
}
|
|
1190
1190
|
/** Re-render from the player's current chapters + thumbnail track. */
|
|
1191
1191
|
rebuild() {
|
|
1192
1192
|
this.list.textContent = "", this.items = [];
|
|
1193
1193
|
const t = this.player.thumbnails;
|
|
1194
1194
|
for (const e of this.player.chapterList) {
|
|
1195
|
-
const
|
|
1195
|
+
const i = l("button", "imp-playlist__item", { type: "button" }), s = l("div", "imp-playlist__thumb"), n = t?.cueAt(e.start + 0.01) ?? null;
|
|
1196
1196
|
if (n?.xywh) {
|
|
1197
1197
|
const h = l("div", "imp-scenes__sprite");
|
|
1198
|
-
h.style.width = `${n.xywh.w}px`, h.style.height = `${n.xywh.h}px`, h.style.backgroundImage = `url("${n.src}")`, h.style.backgroundPosition = `-${n.xywh.x}px -${n.xywh.y}px`, h.style.setProperty("--imp-sprite-w", String(n.xywh.w)),
|
|
1199
|
-
} else n && (
|
|
1198
|
+
h.style.width = `${n.xywh.w}px`, h.style.height = `${n.xywh.h}px`, h.style.backgroundImage = `url("${n.src}")`, h.style.backgroundPosition = `-${n.xywh.x}px -${n.xywh.y}px`, h.style.setProperty("--imp-sprite-w", String(n.xywh.w)), s.append(h);
|
|
1199
|
+
} else n && (s.style.backgroundImage = `url("${n.src}")`);
|
|
1200
1200
|
const o = l("div", "imp-playlist__meta"), a = l("div", "imp-playlist__title");
|
|
1201
1201
|
a.textContent = e.title || w(e.start);
|
|
1202
1202
|
const c = l("div", "imp-playlist__duration");
|
|
1203
|
-
c.textContent = w(e.start), o.append(a, c),
|
|
1203
|
+
c.textContent = w(e.start), o.append(a, c), i.append(s, o), i.addEventListener("click", () => {
|
|
1204
1204
|
this.player.seek(e.start), this.player.play();
|
|
1205
|
-
}), this.list.append(
|
|
1205
|
+
}), this.list.append(i), this.items.push(i);
|
|
1206
1206
|
}
|
|
1207
1207
|
this.syncActive();
|
|
1208
1208
|
}
|
|
1209
1209
|
syncActive() {
|
|
1210
1210
|
const t = this.player.chapter, e = this.player.chapterList;
|
|
1211
|
-
this.items.forEach((
|
|
1212
|
-
|
|
1211
|
+
this.items.forEach((i, s) => {
|
|
1212
|
+
i.classList.toggle("imp-playlist__item--active", t !== null && e[s] === t);
|
|
1213
1213
|
});
|
|
1214
1214
|
}
|
|
1215
1215
|
get visible() {
|
|
@@ -1247,15 +1247,15 @@ const kt = {
|
|
|
1247
1247
|
class _t {
|
|
1248
1248
|
constructor(t, e = {}) {
|
|
1249
1249
|
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.decodeRecoveries = 0, this.adManager = null, this.playedOnce = !1, this.idleTimer = null, this.destroyed = !1;
|
|
1250
|
-
const
|
|
1251
|
-
if (!
|
|
1252
|
-
this.mount =
|
|
1253
|
-
const
|
|
1254
|
-
if (
|
|
1255
|
-
const h = typeof
|
|
1250
|
+
const i = typeof t == "string" ? document.querySelector(t) : t;
|
|
1251
|
+
if (!i) throw new Error(`[itube-player] mount target not found: ${String(t)}`);
|
|
1252
|
+
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);
|
|
1253
|
+
const s = e.styling;
|
|
1254
|
+
if (s?.themeColor && this.container.style.setProperty("--imp-accent", s.themeColor), s?.likeColor && this.container.style.setProperty("--imp-like", s.likeColor), s?.dislikeColor && this.container.style.setProperty("--imp-dislike", s.dislikeColor), s?.borderRadius !== void 0) {
|
|
1255
|
+
const h = typeof s.borderRadius == "number" ? `${s.borderRadius}px` : s.borderRadius;
|
|
1256
1256
|
this.container.style.setProperty("--imp-radius", h);
|
|
1257
1257
|
}
|
|
1258
|
-
|
|
1258
|
+
s?.playButtonStyle === "inverted" && this.container.classList.add("imp-player--play-inverted"), 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);
|
|
1259
1259
|
const n = l("div", "imp-layer");
|
|
1260
1260
|
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");
|
|
1261
1261
|
const o = l("div", "imp-layer__middle");
|
|
@@ -1268,8 +1268,14 @@ class _t {
|
|
|
1268
1268
|
c
|
|
1269
1269
|
)), this.emitter.on("error", ({ message: h }) => {
|
|
1270
1270
|
this.errorBox.textContent = h, this.errorBox.hidden = !1, this.spinner.hidden = !0, this.poster.hide();
|
|
1271
|
-
}), this.bindVideoEvents(), this.bindIdleHide(), e.keyboard !== !1 && this.bindKeyboard(), this.video.addEventListener("
|
|
1272
|
-
|
|
1271
|
+
}), this.bindVideoEvents(), this.bindIdleHide(), e.keyboard !== !1 && this.bindKeyboard(), this.video.addEventListener("pointerup", (h) => {
|
|
1272
|
+
if (!(this.adPlaying || !this.playedOnce)) {
|
|
1273
|
+
if (h.pointerType === "mouse") {
|
|
1274
|
+
this.togglePlay();
|
|
1275
|
+
return;
|
|
1276
|
+
}
|
|
1277
|
+
this.container.classList.contains("imp-player--idle") ? (this.showControlsNow(), this.scheduleIdle()) : this.video.paused || this.container.classList.add("imp-player--idle");
|
|
1278
|
+
}
|
|
1273
1279
|
}, { signal: this.abort.signal }), this.pauseScreen.root.addEventListener("click", (h) => {
|
|
1274
1280
|
this.pauseScreen.hasCustom || h.target === this.pauseScreen.root && this.togglePlay();
|
|
1275
1281
|
}, { 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 });
|
|
@@ -1353,7 +1359,7 @@ class _t {
|
|
|
1353
1359
|
get qualityLevels() {
|
|
1354
1360
|
if (this.sourceController?.kind === "hls") return this.sourceController.levels;
|
|
1355
1361
|
const t = this.source?.qualities;
|
|
1356
|
-
return t ? t.map((e,
|
|
1362
|
+
return t ? t.map((e, i) => ({ index: i, label: e.label ?? String(e.quality ?? i) })) : [];
|
|
1357
1363
|
}
|
|
1358
1364
|
get qualityAutoAvailable() {
|
|
1359
1365
|
return this.sourceController?.kind === "hls";
|
|
@@ -1370,8 +1376,8 @@ class _t {
|
|
|
1370
1376
|
}
|
|
1371
1377
|
const e = this.source?.qualities;
|
|
1372
1378
|
if (!e || !e[t] || t === this.progressiveQuality) return;
|
|
1373
|
-
const
|
|
1374
|
-
this.progressiveQuality = t, this.video.src = e[t].src, this.video.currentTime =
|
|
1379
|
+
const i = this.video.currentTime, s = !this.video.paused && !this.video.ended;
|
|
1380
|
+
this.progressiveQuality = t, this.video.src = e[t].src, this.video.currentTime = i, s && this.video.play().catch(() => {
|
|
1375
1381
|
}), this.emitter.emit("qualitychange", { label: e[t].label ?? String(e[t].quality ?? t) });
|
|
1376
1382
|
}
|
|
1377
1383
|
// === subtitles ========================================================
|
|
@@ -1386,8 +1392,8 @@ class _t {
|
|
|
1386
1392
|
}
|
|
1387
1393
|
setSubtitle(t) {
|
|
1388
1394
|
const e = this.video.textTracks;
|
|
1389
|
-
for (let
|
|
1390
|
-
e[
|
|
1395
|
+
for (let i = 0; i < e.length; i++)
|
|
1396
|
+
e[i].mode = i === t ? "showing" : "disabled";
|
|
1391
1397
|
this.emitter.emit("subtitlechange", { track: this.subtitleTracks[t] ?? null });
|
|
1392
1398
|
}
|
|
1393
1399
|
// === playlist =========================================================
|
|
@@ -1517,12 +1523,12 @@ class _t {
|
|
|
1517
1523
|
}
|
|
1518
1524
|
// === internals ========================================================
|
|
1519
1525
|
async loadItem(t, e) {
|
|
1520
|
-
const
|
|
1521
|
-
if (!
|
|
1526
|
+
const i = ++this.loadToken, s = this.sources[t];
|
|
1527
|
+
if (!s) return;
|
|
1522
1528
|
this.currentIndex = t, this.sourceController?.destroy(), this.sourceController = null, this.chapters = [], this.currentChapter = null, this.progressiveQuality = -1, this.playedOnce = !1;
|
|
1523
1529
|
for (const c of [...this.video.querySelectorAll("track")]) c.remove();
|
|
1524
|
-
this.adManager?.resetForNewSource(), this.related.hide(), this.pauseScreen.hide(), this.scenesPanel.hide(), this.thumbTrack = null, this.errorBox.hidden = !0, this.decodeRecoveries = 0, this.video.poster =
|
|
1525
|
-
for (const c of Q(
|
|
1530
|
+
this.adManager?.resetForNewSource(), this.related.hide(), this.pauseScreen.hide(), this.scenesPanel.hide(), this.thumbTrack = null, this.errorBox.hidden = !0, this.decodeRecoveries = 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();
|
|
1531
|
+
for (const c of Q(s.subtitles)) {
|
|
1526
1532
|
const h = l("track", "", {
|
|
1527
1533
|
kind: "subtitles",
|
|
1528
1534
|
src: c.src,
|
|
@@ -1531,56 +1537,56 @@ class _t {
|
|
|
1531
1537
|
});
|
|
1532
1538
|
c.default && h.setAttribute("default", ""), this.video.append(h);
|
|
1533
1539
|
}
|
|
1534
|
-
let n =
|
|
1535
|
-
if (
|
|
1536
|
-
const c = Math.max(0,
|
|
1537
|
-
this.progressiveQuality = c, n =
|
|
1540
|
+
let n = s.src, o = s.type;
|
|
1541
|
+
if (s.qualities && s.qualities.length > 0) {
|
|
1542
|
+
const c = Math.max(0, s.qualities.findIndex((h) => h.src === s.src));
|
|
1543
|
+
this.progressiveQuality = c, n = s.qualities[c].src, o = s.qualities[c].type ?? o;
|
|
1538
1544
|
}
|
|
1539
1545
|
this.video.preload = this.adManager?.hasPendingPreRoll ? "auto" : "metadata";
|
|
1540
1546
|
const a = await ut(this.video, n, o, {
|
|
1541
1547
|
onLevels: () => {
|
|
1542
|
-
|
|
1548
|
+
i === this.loadToken && this.controls.syncFeatureButtons();
|
|
1543
1549
|
},
|
|
1544
1550
|
onLevelSwitch: (c) => this.emitter.emit("qualitychange", { label: c }),
|
|
1545
1551
|
onError: (c, h) => this.emitter.emit("error", { message: c, cause: h })
|
|
1546
1552
|
});
|
|
1547
|
-
if (
|
|
1553
|
+
if (i !== this.loadToken) {
|
|
1548
1554
|
a.destroy();
|
|
1549
1555
|
return;
|
|
1550
1556
|
}
|
|
1551
|
-
if (this.sourceController = a, this.emitter.emit("sourcechange", { source:
|
|
1552
|
-
|
|
1553
|
-
}).catch((c) => this.emitter.emit("error", { message: "Failed to load thumbnails track", cause: c })),
|
|
1557
|
+
if (this.sourceController = a, this.emitter.emit("sourcechange", { source: s, index: t }), s.thumbnails && q.load(s.thumbnails).then((c) => {
|
|
1558
|
+
i === this.loadToken && (this.thumbTrack = c, this.controls.progress.setThumbnails(c), this.scenesPanel.visible && this.scenesPanel.rebuild());
|
|
1559
|
+
}).catch((c) => this.emitter.emit("error", { message: "Failed to load thumbnails track", cause: c })), s.chapters) {
|
|
1554
1560
|
const c = (h) => {
|
|
1555
1561
|
const u = () => {
|
|
1556
|
-
|
|
1562
|
+
i === this.loadToken && (this.chapters = at(h, this.video.duration), this.controls.progress.setChapters(this.chapters), this.controls.syncFeatureButtons(), this.scenesPanel.visible && this.scenesPanel.rebuild());
|
|
1557
1563
|
};
|
|
1558
1564
|
Number.isFinite(this.video.duration) && this.video.duration > 0 ? u() : this.video.addEventListener("loadedmetadata", u, { once: !0, signal: this.abort.signal });
|
|
1559
1565
|
};
|
|
1560
|
-
typeof
|
|
1566
|
+
typeof s.chapters == "string" ? ht(s.chapters).then((h) => c(h)).catch((h) => this.emitter.emit("error", { message: "Failed to load chapters track", cause: h })) : c(s.chapters);
|
|
1561
1567
|
}
|
|
1562
|
-
if (
|
|
1563
|
-
const c =
|
|
1564
|
-
|
|
1568
|
+
if (s.heatmap && s.heatmap.length > 0 && this.controlsOptions.heatmap) {
|
|
1569
|
+
const c = s.heatmap, h = () => {
|
|
1570
|
+
i === this.loadToken && this.controls.progress.setHeatmap(et(c, this.video.duration));
|
|
1565
1571
|
};
|
|
1566
1572
|
Number.isFinite(this.video.duration) && this.video.duration > 0 ? h() : this.video.addEventListener("loadedmetadata", h, { once: !0, signal: this.abort.signal });
|
|
1567
1573
|
}
|
|
1568
1574
|
e && this.play().catch(() => {
|
|
1569
|
-
|
|
1575
|
+
i === this.loadToken && this.poster.show();
|
|
1570
1576
|
});
|
|
1571
1577
|
}
|
|
1572
1578
|
bindVideoEvents() {
|
|
1573
1579
|
const { signal: t } = this.abort, e = this.video;
|
|
1574
1580
|
e.addEventListener("play", () => {
|
|
1575
|
-
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();
|
|
1581
|
+
this.playedOnce = !0, this.poster.hide(), this.pauseScreen.hide(), this.related.visible || this.related.hide(), this.related.hide(), e.readyState < 3 && (this.spinner.hidden = !1), this.emitter.emit("play", void 0), this.scheduleIdle();
|
|
1576
1582
|
}, { signal: t }), e.addEventListener("pause", () => {
|
|
1577
|
-
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());
|
|
1583
|
+
this.spinner.hidden = !0, 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());
|
|
1578
1584
|
}, { signal: t }), e.addEventListener("ended", () => {
|
|
1579
1585
|
this.handleEnded();
|
|
1580
1586
|
}, { signal: t }), e.addEventListener("timeupdate", () => {
|
|
1581
1587
|
this.emitter.emit("timeupdate", { currentTime: e.currentTime, duration: e.duration || 0 }), this.adManager?.checkMidRolls(e.currentTime);
|
|
1582
|
-
const
|
|
1583
|
-
|
|
1588
|
+
const i = K(this.chapters, e.currentTime);
|
|
1589
|
+
i !== this.currentChapter && (this.currentChapter = i, this.emitter.emit("chapterchange", { chapter: i }));
|
|
1584
1590
|
}, { signal: t }), e.addEventListener("progress", () => {
|
|
1585
1591
|
this.emitter.emit("progress", { buffered: this.bufferedEnd });
|
|
1586
1592
|
}, { signal: t }), e.addEventListener("volumechange", () => {
|
|
@@ -1588,14 +1594,14 @@ class _t {
|
|
|
1588
1594
|
}, { 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", () => {
|
|
1589
1595
|
this.spinner.hidden = !1;
|
|
1590
1596
|
}, { signal: t });
|
|
1591
|
-
for (const
|
|
1592
|
-
e.addEventListener(
|
|
1597
|
+
for (const i of ["playing", "canplay", "seeked"])
|
|
1598
|
+
e.addEventListener(i, () => {
|
|
1593
1599
|
this.spinner.hidden = !0, this.errorBox.hidden = !0, this.decodeRecoveries = 0;
|
|
1594
1600
|
}, { signal: t });
|
|
1595
1601
|
e.addEventListener("error", () => {
|
|
1596
|
-
const
|
|
1597
|
-
if (!
|
|
1598
|
-
if ((
|
|
1602
|
+
const i = e.error;
|
|
1603
|
+
if (!i || this.sourceController?.kind === "hls" || e.getAttribute("src") === null && !e.currentSrc) return;
|
|
1604
|
+
if ((i.code === MediaError.MEDIA_ERR_DECODE || /DEMUXER|PARSE|DECODE/i.test(i.message || "")) && this.decodeRecoveries < 2 && Number.isFinite(e.duration)) {
|
|
1599
1605
|
this.decodeRecoveries++;
|
|
1600
1606
|
const n = e.currentTime, o = !e.paused;
|
|
1601
1607
|
e.load();
|
|
@@ -1610,7 +1616,7 @@ class _t {
|
|
|
1610
1616
|
e.readyState >= 1 ? a() : e.addEventListener("loadedmetadata", a, { once: !0, signal: this.abort.signal });
|
|
1611
1617
|
return;
|
|
1612
1618
|
}
|
|
1613
|
-
this.emitter.emit("error", { message:
|
|
1619
|
+
this.emitter.emit("error", { message: i.message || `Media error (code ${i.code})`, cause: i });
|
|
1614
1620
|
}, { 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", () => {
|
|
1615
1621
|
this.emitter.emit("fullscreenchange", { active: this.isFullscreen }), this.container.classList.toggle("imp-player--fullscreen", this.isFullscreen);
|
|
1616
1622
|
}, { signal: t });
|
|
@@ -1629,8 +1635,12 @@ class _t {
|
|
|
1629
1635
|
const { signal: t } = this.abort, e = () => {
|
|
1630
1636
|
this.showControlsNow(), this.scheduleIdle();
|
|
1631
1637
|
};
|
|
1632
|
-
this.container.addEventListener("pointermove",
|
|
1633
|
-
|
|
1638
|
+
this.container.addEventListener("pointermove", (i) => {
|
|
1639
|
+
i.pointerType !== "touch" && e();
|
|
1640
|
+
}, { signal: t }), this.container.addEventListener("pointerdown", (i) => {
|
|
1641
|
+
i.pointerType !== "touch" && e();
|
|
1642
|
+
}, { signal: t }), this.container.addEventListener("keydown", e, { signal: t }), this.container.addEventListener("pointerleave", (i) => {
|
|
1643
|
+
i.pointerType !== "touch" && (this.video.paused || this.container.classList.add("imp-player--idle"));
|
|
1634
1644
|
}, { signal: t });
|
|
1635
1645
|
}
|
|
1636
1646
|
showControlsNow() {
|
|
@@ -1646,11 +1656,11 @@ class _t {
|
|
|
1646
1656
|
this.container.addEventListener("keydown", (t) => {
|
|
1647
1657
|
const e = t.target;
|
|
1648
1658
|
if (e.closest("input, select, textarea, [contenteditable]")) return;
|
|
1649
|
-
const
|
|
1659
|
+
const s = e.closest("button") !== null;
|
|
1650
1660
|
switch (t.key) {
|
|
1651
1661
|
case " ":
|
|
1652
1662
|
case "k":
|
|
1653
|
-
if (t.key === " " &&
|
|
1663
|
+
if (t.key === " " && s) return;
|
|
1654
1664
|
t.preventDefault(), this.togglePlay();
|
|
1655
1665
|
break;
|
|
1656
1666
|
case "ArrowLeft":
|