Ce script ne devrait pas être installé directement. C'est une librairie créée pour d'autres scripts. Elle doit être inclus avec la commande // @require https://update.greasyfork.org/scripts/489648/1341736/dpaaaa.js
window.dpPlugins =
window.dpPlugins ||
(function (e) {
var t = {
version: "1.1.5",
init: function (s, i) {
(t = Object.assign(i || {}, t)),
window.m3u8Parser ||
t.loadJs(
"https://cdn.staticfile.org/m3u8-parser/7.1.0/m3u8-parser.min.js"
),
window.localforage ||
t.loadJs(
"https://cdn.staticfile.org/localforage/1.10.0/localforage.min.js"
),
t.ready(s).then(() => {
e.forEach((e) => {
const i = new e(s, t);
s.plugins[i.constructor.name] = i;
});
});
},
ready: function (e) {
return new Promise(function (t, s) {
e.isReady
? t()
: e.video.duration > 0 || e.video.readyState > 2
? ((e.isReady = !0), t())
: (e.video.ondurationchange = function () {
(e.video.ondurationchange = null), (e.isReady = !0), t();
});
});
},
query: function (e, t = document) {
return t.querySelector(e);
},
queryAll: function (e, t = document) {
return t.querySelectorAll(e);
},
append: function (e, t) {
return (
t instanceof Element
? Node.prototype.appendChild.call(e, t)
: e.insertAdjacentHTML("beforeend", String(t)),
e.lastElementChild || e.lastChild
);
},
prepend: function (e, t) {
return (
t instanceof Element
? Node.prototype.insertBefore.call(
e,
t,
e.firstElementChild || e.firstChild
)
: e.insertAdjacentHTML("afterbegin", String(t)),
e.firstElementChild || e.firstChild
);
},
insertAfter: function (e, s) {
var i = e.parentNode;
return i.lastChild == e
? t.append(i, s)
: ((s = t.append(i, s)),
Node.prototype.insertBefore.call(i, s, e.nextSibling));
},
remove: function (e) {
return Node.prototype.removeChild.call(e?.parentNode || document, e);
},
setStyle: function (e, t, s) {
if ("object" == typeof t) {
for (const s in t) e.style[s] = t[s];
return e;
}
return (e.style[t] = s), e;
},
};
return (
console.info(
"\n %c dpPlugins v" +
t.version +
" %c https://scriptcat.org/zh-CN/users/13895 \n",
"color: #fadfa3; background: #030307; padding:5px 0;",
"background: #fadfa3; padding:5px 0;"
),
t
);
})([
class HlsEvents {
constructor(e) {
(this.player = e),
(this.hls = this.player.plugins.hls),
(this.now = Date.now()),
(this.currentTime = 0),
(this.fragLoadError = 0),
this.player.events.type("video_end") ||
this.player.events.playerEvents.push("video_end"),
this.player.on("video_end", () => {
this.switchUrl();
}),
this.player.on("quality_end", () => {
this.hls &&
(this.hls.destroy(),
(this.hls = this.player.plugins.hls),
localStorage.setItem(
"dplayer-defaultQuality",
this.player.quality.name
),
this.onEvents());
}),
this.player.on("destroy", () => {
this.hls && this.hls.destroy();
}),
this.onEvents();
}
onEvents() {
if (this.hls) {
const e = window.Hls || unsafeWindow.Hls;
this.hls.on(e.Events.ERROR, (t, s) => {
if (
(this.hls.media.currentTime > 0 &&
(this.currentTime = this.hls.media.currentTime),
s.fatal)
)
switch (
(this.player.notice(
`当前带宽: ${
Math.round(
(this.hls.bandwidthEstimate / 1024 / 1024 / 8) * 100
) / 100
} MB/s`
),
s.type)
) {
case e.ErrorTypes.NETWORK_ERROR:
s.details === e.ErrorDetails.MANIFEST_LOAD_ERROR ||
s.details === e.ErrorDetails.MANIFEST_LOAD_TIMEOUT ||
s.details === e.ErrorDetails.MANIFEST_PARSING_ERROR
? this.hls.loadSource(this.hls.url)
: s.details === e.ErrorDetails.FRAG_LOAD_ERROR
? this.fragLoadError < 10 &&
(this.fragLoadError++,
this.hls.loadSource(this.hls.url),
(this.hls.media.currentTime = this.currentTime),
this.hls.media.play())
: this.hls.startLoad();
break;
case e.ErrorTypes.MEDIA_ERROR:
this.hls.recoverMediaError();
break;
default:
this.player.notice("视频播放异常,请刷新重试"),
this.hls.destroy();
}
else if (s.type === e.ErrorTypes.NETWORK_ERROR)
if (
s.details === e.ErrorDetails.FRAG_LOAD_ERROR &&
this.isUrlExpires(this.hls.url)
)
return (
(this.fragLoadError = 0),
(this.now = Date.now()),
this.hls.stopLoad(),
this.player.plugins?.Appreciation.isAppreciation()
.catch((e) =>
Object.keys(localStorage).forEach(
(e) => e.startsWith("dp") && localStorage.removeItem(e)
)
)
.finally(() => {
this.player.events.trigger("video_start");
})
);
});
}
}
switchUrl() {
if (
this.hls &&
this.hls.hasOwnProperty("data") &&
this.hls.levelController &&
!this.hls.levelController.currentLevelIndex
) {
const e = (this.hls.url = this.player.quality.url);
fetch(e)
.then((e) => (e.ok ? e.text() : Promise.reject()))
.then((t) => {
const s = new (
window.m3u8Parser || unsafeWindow.m3u8Parser
).Parser();
s.push(t), s.end();
const i = e.replace(/media.m3u8.+/, ""),
a = s.manifest.segments;
this.hls.bufferController.details.fragments.forEach(function (
t,
s
) {
const l = a[s];
Object.assign(t, { baseurl: e, relurl: l.uri, url: i + l.uri });
}),
this.hls.startLoad(this.player.video.currentTime);
});
}
}
isUrlExpires(e) {
var t =
arguments.length > 1 && void 0 !== arguments[1]
? arguments[1]
: 6e3,
s = e.match(/&x-oss-expires=(\d+)&/);
return s
? +"".concat(s[1], "000") - t < Date.now()
: Date.now() - this.now > 3e5 - t;
}
},
class ImageEnhancer {
constructor(e, t) {
(this.player = e),
Object.assign(this.player.user.storageName, {
imageenhancer: "dplayer-imageenhancer",
}),
Object.assign(this.player.user.default, { imageenhancer: 0 }),
this.player.user.init(),
(this.imageenhancer = this.player.user.get("imageenhancer")),
this.imageenhancer &&
(this.player.video.style.filter =
"contrast(1.01) brightness(1.05) saturate(1.1)"),
(this.player.template.imageEnhancer = t.append(
t.query(
".dplayer-setting-origin-panel",
this.player.template.settingBox
),
'<div class="dplayer-setting-item dplayer-setting-imageenhancer"><span class="dplayer-label">画质增强</span><div class="dplayer-toggle"><input class="dplayer-toggle-setting-input" type="checkbox" name="dplayer-toggle"><label for="dplayer-toggle"></label></div></div>'
)),
(this.player.template.imageEnhancerToggle = t.query(
"input",
this.player.template.imageEnhancer
)),
(this.player.template.imageEnhancerToggle.checked =
this.imageenhancer),
this.player.template.imageEnhancer.addEventListener("click", () => {
(this.imageenhancer =
this.player.template.imageEnhancerToggle.checked =
!this.player.template.imageEnhancerToggle.checked),
this.player.user.set("imageenhancer", Number(this.imageenhancer)),
(this.player.video.style.filter = this.imageenhancer
? "contrast(1.01) brightness(1.05) saturate(1.1)"
: ""),
this.player.notice(
"画质增强: " + (this.imageenhancer ? "开启" : "关闭")
);
}),
this.player.on("playing", () => {
this.imageenhancer &&
(this.player.video.style.filter =
"contrast(1.01) brightness(1.05) saturate(1.1)");
});
}
},
class SoundEnhancer {
constructor(e, t) {
(this.player = e),
(this.Joysound = window.Joysound || unsafeWindow.Joysound),
(this.localforage = window.localforage || unsafeWindow.localforage),
(this.joySound = null),
(this.offset = null),
Object.assign(this.player.user.storageName, {
soundenhancer: "dplayer-soundenhancer",
volumeenhancer: "dplayer-volumeenhancer",
}),
Object.assign(this.player.user.default, {
soundenhancer: 0,
volumeenhancer: 0,
}),
this.player.user.init(),
(this.player.template.soundEnhancer = t.append(
t.query(
".dplayer-setting-origin-panel",
this.player.template.settingBox
),
'<div class="dplayer-setting-item dplayer-setting-soundenhancer"><span class="dplayer-label">音质增强</span><div class="dplayer-toggle"><input class="dplayer-toggle-setting-input" type="checkbox" name="dplayer-toggle"><label for="dplayer-toggle"></label></div></div>'
)),
(this.player.template.soundEnhancerToggle = t.query(
"input",
this.player.template.soundEnhancer
)),
(this.player.template.soundEnhancerToggle.checked =
!!this.player.user.get("soundenhancer")),
this.player.template.soundEnhancer.addEventListener("click", () => {
let e = (this.player.template.soundEnhancerToggle.checked =
!this.player.template.soundEnhancerToggle.checked);
this.player.user.set("soundenhancer", Number(e)),
this.switchJoysound(e);
}),
(this.player.template.gainBox = t.prepend(
t.query(
".dplayer-setting-origin-panel",
this.player.template.settingBox
),
'<div class="dplayer-setting-item dplayer-setting-danmaku dplayer-setting-gain" style="display: block;"><span class="dplayer-label">音量增强</span><div class="dplayer-danmaku-bar-wrap dplayer-gain-bar-wrap"><div class="dplayer-danmaku-bar dplayer-gain-bar"><div class="dplayer-danmaku-bar-inner dplayer-gain-bar-inner" style="width: 0%;"><span class="dplayer-thumb"></span></div></div></div></div>'
)),
(this.player.template.gainBarWrap =
this.player.template.gainBox.querySelector(
".dplayer-gain-bar-wrap"
)),
(this.player.bar.elements.gain =
this.player.template.gainBox.querySelector(
".dplayer-gain-bar-inner"
));
const s = (e) => {
const t = e || window.event;
let s =
((t.clientX || t.changedTouches[0].clientX) -
this.getElementViewLeft(this.player.template.gainBarWrap)) /
130;
this.switchGainValue(s);
},
i = () => {
document.removeEventListener("touchend", i),
document.removeEventListener("touchmove", s),
document.removeEventListener("mouseup", i),
document.removeEventListener("mousemove", s),
this.player.template.gainBox.classList.remove(
"dplayer-setting-danmaku-active"
);
};
this.player.template.gainBarWrap.addEventListener("click", (e) => {
const t = e || window.event;
let s =
((t.clientX || t.changedTouches[0].clientX) -
this.getElementViewLeft(this.player.template.gainBarWrap)) /
130;
this.switchGainValue(s);
}),
this.player.template.gainBarWrap.addEventListener(
"touchstart",
() => {
document.addEventListener("touchmove", s),
document.addEventListener("touchend", i),
this.player.template.gainBox.classList.add(
"dplayer-setting-danmaku-active"
);
}
),
this.player.template.gainBarWrap.addEventListener("mousedown", () => {
document.addEventListener("mousemove", s),
document.addEventListener("mouseup", i),
this.player.template.gainBox.classList.add(
"dplayer-setting-danmaku-active"
);
}),
this.player.on("playing", () => {
this.localforage.getItem("playing").then((e) => {
(e = e || 0),
++e < 1e3 &&
((this.player.plugins.hls.data = !0),
this.localforage.setItem("playing", e));
}),
this.player.video.joySound || this.init();
});
}
init() {
if (this.Joysound && this.Joysound.isSupport()) {
(this.joySound = new this.Joysound()),
this.joySound.init(this.player.video),
(this.player.video.joySound = !0);
let e = this.player.user.get("soundenhancer");
e && this.switchJoysound(e);
let t = this.player.user.get("volumeenhancer");
t && this.switchGainValue(t);
}
}
switchJoysound(e) {
this.joySound
? (this.joySound.setEnabled(e),
this.player.notice("音质增强: " + (e ? "开启" : "关闭")))
: this.player.notice("Joysound 未完成初始化");
}
switchGainValue(e) {
this.joySound
? ((e = Math.min(Math.max(e, 0), 1)),
this.player.bar.set("gain", e, "width"),
this.player.user.set("volumeenhancer", e),
this.joySound.setVolume(e),
this.player.notice(`音量增强: ${100 + Math.floor(100 * e)}%`))
: this.player.notice("Joysound 未完成初始化");
}
getElementViewLeft(e) {
const t =
window.scrollY ||
window.pageYOffset ||
document.body.scrollTop +
((document.documentElement && document.documentElement.scrollTop) ||
0);
if (e.getBoundingClientRect) {
if ("number" != typeof this.offset) {
let e = document.createElement("div");
(e.style.cssText = "position:absolute;top:0;left:0;"),
document.body.appendChild(e),
(this.offset = -e.getBoundingClientRect().top - t),
document.body.removeChild(e),
(e = null);
}
return e.getBoundingClientRect().left + this.offset;
}
{
let t = e.offsetLeft,
s = e.offsetParent;
const i =
document.body.scrollLeft + document.documentElement.scrollLeft;
if (
document.fullscreenElement ||
document.mozFullScreenElement ||
document.webkitFullscreenElement
)
for (; null !== s && s !== e; )
(t += s.offsetLeft), (s = s.offsetParent);
else for (; null !== s; ) (t += s.offsetLeft), (s = s.offsetParent);
return t - i;
}
}
},
class AspectRatio {
constructor(e) {
(this.player = e),
(this.value = null),
this.player.template.controller
.querySelector(".dplayer-icons-right")
.insertAdjacentHTML(
"afterbegin",
'<div class="dplayer-quality dplayer-aspectRatio"><button class="dplayer-icon dplayer-quality-icon">画面比例</button><div class="dplayer-quality-mask"><div class="dplayer-quality-list dplayer-aspectRatio-list"><div class="dplayer-quality-item" data-value="none">原始比例</div><div class="dplayer-quality-item" data-value="cover">自动裁剪</div><div class="dplayer-quality-item" data-value="fill">拉伸填充</div><div class="dplayer-quality-item" data-value="">系统默认</div></div></div></div>'
),
(this.player.template.aspectRatioButton =
this.player.template.controller.querySelector(
".dplayer-aspectRatio button"
)),
(this.player.template.aspectRatioList =
this.player.template.controller.querySelector(
".dplayer-aspectRatio-list"
)),
this.player.template.aspectRatioList.addEventListener(
"click",
(e) => {
e.target.classList.contains("dplayer-quality-item") &&
((this.value = e.target.dataset.value),
(this.player.video.style["object-fit"] =
e.target.dataset.value),
(this.player.template.aspectRatioButton.innerText =
e.target.innerText));
}
),
this.player.on("playing", () => {
this.value && (this.player.video.style["object-fit"] = this.value);
});
}
},
class SelectEpisode {
constructor(e, t) {
(this.player = e),
Array.isArray(this.player.options.fileList) &&
this.player.options.fileList.length > 1 &&
this.player.options.file &&
(this.player.events.type("episode_end") ||
this.player.events.playerEvents.push("episode_end"),
this.player.on("episode_end", () => {
this.switchVideo();
}),
(this.player.fileIndex = (
this.player.options.fileList || []
).findIndex(
(e, t) => e.file_id === this.player.options.file.file_id
)),
t.prepend(
this.player.template.controller.querySelector(
".dplayer-icons-right"
),
'<style>.episode .content{max-width: 360px;max-height: 330px;width: auto;height: auto;box-sizing: border-box;overflow: hidden auto;position: absolute;left: 0px;transition: all 0.38s ease-in-out 0s;bottom: 52px;transform: scale(0);z-index: 2;}.episode .content .list{background-color: rgba(0,0,0,.3);height: 100%;}.episode .content .video-item{color: #fff;cursor: pointer;font-size: 14px;line-height: 35px;overflow: hidden;padding: 0 10px;text-overflow: ellipsis;text-align: center;white-space: nowrap;}.episode .content .active{background-color: rgba(0,0,0,.3);color: #0df;}</style><div class="dplayer-quality episode"><button class="dplayer-icon prev-icon" title="上一集"><svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M757.527273 190.138182L382.510545 490.123636a28.020364 28.020364 0 0 0 0 43.752728l375.016728 299.985454a28.020364 28.020364 0 0 0 45.474909-21.876363V212.014545a28.020364 28.020364 0 0 0-45.474909-21.876363zM249.949091 221.509818a28.020364 28.020364 0 0 0-27.973818 27.973818v525.032728a28.020364 28.020364 0 1 0 55.994182 0V249.483636a28.020364 28.020364 0 0 0-28.020364-27.973818zM747.054545 270.242909v483.514182L444.834909 512l302.173091-241.757091z"></path></svg></button><button class="dplayer-icon dplayer-quality-icon episode-icon">选集</button><button class="dplayer-icon next-icon" title="下一集"><svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M248.506182 190.138182l374.970182 299.985454a28.020364 28.020364 0 0 1 0 43.752728L248.552727 833.861818a28.020364 28.020364 0 0 1-45.521454-21.876363V212.014545c0-23.505455 27.182545-36.538182 45.521454-21.876363z m507.485091 31.371636c15.453091 0 28.020364 12.567273 28.020363 27.973818v525.032728a28.020364 28.020364 0 1 1-55.994181 0V249.483636c0-15.453091 12.520727-27.973818 27.973818-27.973818zM258.978909 270.242909v483.514182L561.198545 512 258.978909 270.242909z"></path></svg></button><div class="content"><div class="list"></div></div></div>'
),
(this.player.template.episodeButton =
this.player.template.controller.querySelector(
".episode .episode-icon"
)),
(this.player.template.episodePrevButton =
this.player.template.controller.querySelector(
".episode .prev-icon"
)),
(this.player.template.episodeNextButton =
this.player.template.controller.querySelector(
".episode .next-icon"
)),
(this.player.template.episodeContent =
this.player.template.controller.querySelector(
".episode .content"
)),
(this.player.template.episodeList =
this.player.template.controller.querySelector(".episode .list")),
this.player.options.fileList.forEach((e, s) => {
t.append(
this.player.template.episodeList,
'<div class="video-item" data-index="' +
s +
'" title="' +
e.name +
'">' +
e.name +
"</div>"
);
}),
(this.player.template.episodeVideoItems =
this.player.template.controller.querySelectorAll(
".episode .video-item"
)),
this.player.template.episodeVideoItems.length &&
this.player.fileIndex >= 0 &&
this.player.template.episodeVideoItems[
this.player.fileIndex
].classList.add("active"),
this.player.template.mask.addEventListener("click", () => {
this.hide();
}),
this.player.template.episodeButton.addEventListener(
"click",
(e) => {
"scale(1)" ===
this.player.template.episodeContent.style.transform
? this.hide()
: this.show();
}
),
this.player.template.episodeList.addEventListener("click", (e) => {
e.target.classList.contains("video-item") &&
!e.target.classList.contains("active") &&
(this.player.template.episodeVideoItems[
this.player.fileIndex
].classList.remove("active"),
e.target.classList.add("active"),
(this.player.fileIndex = 1 * e.target.dataset.index),
(this.player.options.file =
this.player.options.fileList[this.player.fileIndex]),
this.hide(),
this.player.events.trigger("episode_start"),
this.player.notice(
"准备播放:" + this.player.options.file.name,
5e3
));
}),
this.player.template.episodePrevButton.addEventListener(
"click",
(e) => {
const t = this.player.fileIndex - 1;
t >= 0
? (this.player.template.episodeVideoItems[
this.player.fileIndex
].classList.remove("active"),
this.player.template.episodeVideoItems[t].classList.add(
"active"
),
(this.player.fileIndex = t),
(this.player.options.file =
this.player.options.fileList[this.player.fileIndex]),
this.hide(),
this.player.events.trigger("episode_start"),
this.player.notice(
"准备播放:" + this.player.options.file.name,
5e3
))
: this.player.notice("没有上一集了");
}
),
this.player.template.episodeNextButton.addEventListener(
"click",
(e) => {
const t = this.player.fileIndex + 1;
t <= this.player.options.fileList.length - 1
? (this.player.template.episodeVideoItems[
this.player.fileIndex
].classList.remove("active"),
this.player.template.episodeVideoItems[t].classList.add(
"active"
),
(this.player.fileIndex = t),
(this.player.options.file =
this.player.options.fileList[this.player.fileIndex]),
this.hide(),
this.player.events.trigger("episode_start"),
this.player.notice(
"准备播放:" + this.player.options.file.name,
5e3
))
: this.player.notice("没有下一集了");
}
));
}
switchVideo() {
this.player.switchVideo({ url: this.player.quality.url, type: "hls" }),
(this.player.video.oncanplay = () => {
(this.player.video.oncanplay = null),
this.player.play(),
this.player.events.trigger("quality_end");
});
}
show() {
(this.player.template.episodeContent.style.transform = "scale(1)"),
this.player.template.mask.classList.add("dplayer-mask-show");
}
hide() {
(this.player.template.episodeContent.style.transform = "scale(0)"),
this.player.template.mask.classList.remove("dplayer-mask-show");
}
},
class AutoNextEpisode {
constructor(e, t) {
(this.player = e),
Object.assign(this.player.user.storageName, {
autonextepisode: "dplayer-autonextepisode",
}),
Object.assign(this.player.user.default, { autonextepisode: 0 }),
this.player.user.init(),
(this.autonextepisode = this.player.user.get("autonextepisode")),
(this.player.template.autoNextEpisode = t.append(
t.query(
".dplayer-setting-origin-panel",
this.player.template.settingBox
),
'<div class="dplayer-setting-item dplayer-setting-autonextepisode"><span class="dplayer-label">自动下一集</span><div class="dplayer-toggle"><input class="dplayer-toggle-setting-input" type="checkbox" name="dplayer-toggle"><label for="dplayer-toggle"></label></div></div>'
)),
(this.player.template.autoNextEpisodeToggle = t.query(
"input",
this.player.template.autoNextEpisode
)),
(this.player.template.autoNextEpisodeToggle.checked =
this.autonextepisode),
this.player.template.autoNextEpisode.addEventListener("click", () => {
(this.autonextepisode =
this.player.template.autoNextEpisodeToggle.checked =
!this.player.template.autoNextEpisodeToggle.checked),
this.player.user.set(
"autonextepisode",
Number(this.autonextepisode)
),
this.player.notice(
"自动播放下集: " + (this.autonextepisode ? "开启" : "关闭")
);
}),
this.player.on("ended", () => {
this.autonextepisode &&
this.player.template.episodeNextButton &&
this.player.template.episodeNextButton.click();
});
}
},
class MemoryPlay {
constructor(e, t) {
(this.player = e),
(this.file_id = this.player.options?.file?.file_id),
(this.hasMemoryDisplay = !1),
Object.assign(this.player.user.storageName, {
automemoryplay: "dplayer-automemoryplay",
}),
Object.assign(this.player.user.default, { automemoryplay: 0 }),
this.player.user.init(),
(this.automemoryplay = this.player.user.get("automemoryplay")),
(this.player.template.autoMemoryPlay = t.append(
t.query(
".dplayer-setting-origin-panel",
this.player.template.settingBox
),
'<div class="dplayer-setting-item dplayer-setting-automemoryplay"><span class="dplayer-label">自动记忆播放</span><div class="dplayer-toggle"><input class="dplayer-toggle-setting-input" type="checkbox" name="dplayer-toggle"><label for="dplayer-toggle"></label></div></div>'
)),
(this.player.template.autoMemoryPlayToggle = t.query(
"input",
this.player.template.autoMemoryPlay
)),
(this.player.template.autoMemoryPlayToggle.checked =
this.automemoryplay),
this.player.template.autoMemoryPlay.addEventListener("click", () => {
(this.automemoryplay =
this.player.template.autoMemoryPlayToggle.checked =
!this.player.template.autoMemoryPlayToggle.checked),
this.player.user.set(
"automemoryplay",
Number(this.automemoryplay)
),
this.player.notice(
"自动记忆播放: " + (this.automemoryplay ? "开启" : "关闭")
);
}),
this.player.on("quality_end", () => {
this.file_id !== this.player.options?.file?.file_id &&
((this.file_id = this.player.options?.file?.file_id),
(this.hasMemoryDisplay = !1)),
this.run();
}),
(document.onvisibilitychange = () => {
if ("hidden" === document.visibilityState) {
const {
video: { currentTime: e, duration: t },
} = this.player;
this.setTime(this.file_id, e, t);
}
}),
(window.onbeforeunload = () => {
const {
video: { currentTime: e, duration: t },
} = this.player;
this.setTime(this.file_id, e, t);
}),
this.run();
}
run() {
if (!1 === this.hasMemoryDisplay) {
this.hasMemoryDisplay = !0;
const {
video: { currentTime: e, duration: t },
} = this.player,
s = this.getTime(this.file_id);
if (s && s > e)
if (this.automemoryplay)
this.player.seek(s),
this.player.video.paused && this.player.play();
else {
const e = this.formatTime(s);
let t = document.createElement("div");
t.setAttribute("class", "memory-play-wrap"),
t.setAttribute(
"style",
"display: block;position: absolute;left: 33px;bottom: 66px;font-size: 15px;padding: 7px;border-radius: 3px;color: #fff;z-index:100;background: rgba(0,0,0,.5);"
),
(t.innerHTML =
"上次播放到:" +
e +
' <a href="javascript:void(0);" class="play-jump" style="text-decoration: none;color: #06c;"> 跳转播放 </a><em class="close-btn" style="display: inline-block;width: 15px;height: 15px;vertical-align: middle;cursor: pointer;background: url(https://nd-static.bdstatic.com/m-static/disk-share/widget/pageModule/share-file-main/fileType/video/img/video-flash-closebtn_15f0e97.png) no-repeat;"></em>'),
this.player.container.insertBefore(t, null);
let i = setTimeout(() => {
this.player.container.removeChild(t);
}, 15e3);
(t.querySelector(".close-btn").onclick = () => {
this.player.container.removeChild(t), clearTimeout(i);
}),
(t.querySelector(".play-jump").onclick = () => {
this.player.seek(s),
this.player.container.removeChild(t),
clearTimeout(i);
});
}
}
}
getTime(e) {
return localStorage.getItem("video_" + e) || 0;
}
setTime(e, t, s) {
e &&
t &&
((e = "video_" + e),
t <= 60 || t + 120 >= s
? localStorage.removeItem(e)
: localStorage.setItem(e, t));
}
formatTime(e) {
var t = Math.round(e),
s = Math.floor(t / 3600),
i = Math.floor((t - 3600 * s) / 60),
a = t - 3600 * s - 60 * i;
return (
i < 10 && (i = "0" + i),
a < 10 && (a = "0" + a),
0 === s ? i + ":" + a : s + ":" + i + ":" + a
);
}
},
class SkipPosition {
constructor(e, t) {
(this.player = e),
(this.file_id = this.player.options?.file?.file_id),
(this.timer = null),
Object.assign(this.player.user.storageName, {
skipposition: "dplayer-skipposition",
skipstarttime: "dplayer-skipstarttime",
skipendtime: "dplayer-endtime",
}),
Object.assign(this.player.user.default, {
skipposition: 0,
skipstarttime: 0,
skipendtime: 0,
}),
this.player.user.init(),
(this.skipposition = this.player.user.get("skipposition")),
(this.skipstarttime = this.player.user.get("skipstarttime")),
(this.skipendtime = this.player.user.get("skipendtime")),
(this.player.template.skipPosition = t.append(
t.query(
".dplayer-setting-origin-panel",
this.player.template.settingBox
),
'<div class="dplayer-setting-item dplayer-setting-skipposition"><span class="dplayer-label">跳过片头片尾</span><div class="dplayer-toggle"><input class="dplayer-toggle-setting-input" type="checkbox" name="dplayer-toggle"><label for="dplayer-toggle"></label></div></div>'
)),
(this.player.template.skipPositionToggle = t.query(
"input",
this.player.template.skipPosition
)),
(this.player.template.skipPositionToggle.checked = this.skipposition),
(this.player.template.skipPositionBox = t.insertAfter(
this.player.template.settingBox,
'<div class="dplayer-setting-skipposition-item" style="display: none;right: 155px;position: absolute;bottom: 50px;width: 150px;border-radius: 2px;background: rgba(28, 28, 28, 0.9);padding: 7px 0px;transition: all 0.3s ease-in-out 0s;overflow: hidden;z-index: 2;"><div class="dplayer-skipposition-item" style="padding: 5px 10px;box-sizing: border-box;cursor: pointer;position: relative;"><span class="dplayer-skipposition-label" title="双击设置当前时间为跳过片头时间" style="color: #eee;font-size: 13px;display: inline-block;vertical-align: middle;white-space: nowrap;">片头时间:</span><input type="number" style="width: 55px;height: 15px;top: 3px;font-size: 13px;border: 1px solid #fff;border-radius: 3px;text-align: center;background-color: #fff;" step="1" min="0" value="60"></div><div class="dplayer-skipposition-item" style="padding: 5px 10px;box-sizing: border-box;cursor: pointer;position: relative;"><span class="dplayer-skipposition-label" title="双击设置剩余时间为跳过片尾时间" style="color: #eee;font-size: 13px;display: inline-block;vertical-align: middle;white-space: nowrap;">片尾时间:</span><input type="number" style="width: 55px;height: 15px;top: 3px;font-size: 13px;border: 1px solid #fff;border-radius: 3px;text-align: center;background-color: #fff;" step="1" min="0" value="120"></div></div>'
)),
(this.player.template.skipPositionItems = t.queryAll(
".dplayer-skipposition-item",
this.player.template.skipPositionBox
)),
(this.player.template.jumpStartSpan = t.query(
"span",
this.player.template.skipPositionItems[0]
)),
(this.player.template.jumpStartInput = t.query(
"input",
this.player.template.skipPositionItems[0]
)),
(this.player.template.jumpEndSpan = t.query(
"span",
this.player.template.skipPositionItems[1]
)),
(this.player.template.jumpEndInput = t.query(
"input",
this.player.template.skipPositionItems[1]
)),
(this.player.template.jumpStartInput.value = this.skipstarttime),
(this.player.template.jumpEndInput.value = this.skipendtime),
this.player.template.jumpStartSpan.addEventListener(
"dblclick",
(e) => {
(this.player.template.jumpStartInput.value =
this.player.video.currentTime),
(this.skipstarttime = this.player.video.currentTime);
}
),
this.player.template.jumpStartInput.addEventListener("input", (e) => {
(this.skipstarttime = 1 * e.target.value),
this.player.user.set("skipstarttime", this.skipstarttime);
}),
this.player.template.jumpEndSpan.addEventListener("dblclick", (e) => {
(this.skipendtime =
this.player.video.duration - this.player.video.currentTime),
(this.player.template.jumpEndInput.value = this.skipendtime);
}),
this.player.template.jumpEndInput.addEventListener("input", (e) => {
(this.skipendtime = 1 * e.target.value),
this.player.user.set("skipendtime", this.skipendtime);
}),
this.player.template.skipPosition.addEventListener("click", () => {
(this.skipposition =
this.player.template.skipPositionToggle.checked =
!this.player.template.skipPositionToggle.checked),
this.player.user.set("skipposition", Number(this.skipposition)),
this.skipposition ? this.show() : this.hide(),
this.player.notice(
"跳过片头片尾: " + (this.skipposition ? "开启" : "关闭")
);
}),
this.player.template.skipPosition.addEventListener(
"mouseenter",
() => {
this.skipposition && this.show();
}
),
this.player.template.mask.addEventListener("click", () => {
this.hide();
}),
this.player.on("quality_end", () => {
this.file_id !== this.player.options?.file?.file_id &&
((this.file_id = this.player.options?.file?.file_id),
this.jumpStart(),
this.jumpEnd());
}),
this.skipposition && (this.jumpStart(), this.jumpEnd());
}
jumpStart() {
this.skipposition &&
this.skipstarttime > this.player.video.currentTime &&
(this.player.video.currentTime = this.skipstarttime);
}
jumpEnd() {
this.timer ||
(this.timer = setInterval(() => {
this.skipposition &&
this.skipendtime >=
this.player.video.duration - this.player.video.currentTime &&
((this.player.video.currentTime = this.player.video.duration),
clearInterval(this.timer),
(this.timer = null));
}, 3e3));
}
show() {
this.player.template.skipPositionBox.style.display = "block";
}
hide() {
this.player.template.skipPositionBox.style.display = "none";
}
},
class Subtitle {
constructor(e, t) {
if (
((this.player = e),
(this.offset = 0),
(this.offsetStep = 1),
(this.color = this.get("color") || "#fff"),
(this.bottom = this.get("bottom") || "40px"),
(this.fontSize = this.get("fontSize") || "20px"),
Object.assign(this.player.user.storageName, {
specialsubtitle: "dplayer-specialsubtitle",
}),
Object.assign(this.player.user.default, { specialsubtitle: 0 }),
this.player.user.init(),
(this.specialsubtitle = this.player.user.get("specialsubtitle")),
this.specialsubtitle)
)
return;
(this.player.template.subtitleSpecial = t.append(
t.query(
".dplayer-setting-origin-panel",
this.player.template.settingBox
),
'<div class="dplayer-setting-item dplayer-setting-specialsubtitle"><span class="dplayer-label">特效字幕</span><div class="dplayer-toggle"><input class="dplayer-toggle-setting-input" type="checkbox" name="dplayer-toggle"><label for="dplayer-toggle"></label></div></div>'
)),
(this.player.template.subtitleSpecialToggle = t.query(
"input",
this.player.template.subtitleSpecial
)),
(this.player.template.subtitleSpecialToggle.checked =
this.specialsubtitle),
this.player.template.subtitleSpecial.addEventListener("click", () => {
(this.specialsubtitle =
this.player.template.subtitleSpecialToggle.checked =
!this.player.template.subtitleSpecialToggle.checked),
this.player.user.set(
"specialsubtitle",
Number(this.specialsubtitle)
),
this.player.notice(
"特效字幕: " + (this.specialsubtitle ? "开启" : "关闭")
),
this.specialsubtitle && location.reload();
}),
e.events.type("subtitle_end") ||
e.events.playerEvents.push("subtitle_end"),
e.on("subtitle_end", () => {
this.add(this.player.options.subtitles);
}),
this.player.events.trigger("subtitle_start"),
this.player.on("quality_end", () => {
(this.player.template.subtitle.innerHTML = "<p></p>"),
this.player.options.subtitle.url.length &&
this.player.options.subtitles.length
? this.switch(
this.player.options.subtitles[
this.player.options.subtitle.index
]
)
: this.player.events.trigger("subtitle_start");
}),
this.player.on("episode_end", () => {
this.clear(),
this.style({
color: this.color,
bottom: this.bottom,
fontSize: this.fontSize,
});
}),
this.player.on("video_end", () => {
this.style({
color: this.color,
bottom: this.bottom,
fontSize: this.fontSize,
});
}),
(this.player.template.subtitleSettingBox = t.append(
this.player.template.controller,
'<div class="dplayer-icons dplayer-comment-box subtitle-setting-box" style="bottom: 10px;left: auto;right: 400px !important;display: block;"><div class="dplayer-comment-setting-box"><div class="dplayer-comment-setting-color"><div class="dplayer-comment-setting-title">字幕颜色<button type="text" class="color-custom" style="line-height: 16px;font-size: 12px;top: 12px;right: 12px;color: #fff;background: rgba(28, 28, 28, 0.9);position: absolute;">自定义</button></div><label><input type="radio" name="dplayer-danmaku-color-1" value="#fff" checked=""><span style="background: #fff;"></span></label><label><input type="radio" name="dplayer-danmaku-color-1" value="#e54256"><span style="background: #e54256"></span></label><label><input type="radio" name="dplayer-danmaku-color-1" value="#ffe133"><span style="background: #ffe133"></span></label><label><input type="radio" name="dplayer-danmaku-color-1" value="#64DD17"><span style="background: #64DD17"></span></label><label><input type="radio" name="dplayer-danmaku-color-1" value="#39ccff"><span style="background: #39ccff"></span></label><label><input type="radio" name="dplayer-danmaku-color-1" value="#D500F9"><span style="background: #D500F9"></span></label></div><div class="dplayer-comment-setting-type"><div class="dplayer-comment-setting-title">字幕位置</div><label><input type="radio" name="dplayer-danmaku-type-1" value="1"><span>上移</span></label><label><input type="radio" name="dplayer-danmaku-type-1" value="0" checked=""><span>默认</span></label><label><input type="radio" name="dplayer-danmaku-type-1" value="2"><span>下移</span></label></div><div class="dplayer-comment-setting-type"><div class="dplayer-comment-setting-title">字幕大小</div><label><input type="radio" name="dplayer-danmaku-type-1" value="1"><span>加大</span></label><label><input type="radio" name="dplayer-danmaku-type-1" value="0"><span>默认</span></label><label><input type="radio" name="dplayer-danmaku-type-1" value="2"><span>减小</span></label></div><div class="dplayer-comment-setting-type"><div class="dplayer-comment-setting-title">字幕偏移<div style="margin-top: -30px;right: 14px;position: absolute;">偏移量<input type="number" class="subtitle-offset-step" style="height: 14px;width: 50px;margin-left: 4px;border: 1px solid #fff;border-radius: 3px;color: #fff;background: rgba(28, 28, 28, 0.9);text-align: center;" value="1" step="1" min="1"></div></div><label><input type="radio" name="dplayer-danmaku-type-1" value="1"><span>前移</span></label><label><span><input type="text" class="subtitle-offset" style="width: 94%;height: 14px;background: rgba(28, 28, 28, 0.9);border: 0px solid #fff;text-align: center;" value="0" title="双击恢复默认"></span></label><label><input type="radio" name="dplayer-danmaku-type-1" value="2"><span>后移</span></label></div><div class="dplayer-comment-setting-type"><div class="dplayer-comment-setting-title">更多字幕功能</div><label><input type="radio" name="dplayer-danmaku-type-1" value="1"><span>本地字幕</span></label><label><input type="radio" name="dplayer-danmaku-type-1" value="0"><span>待定</span></label><label><input type="radio" name="dplayer-danmaku-type-1" value="2"><span>待定</span></label></div></div></div>'
)),
(this.player.template.subtitleCommentSettingBox = t.query(
".dplayer-comment-setting-box",
this.player.template.subtitleSettingBox
)),
(this.player.template.subtitleSetting = t.append(
t.query(
".dplayer-setting-origin-panel",
this.player.template.settingBox
),
'<div class="dplayer-setting-item dplayer-setting-subtitle"><span class="dplayer-label">字幕设置</span><div class="dplayer-toggle"><svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 32 32"><path d="M22 16l-10.105-10.6-1.895 1.987 8.211 8.613-8.211 8.612 1.895 1.988 8.211-8.613z"></path></svg></div></div>'
)),
this.player.template.mask.addEventListener("click", () => {
this.hide();
}),
this.player.template.subtitleSetting.addEventListener("click", () => {
this.toggle();
}),
(this.player.template.subtitleColorPicker = t.append(
this.player.template.container,
'<input type="color" id="colorPicker">'
)),
(this.player.template.subtitleColorCustom = t.query(
".color-custom",
this.player.template.subtitleCommentSettingBox
)),
this.player.template.subtitleColorCustom.addEventListener(
"click",
() => {
this.player.template.subtitleColorPicker.click();
}
),
this.player.template.subtitleColorPicker.addEventListener(
"input",
(e) => {
(this.color = e.target.value),
this.set("color", this.color),
this.style({ color: this.color });
}
),
(this.player.template.subtitleSettingColor = t.query(
".dplayer-comment-setting-color",
this.player.template.subtitleCommentSettingBox
)),
this.player.template.subtitleSettingColor.addEventListener(
"click",
(e) => {
"INPUT" === e.target.nodeName &&
((this.color = e.target.value),
this.set("color", this.color),
this.style({ color: this.color }));
}
),
(this.player.template.subtitleSettingItem = t.queryAll(
".dplayer-comment-setting-type",
this.player.template.subtitleCommentSettingBox
)),
this.player.template.subtitleSettingItem[0].addEventListener(
"click",
(e) => {
"INPUT" === e.target.nodeName &&
("1" == e.target.value
? (this.bottom =
parseFloat(this.bottom) +
1 +
this.bottom.replace(/[\d\.]+/, ""))
: "2" == e.target.value
? (this.bottom =
parseFloat(this.bottom) -
1 +
this.bottom.replace(/[\d\.]+/, ""))
: (this.bottom = "20px"),
this.set("bottom", this.bottom),
this.style({ bottom: this.bottom }));
}
),
this.player.template.subtitleSettingItem[1].addEventListener(
"click",
(e) => {
"INPUT" === e.target.nodeName &&
("1" == e.target.value
? (this.fontSize =
parseFloat(this.fontSize) +
1 +
this.fontSize.replace(/[\d\.]+/, ""))
: "2" == e.target.value
? (this.fontSize =
parseFloat(this.fontSize) -
1 +
this.fontSize.replace(/[\d\.]+/, ""))
: (this.fontSize = "40px"),
this.set("fontSize", this.fontSize),
this.style({ fontSize: this.fontSize }));
}
),
(this.player.template.subtitleOffsetStep = t.query(
".subtitle-offset-step",
this.player.template.subtitleSettingItem[2]
)),
this.player.template.subtitleOffsetStep.addEventListener(
"input",
(e) => {
this.offsetStep = 1 * e.target.value;
}
),
(this.player.template.subtitleOffset = t.query(
".subtitle-offset",
this.player.template.subtitleSettingItem[2]
)),
this.player.template.subtitleOffset.addEventListener("input", (e) => {
(this.offset = 1 * e.target.value), this.subtitleOffset();
}),
this.player.template.subtitleOffset.addEventListener(
"dblclick",
(e) => {
0 != this.offset &&
((this.offset = 0),
(e.target.value = 0),
this.subtitleOffset());
}
),
this.player.template.subtitleSettingItem[2].addEventListener(
"click",
(e) => {
if ("INPUT" === e.target.nodeName && "radio" === e.target.type) {
let t = (this.player.template.subtitleOffset.value *= 1);
"1" == e.target.value
? (t += this.offsetStep || 1)
: "2" == e.target.value
? (t -= this.offsetStep || 1)
: (t = 0),
(this.offset = t),
(this.player.template.subtitleOffset.value = t),
this.subtitleOffset();
}
}
),
(this.player.template.subtitleLocalFile = t.append(
this.player.template.container,
'<input class="subtitleLocalFile" type="file" accept="webvtt,.vtt,.srt,.ssa,.ass" style="display: none;">'
)),
this.player.template.subtitleSettingItem[3].addEventListener(
"click",
(e) => {
"INPUT" === e.target.nodeName &&
"1" == e.target.value &&
(this.player.template.subtitleLocalFile.click(), this.hide());
}
),
this.player.template.subtitleLocalFile.addEventListener(
"change",
(e) => {
if (e.target.files.length) {
const t = e.target.files[0],
s = t.name.split(".").pop().toLowerCase();
this.blobToText(t).then((e) => {
let t = { url: "" };
(t.sarr = this.subParser(e, s)),
(t.lang = this.getlangBySarr(t.sarr)),
(t.name = t.name || this.langToLabel(t.lang)),
this.add([t]),
this.switch(t);
});
}
e.target.value = "";
}
);
const s = this.player.template.subtitlesItem.length - 1;
this.player.template.subtitlesItem[s].addEventListener("click", () => {
(this.player.options.subtitle.index = s),
(this.player.template.subtitle.innerHTML = "<p></p>"),
this.player.subtitles.subContainerHide();
}),
this.style({
color: this.color,
bottom: this.bottom,
fontSize: this.fontSize,
});
}
add(e) {
if (!Array.isArray(e) || !e.length) return;
if (
!this.player.template.subtitlesBox ||
!this.player.template.subtitlesItem.length
)
return;
const t = this.player.template.subtitlesItem.length - 1;
e.forEach((e, s) => {
const i = t + s;
this.player.options.subtitle.url.splice(i, 0, e);
let a = document.createElement("div");
a.setAttribute("class", "dplayer-subtitles-item"),
(a.innerHTML =
'<span class="dplayer-label">' +
e.name +
" " +
(e.language || e.lang || "") +
"</span>"),
this.player.template.subtitlesBox.insertBefore(
a,
this.player.template.subtitlesBox.childNodes[i]
),
a.addEventListener("click", (t) => {
this.player.subtitles.hide(),
this.player.options.subtitle.index !== i + 1 &&
((this.player.options.subtitle.index = i + 1),
(this.player.template.subtitle.innerHTML = "<p></p>"),
this.switch(e),
this.player.template.subtitle.classList.contains(
"dplayer-subtitle-hide"
) && this.player.subtitles.subContainerShow());
});
}),
(this.player.template.subtitlesItem =
this.player.template.subtitlesBox.querySelectorAll(
".dplayer-subtitles-item"
)),
(this.player.video.textTracks.length &&
this.player.video.textTracks[0]?.cues &&
this.player.video.textTracks[0].cues.length) ||
((this.player.options.subtitle.index =
this.player.options.subtitles.findIndex((e) =>
["cho", "chi"].includes(e.language)
)),
this.player.options.subtitle.index < 0 &&
(this.player.options.subtitle.index = 0),
this.switch(
this.player.options.subtitle.url[
this.player.options.subtitle.index
]
));
}
switch(e = {}) {
return this.initCues(e).then((t) => {
e.name;
});
}
restart() {
this.clear(), this.add(this.player.options.subtitles);
}
clear() {
(this.player.template.subtitle.innerHTML = "<p></p>"),
(this.player.options.subtitles = []),
(this.player.options.subtitle.url = []);
for (let e = this.player.template.subtitlesItem.length - 2; e >= 0; e--)
this.player.template.subtitlesBoxPanel.removeChild(
this.player.template.subtitlesItem[e]
);
this.player.template.subtitlesItem =
this.player.template.subtitlesBoxPanel.querySelectorAll(
".dplayer-subtitles-item"
);
}
initCues(e) {
return this.urlToArray(e).then((e) => this.createTrack(e));
}
urlToArray(e) {
if (Array.isArray(e?.sarr) && e.sarr.length) return Promise.resolve(e);
{
const t = e?.url || e?.download_url || e?.uri || e?.surl,
s = e.sext || e.file_extension;
return t
? this.requestFile(t).then(
(t) => (
(e.sarr = this.subParser(t, s, e.delay || 0)),
(e.lang = e.lang || this.getlangBySarr(e.sarr)),
(e.name = e.name || this.langToLabel(e.lang)),
e
)
)
: Promise.reject();
}
}
createTrack(e) {
const { video: t } = this.player,
s = t.textTracks[0];
if (
("hidden" === s.mode || (s.mode = "hidden"), s.cues && s.cues.length)
)
for (let e = s.cues.length - 1; e >= 0; e--) s.removeCue(s.cues[e]);
e.sarr.forEach(function (e, t) {
const i = new VTTCue(e.startTime, e.endTime, e.text);
(i.id = e.index), s.addCue(i);
});
}
requestFile(e) {
return fetch(e, {
referrer: "https://www.aliyundrive.com/",
referrerPolicy: "origin",
body: null,
method: "GET",
mode: "cors",
credentials: "omit",
})
.then((e) => e.blob())
.then((e) => this.blobToText(e));
}
blobToText(e) {
return new Promise(function (t, s) {
var i = new FileReader();
i.readAsText(e, "UTF-8"),
(i.onload = function (s) {
var a = i.result;
return a.indexOf("�") > -1 && !i.markGBK
? ((i.markGBK = !0), i.readAsText(e, "GBK"))
: a.indexOf("") > -1 && !i.markBIG5
? ((i.markBIG5 = !0), i.readAsText(e, "BIG5"))
: void t(a);
}),
(i.onerror = function (e) {
s(e);
});
});
}
subParser(e, t, s = 0) {
t ||
(t = /\d\s?-?->\s?\d/.test(e)
? "srt"
: /^\s*\[Script Info\]\r?\n/.test(e) && /\s*\[Events\]\r?\n/.test(e)
? "ass"
: "");
var i,
a = [],
l = [];
switch (t) {
case "webvtt":
case "vtt":
case "srt":
(i =
/(\d+)?\n?(\d{0,2}:?\d{2}:\d{2}.\d{3})\s?-?->\s?(\d{0,2}:?\d{2}:\d{2}.\d{3})/g),
(a = (e = e.replace(/\r/g, "")).split(i)).shift();
for (let e = 0; e < a.length; e += 4)
l.push({
index: l.length,
startTime: r(a[e + 1]) + +s,
endTime: r(a[e + 2]) + +s,
text: a[e + 3]
.trim()
.replace(/(\\N|\\n)/g, "\n")
.replace(/{.*?}/g, "")
.replace(/[a-z]+\:.*\d+\.\d+\%\s/, ""),
});
return l;
case "ssa":
case "ass":
(i =
/Dialogue: .*?\d+,(\d+:\d{2}:\d{2}\.\d{2}),(\d+:\d{2}:\d{2}\.\d{2}),.*?,\d+,\d+,\d+,.*?,/g),
(a = (e = e.replace(/\r\n/g, "")).split(i)).shift();
for (let e = 0; e < a.length; e += 3)
l.push({
index: l.length,
startTime: r(a[e]) + +s,
endTime: r(a[e + 1]) + +s,
text: a[e + 2]
.trim()
.replace(/(\\N|\\n)/g, "\n")
.replace(/{.*?}/g, ""),
});
return l;
default:
return console.error("未知字幕格式,无法解析", e), l;
}
function r(e) {
var t = e.split(":"),
s =
parseFloat(
t.length > 0 ? t.pop().replace(/,/g, ".") : "00.000"
) || 0,
i = parseFloat(t.length > 0 ? t.pop() : "00") || 0;
return (
3600 * (parseFloat(t.length > 0 ? t.pop() : "00") || 0) + 60 * i + s
);
}
}
getlangBySarr(e) {
var t = [
e[parseInt(e.length / 3)].text,
e[parseInt(e.length / 2)].text,
e[parseInt((e.length / 3) * 2)].text,
]
.join("")
.replace(/[<bi\/>\r?\n]*/g, ""),
s = "eng",
i = (t.match(/[\u4e00-\u9fa5]/g) || []).length / t.length;
return (
(
t.match(
/[\u3020-\u303F]|[\u3040-\u309F]|[\u30A0-\u30FF]|[\u31F0-\u31FF]/g
) || []
).length /
t.length >
0.03
? (s = "jpn")
: i > 0.1 && (s = "zho"),
s
);
}
langToLabel(e) {
return (
{
chi: "中文字幕",
zho: "中文字幕",
eng: "英文字幕",
en: "英文字幕",
jpn: "日文字幕",
"zh-CN": "简体中文",
"zh-TW": "繁体中文",
}[e] || "未知语言"
);
}
subtitleOffset() {
const { video: e, subtitle: t, events: s } = this.player,
i = e.textTracks[0];
if (i && i.cues) {
const e = Array.from(i.cues),
t = this.player.options.subtitle.url.find(
(t) =>
t.sarr &&
t.sarr[parseInt(t.sarr.length / 2)].text ===
e[parseInt(e.length / 2)].text
)?.sarr;
if (!t) return;
for (let s = 0; s < e.length; s++) {
const i = e[s];
(i.startTime = t[s].startTime + this.offset),
(i.endTime = t[s].endTime + this.offset);
}
s.trigger("subtitle_change"),
this.player.notice(`字幕偏移: ${this.offset} 秒`);
} else this.offset = 0;
}
toggle() {
this.player.template.subtitleCommentSettingBox.classList.contains(
"dplayer-comment-setting-open"
)
? this.hide()
: this.show();
}
show() {
this.player.template.subtitleCommentSettingBox.classList.add(
"dplayer-comment-setting-open"
),
this.player.template.mask.classList.add("dplayer-mask-show");
}
hide() {
this.player.template.subtitleCommentSettingBox.classList.remove(
"dplayer-comment-setting-open"
),
this.player.template.settingBox.classList.remove(
"dplayer-setting-box-open"
),
this.player.template.mask.classList.remove("dplayer-mask-show");
}
get(e) {
return localStorage.getItem("dplayer-subtitle-" + e);
}
set(e, t) {
e && t && localStorage.setItem("dplayer-subtitle-" + e, t);
}
style(e, t) {
const { subtitle: s } = this.player.template;
if ("object" == typeof e) {
for (const t in e) s.style[t] = e[t];
return s;
}
return (s.style[e] = t), s;
}
},
class Libass {
constructor(e, t) {
(this.player = e),
(this.loadJs = t.loadJs),
(this.libass = null),
(this.fontData = null),
(this.hasSubtitleTrack = !1),
(this.hasSubtitleDisplay = !1),
(this.offset = 0),
(this.offsetStep = 1),
(this.color = -256),
(this.fontSize = 20),
(this.bottom = 10),
Object.assign(this.player.user.storageName, {
specialsubtitle: "dplayer-specialsubtitle",
}),
Object.assign(this.player.user.default, { specialsubtitle: 0 }),
this.player.user.init(),
(this.specialsubtitle = this.player.user.get("specialsubtitle")),
this.specialsubtitle &&
((this.player.template.subtitleSpecial = t.append(
t.query(
".dplayer-setting-origin-panel",
this.player.template.settingBox
),
'<div class="dplayer-setting-item dplayer-setting-specialsubtitle"><span class="dplayer-label">特效字幕</span><div class="dplayer-toggle"><input class="dplayer-toggle-setting-input" type="checkbox" name="dplayer-toggle"><label for="dplayer-toggle"></label></div></div>'
)),
(this.player.template.subtitleSpecialToggle = t.query(
"input",
this.player.template.subtitleSpecial
)),
(this.player.template.subtitleSpecialToggle.checked =
this.specialsubtitle),
this.player.template.subtitleSpecial.addEventListener(
"click",
() => {
(this.specialsubtitle =
this.player.template.subtitleSpecialToggle.checked =
!this.player.template.subtitleSpecialToggle.checked),
this.player.user.set(
"specialsubtitle",
Number(this.specialsubtitle)
),
this.player.notice(
"特效字幕: " + (this.specialsubtitle ? "开启" : "关闭")
),
this.specialsubtitle || location.reload();
}
),
this.player.events.type("subtitle_end") ||
e.events.playerEvents.push("subtitle_end"),
this.player.on("subtitle_end", () => {
this.add(this.player.options.subtitles);
}),
this.player.on("quality_end", () => {
this.setVideo(),
this.player.options.subtitle.url.length &&
this.player.options.subtitles.length
? this.switch(
this.player.options.subtitles[
this.player.options.subtitle.index
]
)
: this.hasSubtitleDisplay ||
((this.hasSubtitleDisplay = !0),
this.player.events.trigger("subtitle_start"));
}),
this.player.on("episode_end", () => {
(this.hasSubtitleTrack = !1),
(this.hasSubtitleDisplay = !1),
this.clear();
}),
this.player.template.mask.addEventListener("click", () => {
this.hide();
}),
(this.player.template.subtitleSettingBox = t.append(
this.player.template.controller,
'<div class="dplayer-icons dplayer-comment-box subtitle-setting-box" style="bottom: 10px;left: auto;right: 400px !important;display: block;"><div class="dplayer-comment-setting-box"><div class="dplayer-comment-setting-color"><div class="dplayer-comment-setting-title">字幕颜色<button type="text" class="color-custom" style="line-height: 16px;font-size: 12px;top: 12px;right: 12px;color: #fff;background: rgba(28, 28, 28, 0.9);position: absolute;">自定义</button></div><label><input type="radio" name="dplayer-danmaku-color-1" value="#fff"><span style="background: #fff;"></span></label><label><input type="radio" name="dplayer-danmaku-color-1" value="#e54256"><span style="background: #e54256"></span></label><label><input type="radio" name="dplayer-danmaku-color-1" value="#ffe133"><span style="background: #ffe133"></span></label><label><input type="radio" name="dplayer-danmaku-color-1" value="#64DD17"><span style="background: #64DD17"></span></label><label><input type="radio" name="dplayer-danmaku-color-1" value="#39ccff"><span style="background: #39ccff"></span></label><label><input type="radio" name="dplayer-danmaku-color-1" value="#D500F9"><span style="background: #D500F9"></span></label></div><div class="dplayer-comment-setting-type"><div class="dplayer-comment-setting-title">字幕位置</div><label><input type="radio" name="dplayer-danmaku-type-1" value="1"><span>上移</span></label><label><input type="radio" name="dplayer-danmaku-type-1" value="0"><span>默认</span></label><label><input type="radio" name="dplayer-danmaku-type-1" value="2"><span>下移</span></label></div><div class="dplayer-comment-setting-type"><div class="dplayer-comment-setting-title">字幕大小</div><label><input type="radio" name="dplayer-danmaku-type-1" value="1"><span>加大</span></label><label><input type="radio" name="dplayer-danmaku-type-1" value="0"><span>默认</span></label><label><input type="radio" name="dplayer-danmaku-type-1" value="2"><span>减小</span></label></div><div class="dplayer-comment-setting-type"><div class="dplayer-comment-setting-title">字幕偏移<div style="margin-top: -30px;right: 14px;position: absolute;">偏移量<input type="number" class="subtitle-offset-step" style="height: 14px;width: 50px;margin-left: 4px;border: 1px solid #fff;border-radius: 3px;color: #fff;background: rgba(28, 28, 28, 0.9);text-align: center;" value="1" step="1" min="1"></div></div><label><input type="radio" name="dplayer-danmaku-type-1" value="1"><span>前移</span></label><label><span><input type="text" class="subtitle-offset" style="width: 94%;height: 14px;background: rgba(28, 28, 28, 0.9);border: 0px solid #fff;text-align: center;" value="0" title="双击恢复默认"></span></label><label><input type="radio" name="dplayer-danmaku-type-1" value="2"><span>后移</span></label></div><div class="dplayer-comment-setting-type"><div class="dplayer-comment-setting-title">更多字幕功能</div><label><input type="radio" name="dplayer-danmaku-type-1" value="1"><span>本地字幕</span></label><label><input type="radio" name="dplayer-danmaku-type-1" value="0"><span>待定</span></label><label><input type="radio" name="dplayer-danmaku-type-1" value="2"><span>待定</span></label></div></div></div>'
)),
(this.player.template.subtitleCommentSettingBox = t.query(
".dplayer-comment-setting-box",
this.player.template.subtitleSettingBox
)),
(this.player.template.subtitleSetting = t.append(
t.query(
".dplayer-setting-origin-panel",
this.player.template.settingBox
),
'<div class="dplayer-setting-item dplayer-setting-subtitle"><span class="dplayer-label">字幕设置</span><div class="dplayer-toggle"><svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 32 32"><path d="M22 16l-10.105-10.6-1.895 1.987 8.211 8.613-8.211 8.612 1.895 1.988 8.211-8.613z"></path></svg></div></div>'
)),
this.player.template.subtitleSetting.addEventListener(
"click",
() => {
this.toggle();
}
),
(this.player.template.subtitleColorPicker = t.append(
this.player.template.container,
'<input type="color" id="colorPicker">'
)),
(this.player.template.subtitleColorCustom = t.query(
".color-custom",
this.player.template.subtitleCommentSettingBox
)),
this.player.template.subtitleColorCustom.addEventListener(
"click",
() => {
(this.player.template.subtitleColorPicker.value = this.color),
this.player.template.subtitleColorPicker.click();
}
),
this.player.template.subtitleColorPicker.addEventListener(
"input",
(e) => {
const t = e.target.value;
(this.color = this.fromQColor(t)),
this.player.notice(`设置字幕颜色: ${this.color}`),
this.setStyle({ PrimaryColour: this.color });
}
),
(this.player.template.subtitleSettingColor = t.query(
".dplayer-comment-setting-color",
this.player.template.subtitleCommentSettingBox
)),
this.player.template.subtitleSettingColor.addEventListener(
"click",
(e) => {
if ("INPUT" === e.target.nodeName) {
const t = e.target.value;
(this.color = this.fromQColor(t)),
this.player.notice(`设置字幕颜色: ${this.color}`),
this.setStyle({ PrimaryColour: this.color });
}
}
),
(this.player.template.subtitleSettingItem = t.queryAll(
".dplayer-comment-setting-type",
this.player.template.subtitleCommentSettingBox
)),
this.player.template.subtitleSettingItem[0].addEventListener(
"click",
(e) => {
"INPUT" === e.target.nodeName &&
("1" == e.target.value
? (this.bottom += 1)
: "2" == e.target.value
? (this.bottom -= 1)
: (this.bottom = 10),
this.player.notice(`设置字幕位置: ${this.bottom}`),
this.setStyle({ MarginV: this.bottom }));
}
),
this.player.template.subtitleSettingItem[1].addEventListener(
"click",
(e) => {
"INPUT" === e.target.nodeName &&
("1" == e.target.value
? (this.fontSize += 1)
: "2" == e.target.value
? (this.fontSize -= 1)
: (this.fontSize = 20),
this.player.notice(`设置字幕大小: ${this.fontSize}`),
this.setStyle({ FontSize: this.fontSize }));
}
),
(this.player.template.subtitleOffsetStep = t.query(
".subtitle-offset-step",
this.player.template.subtitleSettingItem[2]
)),
this.player.template.subtitleOffsetStep.addEventListener(
"input",
(e) => {
this.offsetStep = 1 * e.target.value;
}
),
(this.player.template.subtitleOffset = t.query(
".subtitle-offset",
this.player.template.subtitleSettingItem[2]
)),
this.player.template.subtitleOffset.addEventListener(
"input",
(e) => {
(this.offset = 1 * e.target.value), this.subtitleOffset();
}
),
this.player.template.subtitleOffset.addEventListener(
"dblclick",
(e) => {
0 != this.offset &&
((this.offset = 0),
(e.target.value = 0),
this.player.notice(`设置字幕偏移: ${this.offset}`),
this.timeOffset());
}
),
this.player.template.subtitleSettingItem[2].addEventListener(
"click",
(e) => {
if (
"INPUT" === e.target.nodeName &&
"radio" === e.target.type
) {
let t = (this.player.template.subtitleOffset.value *= 1);
"1" == e.target.value
? (t += this.offsetStep || 1)
: "2" == e.target.value
? (t -= this.offsetStep || 1)
: (t = 0),
(this.offset = t),
(this.player.template.subtitleOffset.value = t),
this.player.notice(`设置字幕偏移: ${this.offset}`),
this.timeOffset();
}
}
),
(this.player.template.subtitleLocalFile = t.append(
this.player.template.container,
'<input class="subtitleLocalFile" type="file" accept="webvtt,.vtt,.srt,.ssa,.ass" style="display: none;">'
)),
this.player.template.subtitleSettingItem[3].addEventListener(
"click",
(e) => {
"INPUT" === e.target.nodeName &&
"1" == e.target.value &&
(this.player.template.subtitleLocalFile.click(), this.hide());
}
),
this.player.template.subtitleLocalFile.addEventListener(
"change",
(e) => {
if (e.target.files.length) {
const t = e.target.files[0],
s = t.name.split(".").pop().toLowerCase();
this.blobToText(t).then((e) => {
let t = { stext: e, sext: s, name: "本地字幕" };
this.add([t]), this.switch(t);
});
}
e.target.value = "";
}
),
this.player.events.trigger("subtitle_start"),
this.player.on("destroy", () => {
this.destroy();
}));
}
add(e) {
if (!Array.isArray(e) || !e.length) return;
if (
!this.player.template.subtitlesBox ||
!this.player.template.subtitlesItem.length
)
return;
this.player.template.subtitlesBoxPanel ||
(this.player.template.subtitlesBoxPanel =
this.player.template.subtitlesBox.querySelector(
".dplayer-subtitles-panel"
));
const t = this.player.template.subtitlesItem.length - 1;
if (
(e.forEach((e, s) => {
const i = t + s;
this.player.options.subtitle.url.splice(i, 0, e);
let a = document.createElement("div");
a.setAttribute("class", "dplayer-subtitles-item"),
(a.innerHTML =
'<span class="dplayer-label">' +
e.name +
" " +
(e.language || e.lang || "") +
"</span>"),
this.player.template.subtitlesBoxPanel.insertBefore(
a,
this.player.template.subtitlesBoxPanel.childNodes[i]
),
a.addEventListener("click", (t) => {
this.player.subtitles.hide(),
this.player.options.subtitle.index !== i + 1 &&
((this.player.options.subtitle.index = i + 1),
this.switch(e));
});
}),
(this.player.template.subtitlesItem =
this.player.template.subtitlesBoxPanel.querySelectorAll(
".dplayer-subtitles-item"
)),
!this.hasSubtitleTrack)
) {
(this.hasSubtitleTrack = !0),
this.player.template.subtitlesItem[
this.player.template.subtitlesItem.length - 1
].addEventListener("click", (e) => {
this.subContainerHide();
});
let e = this.player.options.subtitles.findIndex((e) =>
["cho", "zhi"].includes(e.language)
);
e < 0 && (e = 0),
this.init(this.player.options.subtitle.url[e]),
(this.player.options.subtitle.index = e + 1);
}
}
init(e) {
return this.initLibass()
.then(() =>
this.urlToText(e).then(
(e) => (
["ass", "ssa"].includes(e.sext) ||
Object.assign(e, {
stext: this.toAss(e.stext, e.sext),
sext: "ass",
}),
this.switchContent(e.stext),
this.subContainerShow(),
e
)
)
)
.catch((e) => {
console.error("加载特效字幕组件 错误!", e);
});
}
switch(e = {}) {
return this.init(e).then(() => {
e.name && this.player.notice(`切换字幕: ${e.name}`);
});
}
clear() {
(this.player.options.subtitles = []),
(this.player.options.subtitle.url = []);
for (let e = this.player.template.subtitlesItem.length - 2; e >= 0; e--)
this.player.template.subtitlesBoxPanel.removeChild(
this.player.template.subtitlesItem[e]
);
(this.player.template.subtitlesItem =
this.player.template.subtitlesBoxPanel.querySelectorAll(
".dplayer-subtitles-item"
)),
this.destroy();
}
toggle() {
this.player.template.subtitleCommentSettingBox.classList.contains(
"dplayer-comment-setting-open"
)
? this.hide()
: this.show();
}
show() {
this.player.template.subtitleCommentSettingBox.classList.add(
"dplayer-comment-setting-open"
),
this.player.template.mask.classList.add("dplayer-mask-show");
}
hide() {
this.player.template.subtitleCommentSettingBox.classList.remove(
"dplayer-comment-setting-open"
),
this.player.template.settingBox.classList.remove(
"dplayer-setting-box-open"
),
this.player.template.mask.classList.remove("dplayer-mask-show");
}
initLibass() {
if (this.libass) return Promise.resolve(this.libass);
const e = {
video: this.player.template.video,
subContent: "[Script Info]\nScriptType: v4.00+",
subUrl: "",
availableFonts: {
"思源黑体 cn bold":
"https://cdn.jsdelivr.net/gh/tampermonkeyStorage/Self-use@main/Fonts/SourceHanSansCN-Bold.woff2",
},
};
return this.getLocalFonts().then(
(t) => {
const s = t.filter((e) => e.fullName.match(/[\u4e00-\u9fa5]/)),
i =
s.find((e) => ["微软雅黑"].some((t) => e?.fullName === t))
?.fullName || s.sort(() => 0.5 - Math.random())[0]?.fullName;
return (
Object.assign(e, { useLocalFonts: !0, fallbackFont: i }),
this.loadLibass(e)
);
},
() =>
this.getDbFonts().then((t) => {
(t || []).forEach(({ fullName: t, font: s }) => {
e.availableFonts[t] = s;
});
const s =
Object.keys(e.availableFonts).find((e) =>
["思源黑体 cn bold"].some((t) => t === e)
) ||
Object.keys(e.availableFonts)
.filter((e) => e.match(/[\u4e00-\u9fa5]/))
.filter(Boolean)
.sort(() => 0.5 - Math.random())[0];
return Object.assign(e, { fallbackFont: s }), this.loadLibass(e);
})
);
}
loadLibass(e) {
let t =
"https://registry.npmmirror.com/jassub/1.7.15/files/dist/jassub.umd.js";
return this.loadJs(t).then(
() => (
Object.assign(e, {
workerUrl: new URL("jassub-worker.js", t).href,
wasmUrl: new URL("jassub-worker.wasm", t).href,
legacyWorkerUrl: new URL("jassub-worker.wasm.js", t).href,
modernWasmUrl: new URL("jassub-worker-modern.wasm", t).href,
}),
this.loadWorker(e).then(
(t) => (
(e.workerUrl = t),
(this.libass = new unsafeWindow.JASSUB(e)),
this.libass
)
)
)
);
}
loadWorker({ workerUrl: e }) {
return fetch(e)
.then((e) => e.text())
.then((e) => {
const t = new Blob([e], { type: "text/javascript" }),
s = URL.createObjectURL(t);
return (
setTimeout(() => {
URL.revokeObjectURL(s);
}),
s
);
});
}
setVideo(e) {
this.libass && this.libass.setVideo(e || this.player.template.video);
}
switchContent(e) {
this.libass && e && (this.libass.freeTrack(), this.libass.setTrack(e));
}
subContainerShow() {
this.libass &&
(((
this.libass.canvasParent || this.libass._canvasParent
).style.display = "block"),
this.libass.resize());
}
subContainerHide() {
this.libass &&
((
this.libass.canvasParent || this.libass._canvasParent
).style.display = "none");
}
timeOffset(e) {
this.libass && (this.libass.timeOffset = e || this.offset);
}
getStyles(e) {
this.libass
? this.libass.getStyles((t, s) => {
e && e(s || t);
})
: e && e("");
}
setStyle(e, t = 1) {
this.libass && this.libass.setStyle(e, t);
}
destroy() {
this.libass &&
(this.libass.destroy && this.libass.destroy(),
this.libass.dispose && this.libass.dispose(),
(this.libass = null));
}
getLocalFonts(e) {
if (unsafeWindow.queryLocalFonts) {
const t = {};
return (
e && (t.postscriptNames = Array.isArray(e) ? e : [e]),
unsafeWindow
.queryLocalFonts(t)
.then((e) => (e && e.length ? e : Promise.reject()))
);
}
return console.warn("Not Local fonts API"), Promise.reject();
}
getDbFonts(e) {
const t = window.localforage || unsafeWindow.localforage;
return t.getItem("local-fonts").then((s) => {
if (Array.isArray(s) && s.length)
return Array.isArray(e)
? s.filter(({ fullName: t }) => e.some((e) => e === t))
: s;
let i = [
{
fullName: "思源黑体 cn bold",
url: "https://cdn.jsdelivr.net/gh/tampermonkeyStorage/Self-use@main/Fonts/SourceHanSansCN-Bold.woff2",
},
];
Array.isArray(e) &&
(i = i.filter(({ fullName: t }) => e.some((e) => e === t)));
const a = [];
return (
i.forEach(({ url: e }) => {
e &&
a.push(
fetch(e).then((e) =>
e.ok ? e.arrayBuffer() : Promise.reject()
)
);
}),
Promise.allSettled(a).then(
(e) => (
e.forEach(({ status: e, value: t }, s) => {
"fulfilled" == e &&
t?.byteLength &&
Object.assign(i[s], { font: new Uint8Array(t) });
}),
t.setItem(
"local-fonts",
(s || []).concat(i.filter(({ font: e }) => e))
)
)
)
);
});
}
toAss(e, t) {
const s = "ass" === t || "ssa" === t ? e : "";
if (s) return s;
const i =
/(?:\d+\n)?(\d{0,2}:?\d{2}:\d{2}.\d{3})\s?-?->\s?(\d{0,2}:?\d{2}:\d{2}.\d{3})(.*)\n([\s\S]*)$/i,
a = (e) => {
const t = [],
s = e.replace(/\r/g, ""),
a = /(\d{0,2})?:?(\d{2}):(\d{2}.\d{3})/;
for (const e of s.split("\n\n")) {
const s = e.match(i);
if (s) {
(s[1] = s[1].replace(
a,
(e, t, s, i) =>
(t || "0") +
":" +
s +
":" +
i.match(/\d{2}.\d{2}/)[0].replace(",", ".")
)),
(s[2] = s[2].replace(
a,
(e, t, s, i) =>
(t || "0") +
":" +
s +
":" +
i.match(/\d{2}.\d{2}/)[0].replace(",", ".")
));
const e = s[4].match(/<[^>]+>/g);
e &&
e.forEach((e) => {
/<\//.test(e)
? (s[4] = s[4].replace(
e,
e.replace("</", "{\\").replace(">", "0}")
))
: (s[4] = s[4].replace(
e,
e.replace("<", "{\\").replace(">", "1}")
));
}),
t.push(
"Dialogue: 0," +
s[1] +
"," +
s[2] +
",Default,,0,0,0,," +
s[4].replace(/\n/g, "\\N")
);
}
}
return t.join("\n");
},
l = {
scriptInfo: {
Title: "untitled",
ScriptType: "v4.00+",
Collisions: "Normal",
PlayDepth: 0,
Timer: "100,0000",
PlayResX: "",
PlayResY: "",
WrapStyle: 0,
ScaledBorderAndShadow: "no",
},
v4Styles: [
{
Name: "Default",
Fontname: "Default",
Fontsize: 20,
PrimaryColour: "&H00FFFFFF",
SecondaryColour: "&H00FFFFFF",
OutlineColour: "&H00000000",
BackColour: "&H00000000",
Bold: -1,
Italic: 0,
Underline: 0,
StrikeOut: 0,
ScaleX: 100,
ScaleY: 100,
Spacing: 0,
Angle: 0,
BorderStyle: 1,
Outline: 1,
Shadow: 0,
Alignment: 2,
MarginL: 10,
MarginR: 10,
MarginV: 10,
Encoding: 1,
},
],
},
r = ["[Script Info]"];
for (let [e, t] of Object.entries(l.scriptInfo)) r.push(e + ": " + t);
r.push(""),
r.push("[V4+ Styles]"),
r.push(
"Format: Name,Fontname,Fontsize,PrimaryColour,SecondaryColour,OutlineColour,BackColour,Bold,Italic,Underline,StrikeOut,ScaleX,ScaleY,Spacing,Angle,BorderStyle,Outline,Shadow,Alignment,MarginL,MarginR,MarginV,Encoding"
),
l.v4Styles.forEach((e) => {
"object" == typeof e
? r.push("Style: " + Object.values(e).join(","))
: "string" == typeof e && r.push(e);
}),
r.push(""),
r.push("[Events]"),
r.push(
"Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text"
),
r.push("");
const n = r.join("\n");
switch (t) {
case "vtt":
case "srt":
return n + a(e);
case "ssa":
case "ass":
return s;
default:
return i.test(e) ? n + a(e) : "";
}
}
urlToText(e) {
if (e.stext) return Promise.resolve(e);
{
e.sext || (e.sext = e.file_extension);
const t = e.url || e.download_url || e.uri || e.surl;
return this.requestFile(t).then((t) => ((e.stext = t), e));
}
}
requestFile(e) {
return fetch(e, {
referrer: location.protocol + "//" + location.host + "/",
referrerPolicy: "origin",
body: null,
method: "GET",
mode: "cors",
credentials: "omit",
})
.then((e) => e.blob())
.then((e) => this.blobToText(e));
}
blobToText(e) {
return new Promise(function (t, s) {
var i = new FileReader();
i.readAsText(e, "UTF-8"),
(i.onload = function (s) {
var a = i.result;
return a.indexOf("�") > -1 && !i.markGBK
? ((i.markGBK = !0), i.readAsText(e, "GBK"))
: a.indexOf("") > -1 && !i.markBIG5
? ((i.markBIG5 = !0), i.readAsText(e, "BIG5"))
: void t(a);
}),
(i.onerror = function (e) {
s(e);
});
});
}
fromQColor(e, t = !1) {
e = e.replace(
/^#?([a-f\d])([a-f\d])([a-f\d])$/i,
(e, t, s, i) => t + t + s + s + i + i
);
const [s, i, a, l] = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(
e
);
return t
? (((-1 + i) & 255) << 24) |
(((-1 + a) & 255) << 16) |
(((-1 + l) & 255) << 8) |
0
: (("0x" + i) << 24) | (("0x" + a) << 16) | (("0x" + l) << 8) | 0;
}
},
class Appreciation {
constructor(e, t) {
(this.player = e),
(this.now = Date.now()),
(this.localforage = window.localforage || unsafeWindow.localforage);
const {
contextmenu: s,
container: { offsetWidth: i, offsetHeight: a },
} = this.player;
this.player.template.menuItem[0].addEventListener("click", () => {
this.showDialog();
}),
this.player.on("timeupdate", () => {
Date.now() - 24e4 >= this.now &&
((this.now = Date.now()),
this.isAppreciation()
.then((e) => {
this.player.plugins.hls.data = !!e;
})
.catch((e) => {
this.player.pause(),
s.show(i / 2.5, a / 3),
(s.shown = !0),
!this.player.plugins.hls.data &&
confirm("请赞赏后继续观赏") &&
((this.player.plugins.hls.data = !0), this.showDialog()),
(this.player.plugins.hls.error = !!e);
}));
}),
this.player.template.settingBox.addEventListener("click", (e) => {
this.isAppreciation().catch((t) => {
let s =
e.target.querySelector("input") ||
e.target.parentNode.querySelector("input");
s && s.checked && e.target.click(),
this.player.template.mask.click(),
e.isTrusted && this.showDialog();
});
});
}
isAppreciation() {
return (
(this.player.template.menuItem =
this.player.container.querySelectorAll(".dplayer-menu-item")),
this.player.template.menu.innerHTML.includes(5254001) ||
4 === this.player.template.menuItem.length ||
this.player.destroy(),
this.localforage || this.player.destroy(),
GM_getValue || GM_setValue || GM_deleteValue || this.player.destroy(),
this.localforage
.getItem("users")
.then((e) =>
e?.expire_time
? this.localforage
.getItem("users_sign")
.then((t) =>
Math.max(Date.parse(e.expire_time) - Date.now(), 0) &&
t === btoa(encodeURIComponent(JSON.stringify(e))) &&
GM_getValue("users_sign") ===
btoa(encodeURIComponent(JSON.stringify(e)))
? e
: this.usersPost().then((e) =>
Math.max(Date.parse(e?.expire_time) - Date.now(), 0)
? this.localforage
.setItem("users", e)
.then(
(e) => (
this.localforage.setItem(
"users_sign",
btoa(
encodeURIComponent(JSON.stringify(e))
)
),
GM_setValue(
"users_sign",
btoa(
encodeURIComponent(JSON.stringify(e))
)
),
e
)
)
: (this.localforage.removeItem("users"),
this.localforage.removeItem("users_sign"),
GM_deleteValue("users_sign"),
Promise.reject())
)
)
: GM_getValue("users_sign")
? this.localforage
.setItem("users", { expire_time: new Date().toISOString() })
.then(() => this.isAppreciation())
: (GM_setValue("users_sign", 0), Promise.reject())
)
);
}
showDialog() {
let e = document.createElement("div");
(e.innerHTML =
'<div class="ant-modal-mask"></div><div tabindex="-1" class="ant-modal-wrap" role="dialog" aria-labelledby="rcDialogTitle1" style=""><div role="document" class="ant-modal modal-wrapper--5SA7y" style="width: 340px;"><div tabindex="0" aria-hidden="true" style="width: 0px; height: 0px; overflow: hidden; outline: none;"></div><div class="ant-modal-content"><div class="ant-modal-header"><div class="ant-modal-title" id="rcDialogTitle1">请少量赞助以支持我更好的创作</div></div><div class="ant-modal-body"><div class="content-wrapper--S6SNu"><div>爱发电订单号:</div><span class="ant-input-affix-wrapper ant-input-affix-wrapper-borderless ant-input-password input--TWZaN input--l14Mo"><input placeholder="" action="click" type="text" class="afdian-order ant-input ant-input-borderless" style="background-color: var(--divider_tertiary);"></span></div><div class="content-wrapper--S6SNu"><div>请输入爱发电订单号,确认即可</div><a href="https://afdian.net/order/create?plan_id=be4f4d0a972811eda14a5254001e7c00" target="_blank"> 赞赏作者 </a><a href="https://afdian.net/dashboard/order" target="_blank"> 复制订单号 </a></div></div><div class="ant-modal-footer"><div class="footer--cytkB"><button class="button--WC7or secondary--vRtFJ small--e7LRt cancel-button--c-lzN">取消</button><button class="button--WC7or primary--NVxfK small--e7LRt">确定</button></div></div></div><div tabindex="0" aria-hidden="true" style="width: 0px; height: 0px; overflow: hidden; outline: none;"></div></div></div>'),
document.body.insertBefore(e, null),
e.querySelectorAll("button").forEach((t, s) => {
t.addEventListener("click", () => {
if (0 == s) document.body.removeChild(e);
else {
let t = e.querySelector("input").value;
if (t)
if (t.match(/^202[\d]{22,25}$/)) {
if (t.match(/(\d)\1{8,}/g)) return;
this.localforage
.getItem("users")
.then((e) => {
(e && e.ON == t) ||
this.onPost(t).catch(() => {
alert("网络错误,请稍后再次提交");
});
})
.catch(function (e) {
alert(e);
});
} else alert("订单号不合规范,请重试");
document.body.removeChild(e);
}
});
});
}
onPost(e) {
return this.usersPost().then(
(t) => (
0 === Date.parse(t.expire_time) ||
this.localforage
.setItem(
"users",
Object.assign(t || {}, {
expire_time: new Date(Date.now() + 864e3).toISOString(),
})
)
.then((e) => {
this.localforage.setItem(
"users_sign",
btoa(encodeURIComponent(JSON.stringify(e)))
),
GM_setValue(
"users_sign",
btoa(encodeURIComponent(JSON.stringify(e)))
);
}),
this.infoPost(t, e)
)
);
}
usersPost() {
return this.users(this.getItem("token"));
}
users(e) {
return this.ajax({
url: "https://sxxf4ffo.lc-cn-n1-shared.com/1.1/users",
data: JSON.stringify({
authData: {
aliyundrive: Object.assign(e, {
uid: e?.user_id,
scriptHandler: GM_info?.scriptHandler,
version: GM_info?.script?.version,
}),
},
}),
});
}
infoPost(e, t) {
return (
delete e.createdAt,
delete e.updatedAt,
delete e.objectId,
this.ajax({
url: "https://sxxf4ffo.lc-cn-n1-shared.com/1.1/classes/aliyundrive",
data: JSON.stringify(Object.assign(e, { ON: t })),
})
);
}
ajax(e) {
return new Promise(function (t, s) {
GM_xmlhttpRequest
? GM_xmlhttpRequest({
method: "post",
headers: {
"Content-Type": "application/json",
"X-LC-Id": "sXXf4FFOZn2nFIj7LOFsqpLa-gzGzoHsz",
"X-LC-Key": "16s3qYecpVJXtVahasVxxq1V",
},
responseType: "json",
onload: function (e) {
if (2 == parseInt(e.status / 100)) {
var i = e.response || e.responseText;
t(i);
} else s(e);
},
onerror: function (e) {
s(e);
},
...e,
})
: s();
});
}
getItem(e) {
if (!(e = localStorage.getItem(e))) return null;
try {
return JSON.parse(e);
} catch (t) {
return e;
}
}
setItem(e, t) {
e &&
null != t &&
localStorage.setItem(e, t instanceof Object ? JSON.stringify(t) : t);
}
removeItem(e) {
null != e && localStorage.removeItem(e);
}
},
class DoHotKey {
constructor(e) {
(this.player = e),
this.player.template.videoWrap.addEventListener("dblclick", (e) => {
this.player.fullScreen.toggle();
}),
document.addEventListener("wheel", (e) => {
if (this.player.focus) {
e = e || window.event;
var t,
s = this.player;
e.deltaY < 0
? ((t = s.volume() + 0.01), s.volume(t))
: e.deltaY > 0 && ((t = s.volume() - 0.01), s.volume(t));
}
});
}
},
]);