@oplayer/danmuku
Version:
Danmuku plugin for oplayer
349 lines (347 loc) • 15 kB
JavaScript
import { $ as g } from "@oplayer/core";
function k(r) {
switch (r) {
case 1:
case 2:
case 3:
return 0;
case 4:
case 5:
return 1;
default:
return 0;
}
}
function x(r) {
const t = r.matchAll(/<d (?:.*? )??p="(?<p>.+?)"(?: .*?)?>(?<text>.+?)<\/d>/gs);
return Array.from(t).reduce((e, o) => {
var a, s, h, u;
const i = (s = (a = o.groups) == null ? void 0 : a.p) == null ? void 0 : s.split(","), p = (u = (h = o.groups) == null ? void 0 : h.text) == null ? void 0 : u.trim();
return i && i.length >= 8 && p ? e.concat({
text: p.replace(/"/g, '"').replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&"),
time: Number(i[0]),
mode: k(Number(i[1])),
fontSize: Number(i[2]),
color: `#${Number(i[3]).toString(16)}`,
timestamp: Number(i[4]),
pool: Number(i[5]),
userID: i[6],
rowID: Number(i[7])
}) : e;
}, []);
}
function W(r) {
return fetch(r).then((t) => t.text()).then((t) => x(t));
}
function $({
target: r,
emits: t,
clientWidth: e,
clientHeight: o,
marginBottom: i,
marginTop: p,
antiOverlap: a
}) {
var u, m;
const s = t.filter((n) => n.mode === r.mode && n.top <= o - i).sort((n, c) => n.top - c.top);
if (s.length === 0)
return p;
s.unshift({
top: 0,
left: 0,
right: 0,
height: p,
width: e,
speed: 0,
distance: e
}), s.push({
top: o - i,
left: 0,
right: 0,
height: i,
width: e,
speed: 0,
distance: e
});
for (let n = 1; n < s.length; n += 1) {
const c = s[n], l = s[n - 1], f = l.top + l.height;
if (c.top - f >= r.height)
return f;
}
const h = [];
for (let n = 1; n < s.length - 1; n += 1) {
const c = s[n];
if (h.length) {
const l = h[h.length - 1];
l[0].top === c.top ? l.push(c) : h.push([c]);
} else
h.push([c]);
}
if (a)
switch (r.mode) {
case 0: {
const n = h.find((c) => c.every((l) => e < l.distance ? !1 : r.speed < l.speed || l.right / (r.speed - l.speed) > l.time));
return n && n[0] ? n[0].top : -1;
}
case 1:
return -1;
default:
return -1;
}
else {
switch (r.mode) {
case 0:
h.sort((n, c) => {
const l = Math.min(...c.map((d) => d.right)), f = Math.min(...n.map((d) => d.right));
return l * c.length - f * n.length;
});
break;
case 1:
h.sort((n, c) => {
const l = Math.max(...c.map((d) => d.width));
return Math.max(...n.map((d) => d.width)) * n.length - l * c.length;
});
break;
}
return ((m = (u = h[0]) == null ? void 0 : u[0]) == null ? void 0 : m.top) || -1;
}
}
const y = "KGZ1bmN0aW9uKCl7InVzZSBzdHJpY3QiO2Z1bmN0aW9uIGEoe3RhcmdldDpuLGVtaXRzOmcsY2xpZW50V2lkdGg6cCxjbGllbnRIZWlnaHQ6dSxtYXJnaW5Cb3R0b206ZixtYXJnaW5Ub3A6YyxhbnRpT3ZlcmxhcDptfSl7dmFyIGwsZDtjb25zdCBvPWcuZmlsdGVyKGU9PmUubW9kZT09PW4ubW9kZSYmZS50b3A8PXUtZikuc29ydCgoZSx0KT0+ZS50b3AtdC50b3ApO2lmKG8ubGVuZ3RoPT09MClyZXR1cm4gYztvLnVuc2hpZnQoe3RvcDowLGxlZnQ6MCxyaWdodDowLGhlaWdodDpjLHdpZHRoOnAsc3BlZWQ6MCxkaXN0YW5jZTpwfSksby5wdXNoKHt0b3A6dS1mLGxlZnQ6MCxyaWdodDowLGhlaWdodDpmLHdpZHRoOnAsc3BlZWQ6MCxkaXN0YW5jZTpwfSk7Zm9yKGxldCBlPTE7ZTxvLmxlbmd0aDtlKz0xKXtjb25zdCB0PW9bZV0scz1vW2UtMV0saD1zLnRvcCtzLmhlaWdodDtpZih0LnRvcC1oPj1uLmhlaWdodClyZXR1cm4gaH1jb25zdCByPVtdO2ZvcihsZXQgZT0xO2U8by5sZW5ndGgtMTtlKz0xKXtjb25zdCB0PW9bZV07aWYoci5sZW5ndGgpe2NvbnN0IHM9cltyLmxlbmd0aC0xXTtzWzBdLnRvcD09PXQudG9wP3MucHVzaCh0KTpyLnB1c2goW3RdKX1lbHNlIHIucHVzaChbdF0pfWlmKG0pc3dpdGNoKG4ubW9kZSl7Y2FzZSAwOntjb25zdCBlPXIuZmluZCh0PT50LmV2ZXJ5KHM9PnA8cy5kaXN0YW5jZT8hMTpuLnNwZWVkPHMuc3BlZWR8fHMucmlnaHQvKG4uc3BlZWQtcy5zcGVlZCk+cy50aW1lKSk7cmV0dXJuIGUmJmVbMF0/ZVswXS50b3A6LTF9Y2FzZSAxOnJldHVybi0xO2RlZmF1bHQ6cmV0dXJuLTF9ZWxzZXtzd2l0Y2gobi5tb2RlKXtjYXNlIDA6ci5zb3J0KChlLHQpPT57Y29uc3Qgcz1NYXRoLm1pbiguLi50Lm1hcChpPT5pLnJpZ2h0KSksaD1NYXRoLm1pbiguLi5lLm1hcChpPT5pLnJpZ2h0KSk7cmV0dXJuIHMqdC5sZW5ndGgtaCplLmxlbmd0aH0pO2JyZWFrO2Nhc2UgMTpyLnNvcnQoKGUsdCk9Pntjb25zdCBzPU1hdGgubWF4KC4uLnQubWFwKGk9Pmkud2lkdGgpKTtyZXR1cm4gTWF0aC5tYXgoLi4uZS5tYXAoaT0+aS53aWR0aCkpKmUubGVuZ3RoLXMqdC5sZW5ndGh9KTticmVha31yZXR1cm4oKGQ9KGw9clswXSk9PW51bGw/dm9pZCAwOmxbMF0pPT1udWxsP3ZvaWQgMDpkLnRvcCl8fC0xfX1zZWxmLm9ubWVzc2FnZT0oe2RhdGE6bn0pPT57c2VsZi5wb3N0TWVzc2FnZSh7dG9wOmEobiksaWQ6bi5pZH0pfX0pKCk7Ci8vIyBzb3VyY2VNYXBwaW5nVVJMPWFzc2V0cy9kYW5tdWt1Lndvcmtlci5lNTVjMzgzYS5qcy5tYXA=", b = typeof window < "u" && window.Blob && new Blob([atob(y)], { type: "text/javascript;charset=utf-8" });
function Z() {
const r = b && (window.URL || window.webkitURL).createObjectURL(b);
try {
return r ? new Worker(r) : new Worker("data:application/javascript;base64," + y);
} finally {
r && (window.URL || window.webkitURL).revokeObjectURL(r);
}
}
const T = g.css`
position: absolute;
white-space: pre;
pointer-events: none;
perspective: 500px;
will-change: transform, top;
line-height: 1.125;
text-shadow: rgb(0 0 0) 1px 0px 1px, rgb(0 0 0) 0px 1px 1px, rgb(0 0 0) 0px -1px 1px, rgb(0 0 0) -1px 0px 1px;
`;
class w {
constructor(t, e) {
this.player = t, this.isStop = !1, this.isHide = !1, this.timer = null, this.queue = [], this.$refs = [], this.$player = t.$root, this.$danmuku = g.create(
`div.${g.css`width: 100%; height: 100%; position: absolute; left: 0; top: 0; pointer-events: none;`}`
), this.options = Object.assign(
{
speed: 5,
color: "#fff",
mode: 0,
margin: [2, 2],
antiOverlap: !0,
useWorker: !0,
synchronousPlayback: !0
},
e
), e.useWorker && (this.worker = new Z(), this.worker.addEventListener("error", (o) => {
t.emit("notice", "danmuku-worker:" + o.message);
})), t.on(["play", "playing"], this.start.bind(this)), t.on(["pause", "waiting"], this.stop.bind(this)), t.on(["fullscreen", "webfullscreen", "seeking"], this.reset.bind(this)), t.on("destroy", this.destroy.bind(this)), this.fetch(), g.render(this.$danmuku, this.$player);
}
async fetch() {
try {
let t = [];
typeof this.options.source == "function" ? t = await this.options.source() : typeof this.options.source == "string" ? t = await W(this.options.source) : t = this.options.source, this.player.emit("loadeddanmuku", t), this.load(t);
} catch (t) {
throw this.player.emit("notice", { text: "danmuku: " + t.message }), t;
}
}
load(t) {
this.queue = [], this.$danmuku.innerHTML = "", t.sort((e, o) => e.time - o.time).forEach((e) => {
var o;
((o = this.options) == null ? void 0 : o.filter) && this.options.filter(e) || this.queue.push({
color: this.options.color,
status: "wait",
$ref: null,
restTime: 0,
lastTime: 0,
...e
});
});
}
start() {
this.isStop = !1, this.continue(), this.update(), this.player.emit("danmuku:start");
}
update() {
this.timer = window.requestAnimationFrame(async () => {
var t, e;
if (this.player.isPlaying && !this.isHide && this.queue.length) {
this.mapping("emit", (a) => {
a.restTime -= (Date.now() - a.lastTime) / 1e3, a.lastTime = Date.now(), a.restTime <= 0 && this.makeWait(a);
});
const o = this.getReady(), { clientWidth: i, clientHeight: p } = this.$player;
for (let a = 0; a < o.length; a++) {
const s = o[a];
s.$ref = this.createItem({
text: s.text,
cssText: `left: ${i}px;
${this.options.opacity ? `opacity: ${this.options.opacity};` : ""}
${this.options.fontSize ? `font-size: ${this.options.fontSize}px;` : ""}
${s.color ? `color: ${s.color};` : ""},
${this.options.fontSize ? `font-size: ${this.options.fontSize}px;` : ""}
${s.border ? `border: 1px solid ${s.color}; background-color: rgb(0 0 0 / 50%);` : ""}`
}), this.$danmuku.appendChild(s.$ref), s.lastTime = Date.now(), s.restTime = this.options.synchronousPlayback && this.player.playbackRate ? this.options.speed / this.player.playbackRate : this.options.speed;
const h = this.getActiveDanmukusBoundingClientRect(), u = {
mode: s.mode,
height: s.$ref.clientHeight,
speed: (i + s.$ref.clientWidth) / s.restTime
};
await this.postMessage({
target: u,
emits: h,
clientWidth: i,
clientHeight: p,
antiOverlap: this.options.antiOverlap,
marginTop: ((t = this.options.margin) == null ? void 0 : t[0]) || 0,
marginBottom: ((e = this.options.margin) == null ? void 0 : e[1]) || 50
}).then(({ top: m }) => {
if (!this.isStop && m != -1)
switch (s.status = "emit", s.$ref.style.opacity = "1", s.$ref.style.top = `${m}px`, s.mode) {
case 0: {
const n = i + s.$ref.clientWidth;
s.$ref.style.transform = `translate3d(${-n}px, 0, 0)`, s.$ref.style.transition = `transform ${s.restTime}s linear 0s`;
break;
}
case 1:
s.$ref.style.left = "50%", s.$ref.style.transform = "translate3d(-50%, 0, 0)";
break;
}
else
s.status = "ready", this.$refs.push(s.$ref), s.$ref = null;
});
}
this.isStop || this.update();
}
});
}
continue() {
const { clientWidth: t } = this.$player;
this.mapping("stop", (e) => {
switch (e.status = "emit", e.lastTime = Date.now(), e.mode) {
case 0: {
const o = t + e.$ref.clientWidth;
e.$ref.style.transform = `translate3d(${-o}px, 0, 0)`, e.$ref.style.transition = `transform ${e.restTime}s linear 0s`;
break;
}
}
});
}
suspend() {
const { clientWidth: t } = this.$player;
this.mapping("emit", (e) => {
switch (e.status = "stop", e.mode) {
case 0: {
const o = t - (this.getLeft(e.$ref) - this.getLeft(this.$player));
e.$ref.style.transform = `translate3d(${-o}px, 0, 0)`, e.$ref.style.transition = "transform 0s linear 0s";
break;
}
}
});
}
mapping(t, e) {
this.queue.forEach((o) => o.status === t && e(o));
}
getLeft(t) {
return t.getBoundingClientRect().left;
}
createItem({ text: t, cssText: e }) {
const o = this.$refs.pop();
if (o)
return o;
const i = document.createElement("div");
return i.className = T, i.innerText = t, i.style.cssText = e, i;
}
getReady() {
const { currentTime: t } = this.player;
return this.queue.filter((e) => e.status === "ready" || e.status === "wait" && t + 0.1 >= e.time && e.time >= t - 0.1);
}
getActiveDanmukusBoundingClientRect() {
const t = [], { clientWidth: e } = this.$player, o = this.getLeft(this.$player);
return this.mapping("emit", (i) => {
const p = i.$ref.offsetTop, a = this.getLeft(i.$ref) - o, s = i.$ref.clientHeight, h = i.$ref.clientWidth, u = a + h, m = e - u, n = u / i.restTime;
t.push({
top: p,
left: a,
height: s,
width: h,
right: m,
speed: n,
distance: u,
time: i.restTime,
mode: i.mode
});
}), t;
}
postMessage(t = {}) {
return new Promise((e) => {
if (this.options.useWorker && this.worker && this.worker.postMessage)
t.id = Date.now(), this.worker.onmessage = ({ data: o }) => {
o.id === t.id && e(o);
}, this.worker.postMessage(t);
else {
const o = $(t);
e({ top: o });
}
});
}
makeWait(t) {
t.status = "wait", t.$ref && (t.$ref.style.opacity = "0", t.$ref.style.transform = "translate3d(0, 0, 0)", t.$ref.style.transition = "transform 0s linear 0s", this.$refs.push(t.$ref), t.$ref = null);
}
reset() {
this.queue.forEach((t) => this.makeWait(t));
}
emit(t) {
this.queue.push({
...t,
status: "wait",
$ref: null,
restTime: 0,
lastTime: 0
});
}
stop() {
this.isStop = !0, this.suspend(), window.cancelAnimationFrame(this.timer), this.player.emit("danmuku:stop");
}
show() {
this.isHide = !1, this.start(), this.$danmuku.style.display = "block", this.player.emit("danmuku:show");
}
hide() {
this.isHide = !0, this.stop(), this.queue.forEach((t) => this.makeWait(t)), this.$danmuku.style.display = "none", this.player.emit("danmuku:hide");
}
destroy() {
var t, e;
this.stop(), (e = (t = this.worker) == null ? void 0 : t.terminate) == null || e.call(t), this.$danmuku.remove(), this.player.emit("danmuku:destroy");
}
}
const L = '<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1659511978567" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2218" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M853.333333 170.666667H170.666667c-46.933333 0-85.333333 38.4-85.333334 85.333333v512c0 46.933333 38.4 85.333333 85.333334 85.333333h682.666666c46.933333 0 85.333333-38.4 85.333334-85.333333V256c0-46.933333-38.4-85.333333-85.333334-85.333333zM213.333333 512h85.333334c23.466667 0 42.666667 19.2 42.666666 42.666667s-19.2 42.666667-42.666666 42.666666H213.333333c-23.466667 0-42.666667-19.2-42.666666-42.666666s19.2-42.666667 42.666666-42.666667z m341.333334 256H213.333333c-23.466667 0-42.666667-19.2-42.666666-42.666667s19.2-42.666667 42.666666-42.666666h341.333334c23.466667 0 42.666667 19.2 42.666666 42.666666s-19.2 42.666667-42.666666 42.666667z m256 0h-85.333334c-23.466667 0-42.666667-19.2-42.666666-42.666667s19.2-42.666667 42.666666-42.666666h85.333334c23.466667 0 42.666667 19.2 42.666666 42.666666s-19.2 42.666667-42.666666 42.666667z m0-170.666667h-341.333334c-23.466667 0-42.666667-19.2-42.666666-42.666666s19.2-42.666667 42.666666-42.666667h341.333334c23.466667 0 42.666667 19.2 42.666666 42.666667s-19.2 42.666667-42.666666 42.666666z"></path></svg>', C = (r) => ({
name: "oplayer-plugin-danmuku",
apply: (t) => {
let e = new w(t, r);
const o = () => {
t.emit("addsetting", {
name: "Danmuku",
type: "switcher",
default: !0,
key: "danmuku",
icon: L,
onChange: (i, { isInit: p } = {}) => {
i ? (!p && t.emit("notice", { text: "Show danmuku" }), e == null || e.show()) : (!p && t.emit("notice", { text: "Hide danmuku" }), e == null || e.hide());
}
});
};
t.on("loadedsetting", o), t.on("danmukusourcechange", ({ payload: i }) => {
t.emit("removesetting", "danmuku"), o(), e = new w(t, { ...r, ...i, source: i.source });
}), t.on("videosourcechange", function() {
e == null || e.destroy(), e = null, t.emit("removesetting", "danmuku");
});
}
});
export {
C as default
};
//# sourceMappingURL=index.es.js.map