itube-modern-player 0.1.0 → 0.2.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/README.md +33 -0
- package/dist/core.cjs +4 -4
- package/dist/core.cjs.map +1 -1
- package/dist/core.js +366 -329
- 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/player.d.ts +1 -0
- package/dist/style.css +1 -1
- package/dist/types.d.ts +15 -0
- package/dist/ui/overlays.d.ts +4 -0
- 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 s = document.createElement(r);
|
|
4
|
+
if (t && (s.className = t), e)
|
|
5
|
+
for (const [i, n] of Object.entries(e)) s.setAttribute(i, n);
|
|
6
|
+
return s;
|
|
7
7
|
}
|
|
8
8
|
function m(r, t, e) {
|
|
9
|
-
const
|
|
10
|
-
return
|
|
9
|
+
const s = l("button", `imp-btn ${r}`, { type: "button", "aria-label": t, title: t });
|
|
10
|
+
return s.innerHTML = e, s;
|
|
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,29 +17,29 @@ 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), s = Math.floor(r / 3600), i = s > 0 ? String(e).padStart(2, "0") : String(e), n = String(t).padStart(2, "0");
|
|
21
|
+
return s > 0 ? `${s}:${i}:${n}` : `${i}:${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, s = Number(t[2]), i = Number(t[3]), n = t[4] ? Number(t[4].padEnd(3, "0")) : 0;
|
|
27
|
+
return e * 3600 + s * 60 + i + 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 s of e) {
|
|
33
|
+
const i = s.split(`
|
|
34
34
|
`).filter((f) => f.trim() !== "");
|
|
35
|
-
if (
|
|
36
|
-
let n =
|
|
35
|
+
if (i.length === 0) continue;
|
|
36
|
+
let n = i.findIndex((f) => f.includes("-->"));
|
|
37
37
|
if (n === -1) continue;
|
|
38
|
-
const [o, a] =
|
|
39
|
-
if (
|
|
40
|
-
const u =
|
|
38
|
+
const [o, a] = i[n].split("-->"), c = R(o), h = R((a ?? "").split(" ")[1] ?? a ?? "") ?? R(a ?? "");
|
|
39
|
+
if (c === null || h === null) continue;
|
|
40
|
+
const u = i.slice(n + 1).join(`
|
|
41
41
|
`).trim();
|
|
42
|
-
u && t.push({ start:
|
|
42
|
+
u && t.push({ start: c, end: h, text: u });
|
|
43
43
|
}
|
|
44
44
|
return t;
|
|
45
45
|
}
|
|
@@ -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 s = this.listeners.get(t);
|
|
60
|
+
return s || (s = /* @__PURE__ */ new Set(), this.listeners.set(t, s)), s.add(e), () => this.off(t, e);
|
|
61
61
|
}
|
|
62
62
|
once(t, e) {
|
|
63
|
-
const
|
|
64
|
-
|
|
63
|
+
const s = this.on(t, (i) => {
|
|
64
|
+
s(), e(i);
|
|
65
65
|
});
|
|
66
|
-
return
|
|
66
|
+
return s;
|
|
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 s = this.listeners.get(t);
|
|
73
|
+
if (s)
|
|
74
|
+
for (const i of [...s])
|
|
75
75
|
try {
|
|
76
|
-
|
|
76
|
+
i(e);
|
|
77
77
|
} catch (n) {
|
|
78
78
|
console.error("[itube-player] listener error", n);
|
|
79
79
|
}
|
|
@@ -127,30 +127,30 @@ 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
|
|
132
|
-
for (const
|
|
133
|
-
if (!Number.isFinite(
|
|
134
|
-
const u = Math.max(0,
|
|
130
|
+
const s = new Array(e).fill(0);
|
|
131
|
+
let i = !1;
|
|
132
|
+
for (const h of r) {
|
|
133
|
+
if (!Number.isFinite(h.time) || h.time < 0 || h.time > t) continue;
|
|
134
|
+
const u = Math.max(0, h.value);
|
|
135
135
|
if (u === 0) continue;
|
|
136
|
-
const f = Math.min(e - 1, Math.floor(
|
|
137
|
-
|
|
136
|
+
const f = Math.min(e - 1, Math.floor(h.time / t * e));
|
|
137
|
+
s[f] += u, i = !0;
|
|
138
138
|
}
|
|
139
|
-
if (!
|
|
140
|
-
const n = [0.06, 0.24, 0.4, 0.24, 0.06], o =
|
|
141
|
-
(
|
|
139
|
+
if (!i) return [];
|
|
140
|
+
const n = [0.06, 0.24, 0.4, 0.24, 0.06], o = s.map(
|
|
141
|
+
(h, u) => n.reduce((f, d, b) => f + d * (s[u + b - 2] ?? 0), 0)
|
|
142
142
|
), a = Math.max(...o);
|
|
143
143
|
if (a <= 0) return [];
|
|
144
|
-
const
|
|
145
|
-
return o.map((
|
|
144
|
+
const c = 0.08;
|
|
145
|
+
return o.map((h) => c + (1 - c) * (h / a));
|
|
146
146
|
}
|
|
147
147
|
function it(r, t = 1e3, e = 100) {
|
|
148
148
|
if (r.length === 0) return "";
|
|
149
|
-
const
|
|
150
|
-
let
|
|
149
|
+
const s = t / (r.length - 1 || 1);
|
|
150
|
+
let i = `M 0 ${e}`;
|
|
151
151
|
return r.forEach((n, o) => {
|
|
152
|
-
|
|
153
|
-
}),
|
|
152
|
+
i += ` L ${(o * s).toFixed(1)} ${(e - n * e).toFixed(1)}`;
|
|
153
|
+
}), i += ` L ${t} ${e} Z`, i;
|
|
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, s, i) {
|
|
168
|
+
if (s > 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 {
|
|
@@ -175,18 +175,18 @@ async function j(r, t, e, i, s) {
|
|
|
175
175
|
} finally {
|
|
176
176
|
clearTimeout(o);
|
|
177
177
|
}
|
|
178
|
-
const
|
|
179
|
-
if (
|
|
180
|
-
const
|
|
181
|
-
if (!
|
|
182
|
-
nt(
|
|
183
|
-
const u =
|
|
178
|
+
const c = new DOMParser().parseFromString(a, "text/xml");
|
|
179
|
+
if (c.querySelector("parsererror")) throw new Error("VAST response is not valid XML");
|
|
180
|
+
const h = c.querySelector("VAST > Ad");
|
|
181
|
+
if (!h) throw new Error("VAST response contains no ads");
|
|
182
|
+
nt(h, i);
|
|
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, s + 1, i);
|
|
188
188
|
}
|
|
189
|
-
const f =
|
|
189
|
+
const f = h.querySelector(":scope > InLine");
|
|
190
190
|
if (!f) throw new Error("VAST ad has neither InLine nor Wrapper");
|
|
191
191
|
const d = f.querySelector("Creatives > Creative > Linear");
|
|
192
192
|
if (!d) throw new Error("VAST ad has no Linear creative");
|
|
@@ -202,24 +202,24 @@ async function j(r, t, e, i, s) {
|
|
|
202
202
|
d.getAttribute("skipoffset"),
|
|
203
203
|
V(k(d.querySelector(":scope > Duration")))
|
|
204
204
|
),
|
|
205
|
-
impressions:
|
|
206
|
-
tracking:
|
|
205
|
+
impressions: i.impressions,
|
|
206
|
+
tracking: i.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, s;
|
|
212
|
+
for (const i of r.querySelectorAll("Impression")) {
|
|
213
|
+
const n = k(i);
|
|
214
214
|
n && t.impressions.push(n);
|
|
215
215
|
}
|
|
216
|
-
for (const
|
|
217
|
-
const n =
|
|
216
|
+
for (const i of r.querySelectorAll("Linear > TrackingEvents > Tracking")) {
|
|
217
|
+
const n = i.getAttribute("event"), o = k(i);
|
|
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 i of r.querySelectorAll("Linear > VideoClicks > ClickTracking")) {
|
|
221
|
+
const n = k(i);
|
|
222
|
+
n && ((s = t.tracking).click ?? (s.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 s = e.filter((o) => o.delivery !== "streaming"), i = s.length > 0 ? s : e, n = i[Math.floor(i.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((s) => s.timer !== void 0 && t >= s.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 s = this.host.contentVideo, i = !s.paused && !s.ended;
|
|
325
|
+
s.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
|
+
(i || t[0].roll === "preRoll") && s.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: s } = this.host, i = 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
|
-
const
|
|
349
|
-
u.textContent = t.adTitle ? `${
|
|
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 ? `${s.adLabel} · ${t.adTitle}` : s.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 = s.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), i.append(n, c, h, d), this.host.container.append(i), this.layer = i;
|
|
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,21 +372,21 @@ 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(), i.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
|
};
|
|
379
379
|
n.addEventListener("playing", () => {
|
|
380
380
|
C(t.impressions), L("start"), this.host.emitter.emit("adstart", { ad: t });
|
|
381
381
|
}, { once: !0, signal: a }), n.addEventListener("playing", () => {
|
|
382
|
-
|
|
382
|
+
c.hidden = !0;
|
|
383
383
|
}, { signal: a }), n.addEventListener("waiting", () => {
|
|
384
|
-
|
|
384
|
+
c.hidden = !1;
|
|
385
385
|
}, { signal: a }), n.addEventListener("timeupdate", () => {
|
|
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 = `${s.skipAdIn} ${z}`) : (v.hidden = !1, v.disabled = !1, v.textContent = s.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 || !i.isConnected || (i.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
|
+
i.classList.remove("imp-ad--paused"), B && (B = !1, C(t.tracking.resume), this.host.emitter.emit("adresume", { ad: t }));
|
|
408
|
+
}, { signal: a }), i.addEventListener("click", (g) => {
|
|
409
|
+
(g.target === i || i.classList.contains("imp-ad--paused")) && n.paused && n.play().catch(() => {
|
|
410
410
|
});
|
|
411
411
|
}), n.play().catch(() => {
|
|
412
|
-
|
|
412
|
+
i.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((i, n) => i.start - n.start), s = [];
|
|
426
|
+
for (let i = 0; i < e.length; i++) {
|
|
427
|
+
const n = Math.max(0, e[i].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[i].end ?? e[i + 1]?.start ?? t;
|
|
430
|
+
Number.isFinite(t) && (o = Math.min(o, t)), !(o <= n) && s.push({ start: n, end: o, title: e[i].title });
|
|
431
431
|
}
|
|
432
|
-
return
|
|
432
|
+
return s;
|
|
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((s) => ({ start: s.start, end: s.end, title: s.text }));
|
|
439
439
|
}
|
|
440
440
|
function K(r, t) {
|
|
441
441
|
for (const e of r)
|
|
@@ -452,16 +452,16 @@ async function dt() {
|
|
|
452
452
|
if (r)
|
|
453
453
|
return S = r, S;
|
|
454
454
|
try {
|
|
455
|
-
S = (await import("hls.js")).default;
|
|
455
|
+
S = (await import("hls.js/light")).default;
|
|
456
456
|
} catch {
|
|
457
457
|
S = null;
|
|
458
458
|
}
|
|
459
459
|
return S;
|
|
460
460
|
}
|
|
461
|
-
async function ut(r, t, e,
|
|
461
|
+
async function ut(r, t, e, s) {
|
|
462
462
|
if (ct(t, e)) {
|
|
463
|
-
const
|
|
464
|
-
return n && n.isSupported() ? pt(n, r, t,
|
|
463
|
+
const i = r.canPlayType("application/vnd.apple.mpegurl"), n = i ? null : await dt();
|
|
464
|
+
return n && n.isSupported() ? pt(n, r, t, s) : i ? (r.src = t, $(r)) : (s.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, s) {
|
|
481
|
+
const i = new r({ enableWorker: !0 }), n = {
|
|
482
482
|
kind: "hls",
|
|
483
483
|
levels: [],
|
|
484
484
|
selected: -1,
|
|
485
|
-
setLevel(
|
|
486
|
-
n.selected =
|
|
485
|
+
setLevel(c) {
|
|
486
|
+
n.selected = c, i.currentLevel = c;
|
|
487
487
|
},
|
|
488
488
|
destroy() {
|
|
489
|
-
|
|
489
|
+
i.destroy(), t.removeAttribute("src"), t.load();
|
|
490
490
|
}
|
|
491
491
|
};
|
|
492
|
-
|
|
493
|
-
n.levels =
|
|
494
|
-
index:
|
|
495
|
-
label:
|
|
496
|
-
})).reverse(),
|
|
497
|
-
}),
|
|
498
|
-
const u =
|
|
499
|
-
u &&
|
|
492
|
+
i.on(r.Events.MANIFEST_PARSED, () => {
|
|
493
|
+
n.levels = i.levels.map((c, h) => ({
|
|
494
|
+
index: h,
|
|
495
|
+
label: c.height ? `${c.height}p` : `${Math.round(c.bitrate / 1e3)} kbps`
|
|
496
|
+
})).reverse(), s.onLevels(n.levels);
|
|
497
|
+
}), i.on(r.Events.LEVEL_SWITCHED, (c, h) => {
|
|
498
|
+
const u = i.levels[h.level];
|
|
499
|
+
u && s.onLevelSwitch(u.height ? `${u.height}p` : `${Math.round(u.bitrate / 1e3)} kbps`);
|
|
500
500
|
});
|
|
501
501
|
let o = 0, a = !1;
|
|
502
|
-
return
|
|
503
|
-
if (
|
|
504
|
-
switch (
|
|
502
|
+
return i.on(r.Events.ERROR, (c, h) => {
|
|
503
|
+
if (h.fatal)
|
|
504
|
+
switch (h.type) {
|
|
505
505
|
case r.ErrorTypes.NETWORK_ERROR:
|
|
506
|
-
|
|
506
|
+
i.startLoad();
|
|
507
507
|
break;
|
|
508
508
|
case r.ErrorTypes.MEDIA_ERROR:
|
|
509
|
-
o < 3 ? (o++,
|
|
509
|
+
o < 3 ? (o++, i.recoverMediaError()) : (s.onError(`HLS media error: ${h.details}`, h), i.destroy());
|
|
510
510
|
break;
|
|
511
511
|
default:
|
|
512
512
|
if (a)
|
|
513
|
-
|
|
513
|
+
s.onError(`HLS fatal error: ${h.details}`, h), i.destroy();
|
|
514
514
|
else {
|
|
515
515
|
a = !0;
|
|
516
516
|
try {
|
|
517
|
-
|
|
517
|
+
i.loadSource(e), i.startLoad();
|
|
518
518
|
} catch {
|
|
519
|
-
|
|
519
|
+
s.onError(`HLS fatal error: ${h.details}`, h), i.destroy();
|
|
520
520
|
}
|
|
521
521
|
}
|
|
522
522
|
}
|
|
523
|
-
}),
|
|
523
|
+
}), i.loadSource(e), i.attachMedia(t), n;
|
|
524
524
|
}
|
|
525
525
|
class q {
|
|
526
526
|
constructor(t) {
|
|
@@ -529,25 +529,25 @@ 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 s = await e.text(), i = [];
|
|
533
|
+
for (const n of U(s)) {
|
|
534
534
|
const [o, a] = n.text.trim().split("#");
|
|
535
535
|
if (!o) continue;
|
|
536
|
-
const
|
|
536
|
+
const c = {
|
|
537
537
|
start: n.start,
|
|
538
538
|
end: n.end,
|
|
539
539
|
src: G(o, t)
|
|
540
|
-
},
|
|
541
|
-
|
|
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]) }), i.push(c);
|
|
542
542
|
}
|
|
543
|
-
return
|
|
543
|
+
return i.sort((n, o) => n.start - o.start), new q(i);
|
|
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, s = this.cues.length - 1;
|
|
547
|
+
for (; e <= s; ) {
|
|
548
|
+
const i = e + s >> 1, n = this.cues[i];
|
|
549
|
+
if (t < n.start) s = i - 1;
|
|
550
|
+
else if (t >= n.end) e = i + 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 s = l("div", "imp-menu__section");
|
|
573
573
|
if (e.title) {
|
|
574
|
-
const
|
|
575
|
-
|
|
574
|
+
const i = l("div", "imp-menu__title");
|
|
575
|
+
i.textContent = e.title, s.append(i);
|
|
576
576
|
}
|
|
577
|
-
for (const
|
|
577
|
+
for (const i 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(i.active)), i.active && n.classList.add("imp-menu__item--active"), i.icon) {
|
|
580
580
|
const a = l("span", "imp-menu__icon");
|
|
581
|
-
|
|
581
|
+
i.icon.trimStart().startsWith("<svg") ? a.innerHTML = i.icon : a.append(l("img", "", { src: i.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 = i.label, n.append(o), n.addEventListener("click", () => {
|
|
585
|
+
e.onSelect(i.value), this.close();
|
|
586
|
+
}), s.append(n);
|
|
587
587
|
}
|
|
588
|
-
this.root.append(
|
|
588
|
+
this.root.append(s);
|
|
589
589
|
}
|
|
590
590
|
this.root.hidden = !1, this.backdrop.hidden = !1, this.outsideListener = (e) => {
|
|
591
|
-
const
|
|
592
|
-
!this.root.contains(
|
|
591
|
+
const s = e.target;
|
|
592
|
+
!this.root.contains(s) && !this.anchor.contains(s) && 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: "" }], s = [];
|
|
622
|
+
let i = 0;
|
|
623
623
|
for (const n of e)
|
|
624
|
-
n.start >
|
|
625
|
-
this.duration > 0 &&
|
|
626
|
-
for (const n of
|
|
624
|
+
n.start > i && s.push({ start: i, end: n.start, title: "" }), s.push(n), i = n.end;
|
|
625
|
+
this.duration > 0 && i < this.duration && s.push({ start: i, end: this.duration, title: "" });
|
|
626
|
+
for (const n of s) {
|
|
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, s) {
|
|
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 i = this.duration > 0 ? y(s / this.duration, 0, 1) : 0;
|
|
640
|
+
this.buffered.style.width = `${i * 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: s, fill: i } of this.segments) {
|
|
644
|
+
const n = s.end - s.start, o = n > 0 ? y((t - s.start) / n, 0, 1) : 0;
|
|
645
|
+
i.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 s = K(this.chapters, e);
|
|
669
|
+
this.tooltipChapter.textContent = s?.title ?? "", this.tooltipChapter.hidden = !s?.title;
|
|
670
|
+
const i = this.thumbnails?.cueAt(e) ?? null;
|
|
671
|
+
i ? (this.tooltipThumb.hidden = !1, this.tooltipThumb.style.backgroundImage = `url("${i.src}")`, i.xywh ? (this.tooltipThumb.style.width = `${i.xywh.w}px`, this.tooltipThumb.style.height = `${i.xywh.h}px`, this.tooltipThumb.style.backgroundPosition = `-${i.xywh.x}px -${i.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: s } = t, i = 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
|
+
}), i.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
|
-
const
|
|
697
|
-
this.chapterLabel = l("div", "imp-controls__chapter"), n.append(o,
|
|
698
|
-
const
|
|
699
|
-
if (
|
|
696
|
+
const c = l("div", "imp-controls__spacer");
|
|
697
|
+
this.chapterLabel = l("div", "imp-controls__chapter"), n.append(o, c, a), c.append(this.chapterLabel);
|
|
698
|
+
const h = typeof i.seekButtons == "object" ? i.seekButtons : {}, u = h.back ?? t.seekStep, f = h.forward ?? t.seekStep;
|
|
699
|
+
if (i.playlist && t.hasPlaylist && (this.prevBtn = m("imp-btn--prev", e.previous, s.previous), this.prevBtn.addEventListener("click", () => t.previous()), o.append(this.prevBtn)), i.seekButtons && (this.seekBackBtn = m("imp-btn--seek-back", `${e.seekBack} ${u}s`, s.seekBack), this.seekBackBtn.addEventListener("click", () => t.skip(-u)), o.append(this.seekBackBtn)), i.play && (this.playBtn = m("imp-btn--play", e.play, s.play), this.playBtn.addEventListener("click", () => t.togglePlay()), o.append(this.playBtn)), i.seekButtons && (this.seekFwdBtn = m("imp-btn--seek-forward", `${e.seekForward} ${f}s`, s.seekForward), this.seekFwdBtn.addEventListener("click", () => t.skip(f)), o.append(this.seekFwdBtn)), i.playlist && t.hasPlaylist && (this.nextBtn = m("imp-btn--next", e.next, s.next), this.nextBtn.addEventListener("click", () => t.next()), o.append(this.nextBtn)), i.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, s.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 (i.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), i.subtitles) {
|
|
712
|
+
this.subtitlesBtn = m("imp-btn--subtitles", e.subtitles, s.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 (i.settings) {
|
|
723
|
+
this.settingsBtn = m("imp-btn--settings", e.settings, s.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 (i.quality) {
|
|
734
|
+
this.qualityBtn = m("imp-btn--quality", e.quality, s.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 (i.scenes && (this.scenesBtn = m("imp-btn--scenes", e.scenes, s.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, s.scenes, () => t.toggleScenesPanel())
|
|
750
|
+
})), i.playlist && t.hasPlaylist) {
|
|
751
|
+
const d = m("imp-btn--list", e.playlist, s.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, s.list, () => t.togglePlaylistPanel())
|
|
758
758
|
});
|
|
759
759
|
}
|
|
760
|
-
if (
|
|
761
|
-
const d = m("imp-btn--pip", e.pip,
|
|
760
|
+
if (i.pip && "requestPictureInPicture" in HTMLVideoElement.prototype) {
|
|
761
|
+
const d = m("imp-btn--pip", e.pip, s.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, s.pip, () => void t.togglePip())
|
|
768
768
|
});
|
|
769
769
|
}
|
|
770
|
-
if (
|
|
770
|
+
if (i.fullscreen && (this.fullscreenBtn = m("imp-btn--fullscreen", e.fullscreen, s.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, s.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, s.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`, s.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`, s.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"), i.playlist && t.hasPlaylist && (this.centerPrevBtn = this.makeCenterButton("prev", e.previous, s.previous), this.centerPrevBtn.addEventListener("click", () => t.previous()), this.center.append(this.centerPrevBtn)), i.seekButtons) {
|
|
803
|
+
const d = this.makeCenterButton("seek-back", `${e.seekBack} ${u}s`, s.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 (i.play && (this.centerPlayBtn = this.makeCenterButton("play", e.play, s.play), this.centerPlayBtn.addEventListener("click", () => t.togglePlay()), this.center.append(this.centerPlayBtn)), i.seekButtons) {
|
|
807
|
+
const d = this.makeCenterButton("seek-forward", `${e.seekForward} ${f}s`, s.seekForward);
|
|
808
808
|
d.addEventListener("click", () => t.skip(f)), this.center.append(d);
|
|
809
809
|
}
|
|
810
|
-
|
|
810
|
+
i.playlist && t.hasPlaylist && (this.centerNextBtn = this.makeCenterButton("next", e.next, s.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, s) {
|
|
817
|
+
const i = l("button", `imp-center-btn imp-center-btn--${t}`, { type: "button", "aria-label": e, title: e });
|
|
818
|
+
return i.innerHTML = s, i;
|
|
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, s = e.actionsOptions, { labels: i, icons: n } = e;
|
|
823
|
+
if (s.like) {
|
|
824
|
+
this.likeBtn = m("imp-btn--like", i.like, n.like), this.likeBtn.addEventListener("click", () => e.emit("action", { id: "like" })), this.likeCountEl = this.attachCountTooltip(this.likeBtn, s.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", i.like, n.like, () => e.emit("action", { id: "like" }))
|
|
832
832
|
});
|
|
833
833
|
}
|
|
834
|
-
if (
|
|
835
|
-
this.dislikeBtn = m("imp-btn--dislike",
|
|
834
|
+
if (s.dislike) {
|
|
835
|
+
this.dislikeBtn = m("imp-btn--dislike", i.dislike, n.dislike), this.dislikeBtn.addEventListener("click", () => e.emit("action", { id: "dislike" })), this.dislikeCountEl = this.attachCountTooltip(this.dislikeBtn, s.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", i.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 s = l("span", "imp-count-tooltip");
|
|
848
|
+
return this.setCountText(s, e), s;
|
|
849
849
|
}
|
|
850
850
|
wrapWithTooltip(t, e) {
|
|
851
|
-
const
|
|
852
|
-
return
|
|
851
|
+
const s = l("div", "imp-action");
|
|
852
|
+
return s.append(t, e), s;
|
|
853
853
|
}
|
|
854
854
|
setCountText(t, e) {
|
|
855
|
-
const
|
|
856
|
-
t.textContent =
|
|
855
|
+
const s = e == null || e === "" ? "" : String(e);
|
|
856
|
+
t.textContent = s, t.hidden = s === "";
|
|
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
|
|
869
|
-
this.actionItems.push({ value: `custom:${
|
|
870
|
-
const o = m("imp-btn--more",
|
|
866
|
+
const e = this.player, s = e.actionsOptions, { labels: i, icons: n } = e;
|
|
867
|
+
s.addTo && this.actionItems.push({ value: "addTo", label: i.addTo, icon: n.addTo, run: () => e.emit("action", { id: "addTo" }) }), s.share && this.actionItems.push({ value: "share", label: i.share, icon: n.share, run: () => void e.share() }), s.report && this.actionItems.push({ value: "report", label: i.report, icon: n.report, run: () => e.emit("action", { id: "report" }) });
|
|
868
|
+
for (const c of s.custom ?? [])
|
|
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", i.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,28 +876,28 @@ 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(), s = [], i = ["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) => i.indexOf(o.key) - i.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
|
-
const
|
|
885
|
-
|
|
884
|
+
const c = a.items[0];
|
|
885
|
+
s.push(c), e.set(c.value, () => a.onSelect(c.value));
|
|
886
886
|
} else
|
|
887
887
|
t.push(a);
|
|
888
888
|
}
|
|
889
|
-
if (
|
|
889
|
+
if (s.length > 0 && t.unshift({ title: "", items: s, 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: "",
|
|
893
|
-
items: this.actionItems.map(({ value: a, label:
|
|
893
|
+
items: this.actionItems.map(({ value: a, label: c, icon: h }) => ({ value: a, label: c, icon: h, active: !1 })),
|
|
894
894
|
onSelect: (a) => o.get(a)?.()
|
|
895
895
|
});
|
|
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, s, i) {
|
|
900
|
+
return { title: "", items: [{ value: t, label: e, icon: s, active: !1 }], onSelect: () => i() };
|
|
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, s) => ({ label: e.label, value: String(s), active: t.activeSubtitle === s }))
|
|
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 i of this.collapsibles) {
|
|
950
|
+
if (t && _.CENTER_KEYS.has(i.key)) {
|
|
951
|
+
i.el.style.display = "none";
|
|
952
952
|
continue;
|
|
953
953
|
}
|
|
954
|
-
const n =
|
|
955
|
-
|
|
954
|
+
const n = i.available();
|
|
955
|
+
i.el.style.display = n ? "" : "none", i.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((i) => i.available() && !(t && _.CENTER_KEYS.has(i.key))).sort((i, n) => i.priority - n.priority);
|
|
958
|
+
let s = e.length;
|
|
959
|
+
for (; s-- > 0 && this.overflowsRow(); ) {
|
|
960
|
+
const i = e.find((n) => !this.overflowed.has(n.key));
|
|
961
|
+
if (!i) break;
|
|
962
|
+
i.el.style.display = "none", i.el.classList.add("imp-collapsed"), this.overflowed.add(i.key);
|
|
963
963
|
}
|
|
964
964
|
if (this.moreWrap) {
|
|
965
|
-
const
|
|
966
|
-
this.moreWrap.style.display =
|
|
965
|
+
const i = this.actionItems.length > 0 || this.overflowed.size > 0;
|
|
966
|
+
this.moreWrap.style.display = i ? "" : "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: s }) => {
|
|
977
|
+
this.progress.update(e, s, 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(s)}`));
|
|
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, s] = 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, s), this.centerPlayBtn && E(this.centerPlayBtn, e, s);
|
|
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, s = e === 0 ? t.icons.volumeMute : e < 0.5 ? t.icons.volumeLow : t.icons.volumeHigh;
|
|
1004
|
+
E(this.muteBtn, s, 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() {
|
|
@@ -1033,14 +1033,14 @@ _.CENTER_KEYS = /* @__PURE__ */ new Set(["seekBack", "seekFwd", "prev", "next"])
|
|
|
1033
1033
|
let F = _;
|
|
1034
1034
|
class ft {
|
|
1035
1035
|
constructor(t) {
|
|
1036
|
-
this.root = l("div", "imp-poster"), this.image = l("
|
|
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", (s) => {
|
|
1039
|
+
(s.target === this.root || s.target === this.image) && t.play();
|
|
1040
1040
|
});
|
|
1041
1041
|
}
|
|
1042
1042
|
setSource(t) {
|
|
1043
|
-
this.image.
|
|
1043
|
+
t?.poster ? (this.image.src = t.poster, this.image.fetchPriority = "high", this.image.hidden = !1) : (this.image.removeAttribute("src"), this.image.hidden = !0);
|
|
1044
1044
|
}
|
|
1045
1045
|
show() {
|
|
1046
1046
|
this.root.hidden = !1;
|
|
@@ -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", (s) => s.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 s = t?.channel;
|
|
1072
|
+
this.channel.hidden = !s, this.channelUrl = s?.url ?? null, this.channel.classList.toggle("imp-channel--link", !!s?.url), s && (this.channelName.textContent = s.name, this.channelAvatar.className = `imp-channel__avatar imp-channel__avatar--${s.avatarShape ?? "circle"}`, s.avatar ? (this.channelAvatar.textContent = "", this.channelAvatar.style.backgroundImage = `url("${s.avatar}")`) : (this.channelAvatar.style.backgroundImage = "", this.channelAvatar.textContent = (s.name.trim()[0] ?? "?").toUpperCase()));
|
|
1073
1073
|
}
|
|
1074
1074
|
show() {
|
|
1075
1075
|
this.root.hidden = !1;
|
|
@@ -1080,28 +1080,43 @@ class vt {
|
|
|
1080
1080
|
}
|
|
1081
1081
|
class gt {
|
|
1082
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");
|
|
1083
|
+
this.player = t, this.clickBehavior = "player", 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
1084
|
const e = m("imp-related__close", "Close", t.icons.close);
|
|
1085
1085
|
e.addEventListener("click", () => this.hide()), this.root.append(e, this.heading, this.grid);
|
|
1086
1086
|
}
|
|
1087
1087
|
setOptions(t) {
|
|
1088
1088
|
if (this.grid.textContent = "", !!t) {
|
|
1089
|
-
this.heading.textContent = t.title ?? this.player.labels.related;
|
|
1089
|
+
this.clickBehavior = t.clickBehavior ?? "player", this.heading.textContent = t.title ?? this.player.labels.related;
|
|
1090
1090
|
for (const e of t.items)
|
|
1091
1091
|
this.grid.append(this.buildCard(e));
|
|
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" }), s = l("div", "imp-related__thumb");
|
|
1096
|
+
if (t.poster && (s.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, s.append(n);
|
|
1099
1099
|
}
|
|
1100
|
-
const
|
|
1101
|
-
return
|
|
1102
|
-
this.player.emit("relatedclick", { item: t }), this.hide(),
|
|
1100
|
+
const i = l("div", "imp-related__card-title");
|
|
1101
|
+
return i.textContent = t.title, e.append(s, i), e.addEventListener("click", () => {
|
|
1102
|
+
this.player.emit("relatedclick", { item: t }), this.hide(), this.activate(t);
|
|
1103
1103
|
}), e;
|
|
1104
1104
|
}
|
|
1105
|
+
/** Apply the configured click behavior. */
|
|
1106
|
+
activate(t) {
|
|
1107
|
+
const e = t.url ?? t.source?.src;
|
|
1108
|
+
switch (this.clickBehavior) {
|
|
1109
|
+
case "newWindow":
|
|
1110
|
+
e && window.open(e, "_blank", "noopener");
|
|
1111
|
+
break;
|
|
1112
|
+
case "currentTab":
|
|
1113
|
+
e && (window.location.href = e);
|
|
1114
|
+
break;
|
|
1115
|
+
case "player":
|
|
1116
|
+
default:
|
|
1117
|
+
t.source ? (this.player.load(t.source), this.player.play()) : t.url && window.open(t.url, "_blank", "noopener");
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1105
1120
|
get visible() {
|
|
1106
1121
|
return !this.root.hidden;
|
|
1107
1122
|
}
|
|
@@ -1113,14 +1128,14 @@ class gt {
|
|
|
1113
1128
|
}
|
|
1114
1129
|
}
|
|
1115
1130
|
class yt {
|
|
1116
|
-
constructor(t, e = "sidebar",
|
|
1131
|
+
constructor(t, e = "sidebar", s) {
|
|
1117
1132
|
this.player = t, this.root = l("div", `imp-playlist imp-playlist--${e}`), this.root.hidden = !0;
|
|
1118
|
-
const
|
|
1119
|
-
if (
|
|
1120
|
-
const
|
|
1121
|
-
|
|
1122
|
-
const
|
|
1123
|
-
|
|
1133
|
+
const i = l("div", "imp-playlist__header"), n = l("div", "imp-playlist__heading");
|
|
1134
|
+
if (s) {
|
|
1135
|
+
const c = l("div", "imp-playlist__kicker");
|
|
1136
|
+
c.textContent = t.labels.playlist;
|
|
1137
|
+
const h = l("div", "imp-playlist__name");
|
|
1138
|
+
h.textContent = s, n.append(c, h);
|
|
1124
1139
|
} else
|
|
1125
1140
|
n.textContent = t.labels.playlist;
|
|
1126
1141
|
const o = l("div", "imp-playlist__tools");
|
|
@@ -1130,25 +1145,25 @@ class yt {
|
|
|
1130
1145
|
t.setRepeat(!t.repeat), this.syncModes();
|
|
1131
1146
|
});
|
|
1132
1147
|
const a = m("imp-playlist__close", "Close", t.icons.close);
|
|
1133
|
-
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), i.append(n, o), this.list = l("div", "imp-playlist__list"), this.root.append(i, this.list), this.rebuild(), this.syncModes();
|
|
1134
1149
|
}
|
|
1135
1150
|
syncModes() {
|
|
1136
1151
|
this.shuffleBtn.classList.toggle("imp-btn--active", this.player.shuffle), this.repeatBtn.classList.toggle("imp-btn--active", this.player.repeat);
|
|
1137
1152
|
}
|
|
1138
1153
|
rebuild() {
|
|
1139
1154
|
this.list.textContent = "", this.player.playlist.forEach((t, e) => {
|
|
1140
|
-
const
|
|
1141
|
-
e === this.player.index &&
|
|
1142
|
-
const
|
|
1143
|
-
t.poster && (
|
|
1155
|
+
const s = l("button", "imp-playlist__item", { type: "button" });
|
|
1156
|
+
e === this.player.index && s.classList.add("imp-playlist__item--active");
|
|
1157
|
+
const i = l("div", "imp-playlist__thumb");
|
|
1158
|
+
t.poster && (i.style.backgroundImage = `url("${t.poster}")`);
|
|
1144
1159
|
const n = l("div", "imp-playlist__meta"), o = l("div", "imp-playlist__title");
|
|
1145
1160
|
if (o.textContent = t.title ?? `#${e + 1}`, n.append(o), t.duration) {
|
|
1146
1161
|
const a = l("div", "imp-playlist__duration");
|
|
1147
1162
|
a.textContent = w(t.duration), n.append(a);
|
|
1148
1163
|
}
|
|
1149
|
-
|
|
1164
|
+
s.append(i, n), s.addEventListener("click", () => {
|
|
1150
1165
|
this.player.playItem(e);
|
|
1151
|
-
}), this.list.append(
|
|
1166
|
+
}), this.list.append(s);
|
|
1152
1167
|
});
|
|
1153
1168
|
}
|
|
1154
1169
|
get visible() {
|
|
@@ -1167,34 +1182,34 @@ class yt {
|
|
|
1167
1182
|
class bt {
|
|
1168
1183
|
constructor(t, e = "bottom") {
|
|
1169
1184
|
this.player = t, this.items = [], this.root = l("div", `imp-playlist imp-scenes imp-playlist--${e}`), this.root.hidden = !0;
|
|
1170
|
-
const
|
|
1171
|
-
|
|
1185
|
+
const s = l("div", "imp-playlist__header"), i = l("span");
|
|
1186
|
+
i.textContent = t.labels.scenes;
|
|
1172
1187
|
const n = m("imp-playlist__close", "Close", t.icons.close);
|
|
1173
|
-
n.addEventListener("click", () => this.hide()),
|
|
1188
|
+
n.addEventListener("click", () => this.hide()), s.append(i, n), this.list = l("div", "imp-playlist__list"), this.root.append(s, this.list), t.on("chapterchange", () => this.syncActive());
|
|
1174
1189
|
}
|
|
1175
1190
|
/** Re-render from the player's current chapters + thumbnail track. */
|
|
1176
1191
|
rebuild() {
|
|
1177
1192
|
this.list.textContent = "", this.items = [];
|
|
1178
1193
|
const t = this.player.thumbnails;
|
|
1179
1194
|
for (const e of this.player.chapterList) {
|
|
1180
|
-
const
|
|
1195
|
+
const s = l("button", "imp-playlist__item", { type: "button" }), i = l("div", "imp-playlist__thumb"), n = t?.cueAt(e.start + 0.01) ?? null;
|
|
1181
1196
|
if (n?.xywh) {
|
|
1182
|
-
const
|
|
1183
|
-
|
|
1184
|
-
} else n && (
|
|
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)), i.append(h);
|
|
1199
|
+
} else n && (i.style.backgroundImage = `url("${n.src}")`);
|
|
1185
1200
|
const o = l("div", "imp-playlist__meta"), a = l("div", "imp-playlist__title");
|
|
1186
1201
|
a.textContent = e.title || w(e.start);
|
|
1187
|
-
const
|
|
1188
|
-
|
|
1202
|
+
const c = l("div", "imp-playlist__duration");
|
|
1203
|
+
c.textContent = w(e.start), o.append(a, c), s.append(i, o), s.addEventListener("click", () => {
|
|
1189
1204
|
this.player.seek(e.start), this.player.play();
|
|
1190
|
-
}), this.list.append(
|
|
1205
|
+
}), this.list.append(s), this.items.push(s);
|
|
1191
1206
|
}
|
|
1192
1207
|
this.syncActive();
|
|
1193
1208
|
}
|
|
1194
1209
|
syncActive() {
|
|
1195
1210
|
const t = this.player.chapter, e = this.player.chapterList;
|
|
1196
|
-
this.items.forEach((
|
|
1197
|
-
|
|
1211
|
+
this.items.forEach((s, i) => {
|
|
1212
|
+
s.classList.toggle("imp-playlist__item--active", t !== null && e[i] === t);
|
|
1198
1213
|
});
|
|
1199
1214
|
}
|
|
1200
1215
|
get visible() {
|
|
@@ -1231,20 +1246,26 @@ const kt = {
|
|
|
1231
1246
|
};
|
|
1232
1247
|
class _t {
|
|
1233
1248
|
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
|
|
1236
|
-
if (!
|
|
1237
|
-
this.mount =
|
|
1238
|
-
const
|
|
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 s = typeof t == "string" ? document.querySelector(t) : t;
|
|
1251
|
+
if (!s) throw new Error(`[itube-player] mount target not found: ${String(t)}`);
|
|
1252
|
+
this.mount = s, 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 i = e.styling;
|
|
1254
|
+
if (i?.themeColor && this.container.style.setProperty("--imp-accent", i.themeColor), i?.likeColor && this.container.style.setProperty("--imp-like", i.likeColor), i?.dislikeColor && this.container.style.setProperty("--imp-dislike", i.dislikeColor), i?.borderRadius !== void 0) {
|
|
1255
|
+
const h = typeof i.borderRadius == "number" ? `${i.borderRadius}px` : i.borderRadius;
|
|
1256
|
+
this.container.style.setProperty("--imp-radius", h);
|
|
1257
|
+
}
|
|
1258
|
+
i?.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
|
+
const n = l("div", "imp-layer");
|
|
1239
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");
|
|
1240
|
-
const
|
|
1241
|
-
|
|
1242
|
-
const
|
|
1243
|
-
|
|
1244
|
-
const
|
|
1245
|
-
|
|
1261
|
+
const o = l("div", "imp-layer__middle");
|
|
1262
|
+
o.append(this.spinner, this.errorBox, this.controls.center);
|
|
1263
|
+
const a = l("div", "imp-layer__bottom");
|
|
1264
|
+
a.append(this.controls.root), n.append(this.pauseScreen.root, this.related.root, o, a), this.container.append(this.video, n, 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));
|
|
1265
|
+
const c = e.adConfig ?? e.ads;
|
|
1266
|
+
c && c.adList.length > 0 && (this.adManager = new lt(
|
|
1246
1267
|
{ container: this.container, contentVideo: this.video, emitter: this.emitter, labels: this.labels, closeIcon: this.icons.close },
|
|
1247
|
-
|
|
1268
|
+
c
|
|
1248
1269
|
)), this.emitter.on("error", ({ message: h }) => {
|
|
1249
1270
|
this.errorBox.textContent = h, this.errorBox.hidden = !1, this.spinner.hidden = !0, this.poster.hide();
|
|
1250
1271
|
}), this.bindVideoEvents(), this.bindIdleHide(), e.keyboard !== !1 && this.bindKeyboard(), this.video.addEventListener("click", () => {
|
|
@@ -1332,7 +1353,7 @@ class _t {
|
|
|
1332
1353
|
get qualityLevels() {
|
|
1333
1354
|
if (this.sourceController?.kind === "hls") return this.sourceController.levels;
|
|
1334
1355
|
const t = this.source?.qualities;
|
|
1335
|
-
return t ? t.map((e,
|
|
1356
|
+
return t ? t.map((e, s) => ({ index: s, label: e.label ?? String(e.quality ?? s) })) : [];
|
|
1336
1357
|
}
|
|
1337
1358
|
get qualityAutoAvailable() {
|
|
1338
1359
|
return this.sourceController?.kind === "hls";
|
|
@@ -1349,8 +1370,8 @@ class _t {
|
|
|
1349
1370
|
}
|
|
1350
1371
|
const e = this.source?.qualities;
|
|
1351
1372
|
if (!e || !e[t] || t === this.progressiveQuality) return;
|
|
1352
|
-
const
|
|
1353
|
-
this.progressiveQuality = t, this.video.src = e[t].src, this.video.currentTime =
|
|
1373
|
+
const s = this.video.currentTime, i = !this.video.paused && !this.video.ended;
|
|
1374
|
+
this.progressiveQuality = t, this.video.src = e[t].src, this.video.currentTime = s, i && this.video.play().catch(() => {
|
|
1354
1375
|
}), this.emitter.emit("qualitychange", { label: e[t].label ?? String(e[t].quality ?? t) });
|
|
1355
1376
|
}
|
|
1356
1377
|
// === subtitles ========================================================
|
|
@@ -1365,8 +1386,8 @@ class _t {
|
|
|
1365
1386
|
}
|
|
1366
1387
|
setSubtitle(t) {
|
|
1367
1388
|
const e = this.video.textTracks;
|
|
1368
|
-
for (let
|
|
1369
|
-
e[
|
|
1389
|
+
for (let s = 0; s < e.length; s++)
|
|
1390
|
+
e[s].mode = s === t ? "showing" : "disabled";
|
|
1370
1391
|
this.emitter.emit("subtitlechange", { track: this.subtitleTracks[t] ?? null });
|
|
1371
1392
|
}
|
|
1372
1393
|
// === playlist =========================================================
|
|
@@ -1496,56 +1517,56 @@ class _t {
|
|
|
1496
1517
|
}
|
|
1497
1518
|
// === internals ========================================================
|
|
1498
1519
|
async loadItem(t, e) {
|
|
1499
|
-
const
|
|
1500
|
-
if (!
|
|
1520
|
+
const s = ++this.loadToken, i = this.sources[t];
|
|
1521
|
+
if (!i) return;
|
|
1501
1522
|
this.currentIndex = t, this.sourceController?.destroy(), this.sourceController = null, this.chapters = [], this.currentChapter = null, this.progressiveQuality = -1, this.playedOnce = !1;
|
|
1502
|
-
for (const
|
|
1503
|
-
this.adManager?.resetForNewSource(), this.related.hide(), this.pauseScreen.hide(), this.scenesPanel.hide(), this.thumbTrack = null, this.errorBox.hidden = !0, this.video.poster =
|
|
1504
|
-
for (const
|
|
1505
|
-
const
|
|
1523
|
+
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 = i.poster ?? "", this.poster.setSource(i), this.pauseScreen.setSource(i), this.controls.progress.setChapters([]), this.controls.progress.setThumbnails(null), this.controls.progress.setHeatmap([]), e ? this.poster.hide() : this.poster.show(), this.playlistPanel.rebuild();
|
|
1525
|
+
for (const c of Q(i.subtitles)) {
|
|
1526
|
+
const h = l("track", "", {
|
|
1506
1527
|
kind: "subtitles",
|
|
1507
|
-
src:
|
|
1508
|
-
label:
|
|
1509
|
-
...
|
|
1528
|
+
src: c.src,
|
|
1529
|
+
label: c.label,
|
|
1530
|
+
...c.srclang ? { srclang: c.srclang } : {}
|
|
1510
1531
|
});
|
|
1511
|
-
|
|
1532
|
+
c.default && h.setAttribute("default", ""), this.video.append(h);
|
|
1512
1533
|
}
|
|
1513
|
-
let n =
|
|
1514
|
-
if (
|
|
1515
|
-
const
|
|
1516
|
-
this.progressiveQuality =
|
|
1534
|
+
let n = i.src, o = i.type;
|
|
1535
|
+
if (i.qualities && i.qualities.length > 0) {
|
|
1536
|
+
const c = Math.max(0, i.qualities.findIndex((h) => h.src === i.src));
|
|
1537
|
+
this.progressiveQuality = c, n = i.qualities[c].src, o = i.qualities[c].type ?? o;
|
|
1517
1538
|
}
|
|
1518
1539
|
this.video.preload = this.adManager?.hasPendingPreRoll ? "auto" : "metadata";
|
|
1519
1540
|
const a = await ut(this.video, n, o, {
|
|
1520
1541
|
onLevels: () => {
|
|
1521
|
-
|
|
1542
|
+
s === this.loadToken && this.controls.syncFeatureButtons();
|
|
1522
1543
|
},
|
|
1523
|
-
onLevelSwitch: (
|
|
1524
|
-
onError: (
|
|
1544
|
+
onLevelSwitch: (c) => this.emitter.emit("qualitychange", { label: c }),
|
|
1545
|
+
onError: (c, h) => this.emitter.emit("error", { message: c, cause: h })
|
|
1525
1546
|
});
|
|
1526
|
-
if (
|
|
1547
|
+
if (s !== this.loadToken) {
|
|
1527
1548
|
a.destroy();
|
|
1528
1549
|
return;
|
|
1529
1550
|
}
|
|
1530
|
-
if (this.sourceController = a, this.emitter.emit("sourcechange", { source:
|
|
1531
|
-
|
|
1532
|
-
}).catch((
|
|
1533
|
-
const
|
|
1551
|
+
if (this.sourceController = a, this.emitter.emit("sourcechange", { source: i, index: t }), i.thumbnails && q.load(i.thumbnails).then((c) => {
|
|
1552
|
+
s === this.loadToken && (this.thumbTrack = c, this.controls.progress.setThumbnails(c), this.scenesPanel.visible && this.scenesPanel.rebuild());
|
|
1553
|
+
}).catch((c) => this.emitter.emit("error", { message: "Failed to load thumbnails track", cause: c })), i.chapters) {
|
|
1554
|
+
const c = (h) => {
|
|
1534
1555
|
const u = () => {
|
|
1535
|
-
|
|
1556
|
+
s === this.loadToken && (this.chapters = at(h, this.video.duration), this.controls.progress.setChapters(this.chapters), this.controls.syncFeatureButtons(), this.scenesPanel.visible && this.scenesPanel.rebuild());
|
|
1536
1557
|
};
|
|
1537
1558
|
Number.isFinite(this.video.duration) && this.video.duration > 0 ? u() : this.video.addEventListener("loadedmetadata", u, { once: !0, signal: this.abort.signal });
|
|
1538
1559
|
};
|
|
1539
|
-
typeof
|
|
1560
|
+
typeof i.chapters == "string" ? ht(i.chapters).then((h) => c(h)).catch((h) => this.emitter.emit("error", { message: "Failed to load chapters track", cause: h })) : c(i.chapters);
|
|
1540
1561
|
}
|
|
1541
|
-
if (
|
|
1542
|
-
const
|
|
1543
|
-
|
|
1562
|
+
if (i.heatmap && i.heatmap.length > 0 && this.controlsOptions.heatmap) {
|
|
1563
|
+
const c = i.heatmap, h = () => {
|
|
1564
|
+
s === this.loadToken && this.controls.progress.setHeatmap(et(c, this.video.duration));
|
|
1544
1565
|
};
|
|
1545
|
-
Number.isFinite(this.video.duration) && this.video.duration > 0 ?
|
|
1566
|
+
Number.isFinite(this.video.duration) && this.video.duration > 0 ? h() : this.video.addEventListener("loadedmetadata", h, { once: !0, signal: this.abort.signal });
|
|
1546
1567
|
}
|
|
1547
1568
|
e && this.play().catch(() => {
|
|
1548
|
-
|
|
1569
|
+
s === this.loadToken && this.poster.show();
|
|
1549
1570
|
});
|
|
1550
1571
|
}
|
|
1551
1572
|
bindVideoEvents() {
|
|
@@ -1558,8 +1579,8 @@ class _t {
|
|
|
1558
1579
|
this.handleEnded();
|
|
1559
1580
|
}, { signal: t }), e.addEventListener("timeupdate", () => {
|
|
1560
1581
|
this.emitter.emit("timeupdate", { currentTime: e.currentTime, duration: e.duration || 0 }), this.adManager?.checkMidRolls(e.currentTime);
|
|
1561
|
-
const
|
|
1562
|
-
|
|
1582
|
+
const s = K(this.chapters, e.currentTime);
|
|
1583
|
+
s !== this.currentChapter && (this.currentChapter = s, this.emitter.emit("chapterchange", { chapter: s }));
|
|
1563
1584
|
}, { signal: t }), e.addEventListener("progress", () => {
|
|
1564
1585
|
this.emitter.emit("progress", { buffered: this.bufferedEnd });
|
|
1565
1586
|
}, { signal: t }), e.addEventListener("volumechange", () => {
|
|
@@ -1567,13 +1588,29 @@ class _t {
|
|
|
1567
1588
|
}, { 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
1589
|
this.spinner.hidden = !1;
|
|
1569
1590
|
}, { signal: t });
|
|
1570
|
-
for (const
|
|
1571
|
-
e.addEventListener(
|
|
1572
|
-
this.spinner.hidden = !0;
|
|
1591
|
+
for (const s of ["playing", "canplay", "seeked"])
|
|
1592
|
+
e.addEventListener(s, () => {
|
|
1593
|
+
this.spinner.hidden = !0, this.errorBox.hidden = !0, this.decodeRecoveries = 0;
|
|
1573
1594
|
}, { signal: t });
|
|
1574
1595
|
e.addEventListener("error", () => {
|
|
1575
|
-
const
|
|
1576
|
-
|
|
1596
|
+
const s = e.error;
|
|
1597
|
+
if (!s || this.sourceController?.kind === "hls" || e.getAttribute("src") === null && !e.currentSrc) return;
|
|
1598
|
+
if ((s.code === MediaError.MEDIA_ERR_DECODE || /DEMUXER|PARSE|DECODE/i.test(s.message || "")) && this.decodeRecoveries < 2 && Number.isFinite(e.duration)) {
|
|
1599
|
+
this.decodeRecoveries++;
|
|
1600
|
+
const n = e.currentTime, o = !e.paused;
|
|
1601
|
+
e.load();
|
|
1602
|
+
const a = () => {
|
|
1603
|
+
try {
|
|
1604
|
+
e.currentTime = n;
|
|
1605
|
+
} catch {
|
|
1606
|
+
}
|
|
1607
|
+
o && e.play().catch(() => {
|
|
1608
|
+
});
|
|
1609
|
+
};
|
|
1610
|
+
e.readyState >= 1 ? a() : e.addEventListener("loadedmetadata", a, { once: !0, signal: this.abort.signal });
|
|
1611
|
+
return;
|
|
1612
|
+
}
|
|
1613
|
+
this.emitter.emit("error", { message: s.message || `Media error (code ${s.code})`, cause: s });
|
|
1577
1614
|
}, { 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
1615
|
this.emitter.emit("fullscreenchange", { active: this.isFullscreen }), this.container.classList.toggle("imp-player--fullscreen", this.isFullscreen);
|
|
1579
1616
|
}, { signal: t });
|
|
@@ -1609,11 +1646,11 @@ class _t {
|
|
|
1609
1646
|
this.container.addEventListener("keydown", (t) => {
|
|
1610
1647
|
const e = t.target;
|
|
1611
1648
|
if (e.closest("input, select, textarea, [contenteditable]")) return;
|
|
1612
|
-
const
|
|
1649
|
+
const i = e.closest("button") !== null;
|
|
1613
1650
|
switch (t.key) {
|
|
1614
1651
|
case " ":
|
|
1615
1652
|
case "k":
|
|
1616
|
-
if (t.key === " " &&
|
|
1653
|
+
if (t.key === " " && i) return;
|
|
1617
1654
|
t.preventDefault(), this.togglePlay();
|
|
1618
1655
|
break;
|
|
1619
1656
|
case "ArrowLeft":
|