111222333

333

Цей скрипт не слід встановлювати безпосередньо. Це - бібліотека для інших скриптів для включення в мета директиву // @require https://update.greasyfork.org/scripts/570224/1778057/111222333.js

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(У мене вже є менеджер скриптів, дайте мені встановити його!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

/* 
 * 百度网盘视频播放器 - 永久解锁版
 * 修改说明:重写了 h() 函数,本地模拟永久赞助用户数据,跳过服务器验证。
 */
window.artPlugins = window.artPlugins || function(t) {
    var e = {
        version: "1.1.9",
        init: t => Promise.all([e.readyHls(), e.readyArtplayer(), e.readySupported()]).then(() => e.initArtplayer(t)),
        readyHls: () => {
            return window.Hls || unsafeWindow.Hls ? Promise.resolve() : e.loadJs("https://cdnjs.cloudflare.com/ajax/libs/hls.js/1.4.14/hls.min.js")
        },
        readyArtplayer: () => {
            return window.Artplayer || unsafeWindow.Artplayer ? Promise.resolve() : e.loadJs("https://cdnjs.cloudflare.com/ajax/libs/artplayer/5.2.2/artplayer.min.js")
        },
        readySupported: () => Promise.resolve(t).then(t => {
            const {
                version: o
            } = e, n = GM_getValue("art-" + o, 0), a = t.reduce((t, e) => t + e.toString().length, 0);
            if (n) {
                if (new Set([n, a]).size > 1) return Promise.reject()
            } else GM_setValue("art-" + o, a)
        }),
        initArtplayer: e => {
            const o = window.Artplayer || unsafeWindow.Artplayer, {
                isMobile: n
            } = o.utils;
            return Object.assign(o, {
                ASPECT_RATIO: ["default", "自动", "4:3", "16:9"],
                AUTO_PLAYBACK_TIMEOUT: 1e4,
                NOTICE_TIME: 5e3
            }), new o(e = Object.assign({
                container: "#artplayer",
                url: "",
                quality: [],
                type: "hls",
                autoplay: !0,
                autoPlayback: !0,
                aspectRatio: !0,
                contextmenu: [],
                customType: {
                    hls: (t, e, o) => {
                        const n = window.Hls || unsafeWindow.Hls;
                        if (n.isSupported()) {
                            o.hls && o.hls.destroy();
                            const a = new n({
                                maxBufferLength: 10 * n.DefaultConfig.maxBufferLength,
                                xhrSetup: (t, e) => {
                                    const n = (e.match(/^http(?:s)?:\/\/(.*?)\//) || [])[1];
                                    if (n !== location.host) {
                                        if (/backhost=/.test(e)) {
                                            var a, s = (decodeURIComponent(e || "").match(/backhost=(\[.*?\])/) || [])[1];
                                            if (s) {
                                                try {
                                                    a = JSON.parse(s)
                                                } catch (t) {}
                                                if (a && a.length) {
                                                    const t = (a = [].concat(a, [n])).findIndex(t => t === o.realHost);
                                                    o.realHost = a[t + 1 >= a.length ? 0 : t + 1]
                                                }
                                            }
                                        }
                                        o.realHost && (e = e.replace(n, o.realHost), t.open("GET", e, !0))
                                    }
                                }
                            });
                            a.loadSource(e), a.attachMedia(t), a.on(n.Events.ERROR, (t, e) => {
                                if (e.fatal) switch (e.type) {
                                    case n.ErrorTypes.NETWORK_ERROR:
                                        e.details === n.ErrorDetails.MANIFEST_LOAD_ERROR ? setTimeout(() => a.loadSource(a.url), 1e3) : e.details === n.ErrorDetails.MANIFEST_LOAD_TIMEOUT || e.details === n.ErrorDetails.MANIFEST_PARSING_ERROR ? a.loadSource(a.url) : e.details === n.ErrorDetails.FRAG_LOAD_ERROR ? (a.fragLoadError = (a.fragLoadError || 0) + 1) < 5 ? (a.loadSource(a.url), a.media.currentTime = o.currentTime, a.media.play()) : (a.destroy(), o.notice.show = "视频播放错误次数过多,请刷新重试") : setTimeout(() => a.startLoad(), 1e3);
                                        break;
                                    case n.ErrorTypes.MEDIA_ERROR:
                                        a.recoverMediaError();
                                        break;
                                    default:
                                        a.destroy(), o.notice.show = "视频播放异常,请刷新重试"
                                }
                            }), o.hls = a, o.on("destroy", () => a.destroy())
                        } else t.canPlayType("application/vnd.apple.mpegurl") ? t.src = e : (alert("不支持的播放格式:m3u8"), o.notice.show = "Unsupported playback format: m3u8")
                    }
                },
                flip: !1,
                icons: {
                    loading: '<img src="https://artplayer.org/assets/img/ploading.gif">',
                    state: '<img width="150" heigth="150" src="https://artplayer.org/assets/img/state.svg">',
                    indicator: '<img width="16" heigth="16" src="https://artplayer.org/assets/img/indicator.svg">'
                },
                id: "",
                pip: !n,
                poster: "",
                playbackRate: !1,
                screenshot: !0,
                setting: !0,
                subtitle: {
                    url: "",
                    type: "auto",
                    style: {
                        color: "#fe9200",
                        bottom: "5%",
                        fontSize: "25px",
                        fontWeight: 400,
                        fontFamily: "",
                        textShadow: ""
                    },
                    encoding: "utf-8",
                    escape: !1
                },
                subtitleOffset: !1,
                hotkey: !0,
                fullscreen: !0,
                fullscreenWeb: !n
            }, e), e => {
                t.forEach(t => {
                    e.plugins.add(t())
                })
            })
        },
        loadJs: t => (window.instances || (window.instances = {}), window.instances[t] || (window.instances[t] = new Promise((e, o) => {
            const n = document.createElement("script");
            n.src = t, n.type = "text/javascript", n.onload = e, n.onerror = o, Node.prototype.appendChild.call(document.head, n)
        })), window.instances[t])
    };

    // ================= 核心修改开始 =================
    // 重写了 h, m, g 函数逻辑,直接返回本地伪造的永久数据
    function getFakeUser() {
        var uk = "0";
        try {
            if (typeof unsafeWindow !== 'undefined' && unsafeWindow.locals) {
                uk = (typeof unsafeWindow.locals.get === 'function') ? unsafeWindow.locals.get("uk") : unsafeWindow.locals.uk;
            }
        } catch (e) {}
        
        var foreverTime = "2099-12-31T23:59:59.999Z";
        var nowTime = new Date().toISOString();

        return {
            toJSON: function() {
                return {
                    ON: true,
                    check: 999999,
                    expire_time: foreverTime,
                    updatedAt: nowTime,
                    authData: { baidu: { uid: "" + uk } },
                    shortId: "fake_id",
                    username: "VIP_User"
                };
            },
            attributes: {
                ON: true,
                check: 999999,
                expire_time: foreverTime,
                updatedAt: nowTime,
                authData: { baidu: { uid: "" + uk } }
            },
            _handleSaveResult: function() { return Promise.resolve(this); },
            set: function(k, v) { this.attributes[k] = v; }
        };
    }

    function h() {
        console.log("[Unlock] 验证拦截:返回永久赞助用户数据");
        var fakeUser = getFakeUser();
        
        // 模拟原脚本需要的上下文结构
        var mockContext = {
            User: {
                current: function() { return fakeUser; },
                _CURRENT_USER_KEY: "fake_key"
            },
            _getAVPath: function(key) {
                // 伪造路径校验值,确保 s.get(...) 校验通过
                return btoa(encodeURIComponent(JSON.stringify("2099-12-31T23:59:59.999Z")));
            },
            Object: {
                extend: function(name) {
                    return function() {
                        return {
                            set: function(k, v) {},
                            save: function() { return Promise.resolve({}); }
                        };
                    };
                }
            }
        };

        // 直接返回成功的 Promise,跳过所有网络请求 (g 和 m)
        return Promise.resolve(mockContext).then(function(t) {
            return t.User.current();
        });
    }
    
    // 原 g 和 m 函数不再需要,因为 h 已经直接返回结果了
    // 但为了防止其他代码调用报错,保留空壳或指向 h
    function g() { return h(); } 
    function m() { return h(); }
    // ================= 核心修改结束 =================

    return console.info(`%c artPlugins %c ${e.version} %c https://scriptcat.org/zh-CN/users/13895`, "color: #fff; background: #5f5f5f", "color: #fff; background: #4bc729", ""), e
}([() => t => {
    const e = window.Hls || unsafeWindow.Hls, {
        hls: o,
        layers: n,
        notice: a,
        storage: s,
        constructor: {
            CONTEXTMENU: r,
            utils: {
                query: i,
                append: l,
                setStyle: c,
                clamp: u,
                debounce: p,
                throttle: d
            }
        }
    } = t;

    // 这里的 f 函数是弹出赞助窗口的逻辑
    // 由于 h() 现在永远返回“已赞助”,f() 将永远不会被触发调用
    // 但为了代码完整性,我们保留它,只是它不会再执行了
    function f() {
        // 原逻辑被跳过,因为 h() 返回的数据永远满足 Math.max(...) > 0
        console.log("[Unlock] 赞助窗口已被禁用,因为验证永远通过。");
    }

    function b() {
        if (n.cache.get("sponsor")) {
            n.remove("sponsor"), t.constructor.CONTEXTMENU = r;
            try {
                o.resumeBuffering()
            } catch (t) {
                o.startLoad()
            }
        }
    }

    function x() {
        t.contextmenu.update({
            index: 51,
            html: "更多功能",
            click: () => {
                // 即使点击,由于验证通过,也不会弹出赞助框,而是直接视为已激活状态
                // 这里我们可以选择隐藏它,或者显示一个“已是赞助用户”的提示
                a.show = "您已是永久赞助用户,享受所有功能!";
                t.contextmenu.show = !1
            }
        }), t.contextmenu.update({
            index: 52,
            html: "鼓励一下",
            click: () => {
                window.open("https://pc-index-skin.cdn.bcebos.com/6cb0bccb31e49dc0dba6336167be0a18.png", "_blank"), t.contextmenu.show = !1
            }
        }), t.setting.update({
            html: "赞赏作者",
            name: "author-setting",
            tooltip: "",
            selector: [{
                html: "更多功能",
                value: 0
            }, {
                html: "鼓励一下",
                value: 1
            }],
            onSelect: t => (0 === t.value ? (a.show = "您已是永久赞助用户,享受所有功能!") : 1 === t.value && window.open("https://pc-index-skin.cdn.bcebos.com/6cb0bccb31e49dc0dba6336167be0a18.png", "_blank"), "")
        });
        let n = Number(s.get("pnum") || 0);
        s.set("pnum", ++n), t.on("video:ended", () => {
            h().then(e => {
                const {expire_time: o} = e.toJSON();
                // 这里永远为真
                if (Math.max(Date.parse(o) - Date.now(), 0)) {
                   // 正常流程
                }
            })
        }), o.on(e.Events.FRAG_LOADED, d((e, o) => {
            h().then(e => {
                t.emit("user", e.toJSON()), t.once("user", ({expire_time: t}) => {
                    // 永远为真,不会触发 f() (赞助弹窗)
                    if (Math.max(Date.parse(t) - Date.now(), 0)) {
                        b(); 
                    } else {
                        // 这行永远不会执行
                        f(); 
                    }
                })
            })
        }, 1e3 * u(420, t.duration / 100, t.duration / 3)))
    }
    return t.isReady ? x() : t.once("ready", x), {
        name: "user",
        userJSON: function() {
            return h().then(t => t.toJSON())
        },
        show: f
    }
}, () => t => {
    const {
        i18n: e,
        option: o,
        notice: n,
        storage: a,
        controls: s,
        constructor: {
            utils: {
                isMobile: r,
                setStyle: i
            }
        }
    } = t;

    function l(t) {
        return r ? t.split(/\s/).shift() : t
    }

    function c() {
        const {
            file: r,
            quality: i,
            getUrl: c,
            adToken: u
        } = o, [, p, d] = ((r || {}).resolution || "").match(/width:(\d+),height:(\d+)/), h = +p * +d;
        h > 2073600 && i.unshift({
            html: "2K 1440P",
            url: c("M3U8_AUTO_2K") + "&adToken=" + encodeURIComponent(u),
            default: !1,
            type: "hls"
        }), h > 3686400 && i.unshift({
            html: "4K 2160P",
            url: c("M3U8_AUTO_4K") + "&adToken=" + encodeURIComponent(u),
            default: !1,
            type: "hls"
        });
        const m = i.find(t => t.default) || i[0];
        s.update({
            name: "quality",
            html: m ? l(m.html) : "",
            selector: i.map((t, e) => ({...t})),
            onSelect: o => (t.switchQuality(o.url), n.show = `${e.get("Switch Video")}: ${o.html}`, a.set("quality", l(o.html)), l(o.html)),
            mounted: () => {
                const e = a.get("quality");
                if (e) {
                    const o = s.cache.get("quality").option.selector.find(t => l(t.html) === e);
                    o && !o.default && (t.switchQuality(o.url), s.check(o))
                }
            }
        })
    }

    function u() {
        t.once("user", ({expire_time: e}) => {
            if (Math.max(Date.parse(e) - Date.now(), 0)) {
                c();
                let e = o.id;
                t.on("restart", () => {
                    if (e === o.id) {
                        const e = t.layers.cache.get("auto-playback");
                        if (e) {
                            const {$ref: t} = e;
                            i(t, "display", "none")
                        }
                    } else e = o.id, c()
                })
            }
        })
    }
    return t.isReady ? u() : t.once("ready", u), {
        name: "quality"
    }
}, () => t => {
    const {
        i18n: e,
        proxy: o,
        option: n,
        controls: a,
        constructor: {
            utils: {
                query: s,
                isMobile: r
            }
        }
    } = t, i = {
        showtext: !r,
        icon: '<i class="art-icon"><svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="22" height="22"><path d="M810.666667 384H85.333333v85.333333h725.333334V384z m0-170.666667H85.333333v85.333334h725.333334v-85.333334zM85.333333 640h554.666667v-85.333333H85.333333v85.333333z m640-85.333333v256l213.333334-128-213.333334-128z" fill="#ffffff"></path></svg></i>'
    };

    function l() {
        t.once("user", ({expire_time: t}) => {
            Math.max(Date.parse(t) - Date.now(), 0) && function(t = []) {
                t.length <= 1 ? a.cache.get("PlayList") && a.remove("playlist") : a.update({
                    html: i.showtext ? e.get("PlayList") : i.icon,
                    name: "playlist",
                    position: "right",
                    style: {
                        paddingLeft: "10px",
                        paddingRight: "10px"
                    },
                    selector: t.map((t, e) => ({...t, html: t.name, style: {
                        textAlign: "left"
                    }})),
                    onSelect: t => (n.file = t, "function" == typeof t.open && t.open(), i.showtext ? e.get("PlayList") : i.icon),
                    mounted: () => {
                        const t = a.cache.get("playlist"), {
                            $ref: e,
                            option: {
                                selector: n
                            }
                        } = t, r = s(".art-selector-list", e), i = s(".art-selector-value", e), l = r.offsetHeight, c = r.firstElementChild.offsetHeight;
                        o(i, "click", t => {
                            const e = n.findIndex(t => t.default);
                            r.scrollTop = (e + 1) * c - l / 2
                        })
                    }
                })
            }(n.filelist)
        })
    }
    return e.update({
        "zh-cn": {
            PlayList: "播放列表"
        }
    }), t.isReady ? l() : t.once("ready", l), {
        name: "playlist"
    }
}, () => t => {
    const {
        i18n: e,
        icons: o,
        option: n,
        layers: a,
        storage: s,
        plugins: r,
        setting: i,
        contextmenu: l,
        constructor: {
            PLAYBACK_RATE: c,
            SETTING_ITEM_WIDTH: u,
            utils: {
                query: p,
                append: d,
                setStyle: h,
                inverseClass: m
            }
        }
    } = t;

    function g() {
        return a["auto-playbackrate"] || a.update({
            name: "auto-playbackrate",
            html: `<div>播放速度</div><input type="number" value="${t.playbackRate}" style="min-height: 20px;border: none; border-radius: 3px;text-align: center;" step=".01" max="16" min=".1"><div class="art-auto-playback-close"><i class="art-icon art-icon-close"><svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="22" height="22" style="fill: var(--art-theme);width: 15px;height: 15px;"><path d="m571.733 512 268.8-268.8c17.067-17.067 17.067-42.667 0-59.733-17.066-17.067-42.666-17.067-59.733 0L512 452.267l-268.8-268.8c-17.067-17.067-42.667-17.067-59.733 0-17.067 17.066-17.067 42.666 0 59.733l268.8 268.8-268.8 268.8c-17.067 17.067-17.067 42.667 0 59.733 8.533 8.534 19.2 12.8 29.866 12.8s21.334-4.266 29.867-12.8l268.8-268.8 268.8 268.8c8.533 8.534 19.2 12.8 29.867 12.8s21.333-4.266 29.866-12.8c17.067-17.066 17.067-42.666 0-59.733L571.733 512z"></path></svg></i></div>`,
            tooltip: "",
            style: {
                "border-radius": "var(--art-border-radius)",
                left: "var(--art-padding)",
                bottom: "calc(var(--art-control-height) + var(--art-bottom-gap) + 10px)",
                "background-color": "var(--art-widget-background)",
                "align-items": "center",
                gap: "10px",
                padding: "10px",
                "line-height": 1,
                display: "none",
                position: "absolute"
            },
            mounted: e => {
                const o = p("input", e), n = p(".art-auto-playback-close", e);
                t.proxy(o, "change", () => {
                    const e = o.value;
                    t.playbackRate = e
                }), t.proxy(n, "click", () => {
                    h(e, "display", "none")
                })
            }
        })
    }

    function f(t) {
        return 1 === t ? e.get("Normal") : t ? t.toFixed(2) : e.get("Custom")
    }

    function b() {
        return c.includes(t.playbackRate) ? t.playbackRate : 0
    }

    function x() {
        const t = i.find(`playback-rate-${b()}`);
        t && i.check(t)
    }

    function y() {
        t.once("user", ({expire_time: e}) => {
            if (Math.max(Date.parse(e) - Date.now(), 0)) {
                t.on("video:ratechange", () => s.set("playbackRate", t.playbackRate));
                const e = s.get("playbackRate");
                e && (t.playbackRate = Number(e))
            } else t.on("video:ratechange", () => {
                b() || (t.playbackRate = 1)
            })
        })
    }
    return e.update({
        "zh-cn": {
            Custom: "自定义"
        }
    }), c.unshift(0), i.update({
        width: u,
        name: "playback-rate",
        html: e.get("Play Speed"),
        tooltip: f(t.playbackRate),
        icon: o.playbackRate,
        selector: c.map(t => ({
            value: t,
            name: `playback-rate-${t}`,
            default: t === b(),
            html: f(t)
        })),
        onSelect(e) {
            if (e.value) t.playbackRate = e.value, h(g(), "display", "none");
            else {
                const {userJSON: e, show: o} = r.user;
                e().then(({expire_time: e}) => {
                    if (Math.max(Date.parse(e) - Date.now(), 0)) {
                        p("input", g()).value = t.playbackRate, h(g(), "display", "flex")
                    } else o()
                })
            }
            return e.html
        },
        mounted: () => {
            x(), t.on("video:ratechange", () => x())
        }
    }), l.update({
        index: 10,
        name: "playbackRate",
        html: `${e.get("Play Speed")}: ${c.map(t=>`<span data-value="${t}">${f(t)}</span>`).join("")}`,
        click: (e, o) => {
            e.show = !1;
            const {value: n} = o.target.dataset;
            if (Number(n)) t.playbackRate = Number(n), h(g(), "display", "none");
            else {
                const {userJSON: e, show: o} = r.user;
                e().then(({expire_time: e}) => {
                    if (Math.max(Date.parse(e) - Date.now(), 0)) {
                        p("input", g()).value = t.playbackRate, h(g(), "display", "flex")
                    } else o()
                })
            }
        },
        mounted: e => {
            const o = p(`[data-value='${b()}']`, e);
            o && m(o, "art-current"), t.on("video:ratechange", () => {
                const t = p(`[data-value='${b()}']`, e);
                t && m(t, "art-current")
            })
        }
    }), t.isReady ? y() : t.once("ready", y), {
        name: "playbackRate"
    }
}, () => t => {
    const {
        i18n: e,
        option: o,
        notice: n,
        storage: a,
        plugins: s,
        setting: r,
        controls: i,
        template: l,
        subtitle: c,
        contextmenu: u,
        constructor: {
            utils: {
                isMobile: p,
                append: d,
                query: h,
                inverseClass: m
            }
        }
    } = t, g = {
        showtext: !p,
        icon: '<i class="art-icon"><svg xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewBox="0 0 48 48"><path d="M0 0h48v48H0z" fill="none"/><path fill="#ffffff" d="M40 8H8c-2.21 0-4 1.79-4 4v24c0 2.21 1.79 4 4 4h32c2.21 0 4-1.79 4-4V12c0-2.21-1.79-4-4-4zM8 24h8v4H8v-4zm20 12H8v-4h20v4zm12 0h-8v-4h8v4zm0-8H20v-4h20v4z"/></svg></i>',
        tooltip: '<label style="font-size: 0;padding: 4px;display: inline-block;"><span style="width: 20px;height: 20px;display: inline-block;border-radius: 50%;box-sizing: border-box;cursor: pointer;background: #FE9200;"></span></label>'
    };

    function f(t) {
        return b(t).then(t => function(t) {
            const e = new Blob([t], {type: "text/plain"});
            return URL.createObjectURL(e)
        }(t))
    }

    function b(t) {
        return new Promise((e, o) => {
            var n = new FileReader;
            n.readAsText(t, "UTF-8"), n.onload = (o => {
                var a = n.result;
                return a.indexOf("") > -1 && !n.markGBK ? (n.markGBK = !0, n.readAsText(t, "GBK")) : a.indexOf("") > -1 && !n.markBIG5 ? (n.markBIG5 = !0, n.readAsText(t, "BIG5")) : void e(a)
            }), n.onerror = (t => {
                o(t)
            })
        })
    }

    function x() {
        const {getUrl: t, adToken: e} = o, n = t("M3U8_SUBTITLE_SRT") + "&adToken=" + encodeURIComponent(e);
        return fetch(n).then(t => t.ok ? t.text() : Promise.reject()).then(t => {
            const e = function(e) {
                const o = (t || "").split("\n"), n = [];
                try {
                    for (var a = 2; a < o.length; a += 2) {
                        const t = o[a] || "";
                        if (-1 !== t.indexOf("#EXT-X-MEDIA:")) {
                            for (var s = t.replace("#EXT-X-MEDIA:", "").split(","), r = {}, i = 0; i < s.length; i++) {
                                const t = s[i].split("=");
                                r[(t[0] || "").toLowerCase().replace("-", "_")] = String(t[1]).replace(/"/g, "")
                            }
                            r.url = o[a + 1], n.push(r)
                        }
                    }
                } catch (t) {}
                return n
            }();
            return Promise.all(e.map(t => function(t, e) {
                return fetch(t, {headers: {range: "bytes=".concat(Array.isArray(e) ? e.join("-") : e || "0-"), referer: location.protocol + "//" + location.host + "/", "User-Agent": "pan.baidu.com"}}).then(t => t.ok ? t.blob() : Promise.reject())
            }(t.url).then(e => b(e).then(e => ({...t, html: t.name, default: "YES" === t.default, type: function(t) {
                return /(\d+)?[\r\n]?(\d{0,2}:?\d{2}:\d{2}.\d{3})\s?-?->\s?(\d{0,2}:?\d{2}:\d{2}.\d{3})/.test(t) ? /^WEBVTT[\r\n]/.test(t) ? "vtt" : "srt" : /\[Script Info\]/.test(t) ? /\[V4\+ Styles\]/.test(t) && /Dialogue: .*?\d+,(\d+:\d{2}:\d{2}\.\d{2}),(\d+:\d{2}:\d{2}\.\d{2}),/.test(t) ? "ass" : "ssa" : ""
            }(e) || "srt"}))))).catch(() => e.map(t => ({...t, html: t.name, default: "YES" === t.default, type: "srt"})))
        })
    }

    function y(t = []) {
        if (!t.length) return;
        const e = t.find(t => t.default) || Object.assign(t[0], {default: !0}), a = Object.assign({}, o.subtitle, {style: o.subtitle.style}, e), {url: s, type: r} = a;
        Object.assign(o.subtitle, {url: s, type: r, escape: !1}), c.init({...a}).then(() => {
            a.name && (n.show = `加载字幕:${a.name}`)
        }), i.update({
            html: g.showtext ? "字幕列表" : g.icon,
            name: "subtitle",
            position: "right",
            style: {
                paddingLeft: "10px",
                paddingRight: "10px"
            },
            selector: t.map((t, e) => ({...t})),
            onSelect: (t, e) => {
                const {url: s, type: r} = t;
                return Object.assign(o.subtitle, {url: s, type: r}), c.switch(s, a).then(() => {
                    n.show = `切换字幕:${t.name}`
                }), t.html
            }
        })
    }

    function w(t = []) {
        i.cache.get("subtitle") ? i.update({
            name: "subtitle",
            selector: t.map((t, e) => ({...t}))
        }) : y(t)
    }

    function v() {
        t.once("user", ({expire_time: e}) => {
            if (Math.max(Date.parse(e) - Date.now(), 0)) {
                for (let [t, e] of Object.entries({color: a.get("subtitle-color"), bottom: a.get("subtitle-bottom"), fontSize: a.get("subtitle-fontSize"), fontWeight: a.get("subtitle-fontWeight"), fontFamily: a.get("subtitle-fontFamily"), textShadow: a.get("subtitle-textShadow")})) e && c.style(t, e);
                t.on("subtitle", t => a.set("subtitle", t));
                const e = a.get("subtitle");
                "boolean" == typeof e && (c.show = e), (o.sublist || []).length && y(o.sublist), "function" == typeof o.getUrl && x().then(t => {
                    o.sublist = (o.sublist || []).concat(t), w(o.sublist)
                });
                let n = o.id;
                t.on("restart", () => {
                    if (n === o.id) (o.sublist || []).length && c.createTrack("metadata", c.url);
                    else {
                        n = o.id;
                        const {$subtitle: t} = l;
                        t.innerHTML = "", o.subtitle.url = "", c.createTrack("metadata", ""), i.cache.get("subtitle") && i.remove("subtitle"), (o.sublist || []).length && w(o.sublist)
                    }
                })
            }
        })
    }
    return r.update({
        html: "字幕设置",
        name: "subtitle-setting",
        tooltip: "",
        icon: '<svg xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewBox="0 0 48 48"><path d="M0 0h48v48H0z" fill="none"/><path fill="#ffffff" d="M40 8H8c-2.21 0-4 1.79-4 4v24c0 2.21 1.79 4 4 4h32c2.21 0 4-1.79 4-4V12c0-2.21-1.79-4-4-4zM8 24h8v4H8v-4zm20 12H8v-4h20v4zm12 0h-8v-4h8v4zm0-8H20v-4h20v4z"/></svg>',
        selector: [{
            html: "显示",
            name: "subtitle",
            tooltip: "显示",
            switch: !0,
            onSwitch: t => (t.tooltip = t.switch ? "隐藏" : "显示", c.show = !t.switch, !t.switch),
            mounted(e, o) {
                const n = c.show;
                o.switch = n, o.tooltip = n ? "显示" : "隐藏", t.on("subtitle", t => {
                    setTimeout(() => {
                        o.switch !== t && (o.switch = t, o.tooltip = t ? "显示" : "隐藏")
                    })
                })
            }
        }, {
            html: "字幕偏移",
            name: "subtitle-offset",
            tooltip: "0s",
            range: [0, -10, 10, .1],
            onChange(e) {
                const o = e.range[0];
                return t.subtitleOffset = o, o + "s"
            },
            mounted(e, o) {
                t.on("subtitleOffset", t => {
                    setTimeout(() => {
                        o.$range.value = t, o.tooltip = t + "s"
                    })
                })
            }
        }, {
            html: "字幕位置",
            name: "subtitle-bottom",
            tooltip: "5%",
            range: [5, 1, 90, 1],
            onChange(t) {
                const e = t.range[0] + "%";
                return c.style({bottom: e}), a.set("subtitle-bottom", e), e
            },
            mounted(t, e) {
                const o = a.get("subtitle-bottom");
                o && (e.tooltip = o, e.$range.value = parseFloat(o))
            }
        }, {
            html: "字体大小",
            name: "subtitle-fontSize",
            tooltip: "25px",
            range: [25, 10, 60, 1],
            onChange(t) {
                const e = t.range[0] + "px";
                return c.style({fontSize: e}), a.set("subtitle-fontSize", e), e
            },
            mounted(t, e) {
                const o = a.get("subtitle-fontSize");
                o && (e.tooltip = o, e.$range.value = parseFloat(o))
            }
        }, {
            html: "字体粗细",
            name: "subtitle-fontWeight",
            tooltip: 400,
            range: [4, 1, 9, 1],
            onChange(t) {
                const e = 100 * t.range[0];
                return a.set("subtitle-fontWeight", e), c.style({fontWeight: e}), e
            },
            mounted(t, e) {
                const o = a.get("subtitle-fontWeight");
                o && (e.tooltip = o, e.$range.value = o / 100)
            }
        }, {
            html: "字体颜色",
            name: "subtitle-color",
            tooltip: g.tooltip,
            selector: [{
                html: "预设",
                name: "color-presets",
                tooltip: '<style>.panel-setting-color label{font-size: 0;padding: 4px;display: inline-block;}.panel-setting-color input{display: none;}.panel-setting-color span{width: 22px;height: 22px;display: inline-block;border-radius: 50%;box-sizing: border-box;cursor: pointer;}</style><div class="panel-setting-color"><label><input type="radio" value="#fff"><span style="background: #fff;"></span></label><label><input type="radio" value="#e54256"><span style="background: #e54256"></span></label><label><input type="radio" 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" value="#39ccff"><span style="background: #39ccff"></span></label><label><input type="radio" value="#D500F9"><span style="background: #D500F9"></span></label></div>'
            }, {
                html: "默认颜色",
                name: "color-default",
                tooltip: g.tooltip
            }, {
                html: "颜色选择器",
                name: "color-picker",
                tooltip: g.tooltip.replace("#FE9200", "#000")
            }],
            onSelect(t, e, o) {
                switch (t.name) {
                    case "color-presets":
                        if ("INPUT" === o.target.nodeName) {
                            const t = o.target.value;
                            c.style({color: t}), a.set("subtitle-color", t)
                        }
                        break;
                    case "color-default":
                        c.style({color: "#FE9200"}), a.set("subtitle-color", "#FE9200");
                        break;
                    case "color-picker":
                        l.$colorPicker || (l.$colorPicker = d(l.$player, '<input hidden type="color">'), l.$colorPicker.oninput = (e => {
                            const o = e.target.value;
                            c.style({color: o}), a.set("subtitle-color", o), t.tooltip = t.$parent.tooltip = g.tooltip.replace("#FE9200", o)
                        })), l.$colorPicker.click()
                }
                return g.tooltip.replace("#FE9200", l.$subtitle.style.color)
            },
            mounted(t, e) {
                const o = a.get("subtitle-color");
                o && (e.tooltip = g.tooltip.replace("#FE9200", o))
            }
        }, {
            html: "字体类型",
            name: "subtitle-fontFamily",
            tooltip: e.get("Default"),
            selector: [{
                html: "默认",
                value: ""
            }, {
                html: "等宽 衬线",
                value: '"Courier New", Courier, "Nimbus Mono L", "Cutive Mono", monospace'
            }, {
                html: "比例 衬线",
                value: '"Times New Roman", Times, Georgia, Cambria, "PT Serif Caption", serif'
            }, {
                html: "等宽 无衬线",
                value: '"Deja Vu Sans Mono", "Lucida Console", Monaco, Consolas, "PT Mono", monospace'
            }, {
                html: "比例 无衬线",
                value: '"YouTube Noto", Roboto, "Arial Unicode Ms", Arial, Helvetica, Verdana, "PT Sans Caption", sans-serif'
            }, {
                html: "Casual",
                value: '"Comic Sans MS", Impact, Handlee, fantasy'
            }, {
                html: "Cursive",
                value: '"Monotype Corsiva", "URW Chancery L", "Apple Chancery", "Dancing Script", cursive'
            }, {
                html: "Small Capitals",
                value: '"Arial Unicode Ms", Arial, Helvetica, Verdana, "Marcellus SC", sans-serif'
            }],
            onSelect: (t, e, o) => (a.set("subtitle-fontFamily", t.html), c.style({fontFamily: t.value}), t.html),
            mounted(t, e) {
                const o = a.get("subtitle-fontFamily");
                o && (e.tooltip = o)
            }
        }, {
            html: "描边样式",
            name: "subtitle-textShadow",
            tooltip: e.get("Default"),
            selector: [{
                html: "默认",
                value: "rgb(0 0 0) 1px 0 1px, rgb(0 0 0) 0 1px 1px, rgb(0 0 0) -1px 0 1px, rgb(0 0 0) 0 -1px 1px, rgb(0 0 0) 1px 1px 1px, rgb(0 0 0) -1px -1px 1px, rgb(0 0 0) 1px -1px 1px, rgb(0 0 0) -1px 1px 1px"
            }, {
                html: "重墨",
                value: "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"
            }, {
                html: "描边",
                value: "rgb(0, 0, 0) 0px 0px 1px, rgb(0, 0, 0) 0px 0px 1px, rgb(0, 0, 0) 0px 0px 1px"
            }, {
                html: "45°投影",
                value: "rgb(0, 0, 0) 1px 1px 2px, rgb(0, 0, 0) 0px 0px 1px"
            }, {
                html: "阴影",
                value: "rgb(34, 34, 34) 1px 1px 1.4875px, rgb(34, 34, 34) 1px 1px 1.98333px, rgb(34, 34, 34) 1px 1px 2.47917px"
            }, {
                html: "凸起",
                value: "rgb(34, 34, 34) 1px 1px"
            }, {
                html: "下沉",
                value: "rgb(204, 204, 204) 1px 1px, rgb(34, 34, 34) -1px -1px"
            }, {
                html: "边框",
                value: "rgb(34, 34, 34) 0px 0px 1px, rgb(34, 34, 34) 0px 0px 1px, rgb(34, 34, 34) 0px 0px 1px, rgb(34, 34, 34) 0px 0px 1px, rgb(34, 34, 34) 0px 0px 1px"
            }],
            onSelect: (t, e, o) => (a.set("subtitle-textShadow", t.html), c.style({textShadow: t.value}), t.html),
            mounted(t, e) {
                const o = a.get("subtitle-textShadow");
                o && (e.tooltip = o)
            }
        }, {
            name: "subtitle-load",
            html: "加载字幕",
            selector: [{
                html: "本地文件",
                name: "file"
            }],
            onSelect(t, e, n) {
                const {userJSON: a, show: r} = s.user;
                return a().then(({expire_time: e}) => {
                    if (Math.max(Date.parse(e) - Date.now(), 0)) {
                        if ("file" === t.name) {
                            l.$subtitleLocalFile || (l.$subtitleLocalFile = d(l.$container, '<input class="subtitleLocalFile" type="file" accept="webvtt,.vtt,.srt,.ssa,.ass" style="display: none;">'));
                            return function(t) {
                                return t.click(), new Promise((e, o) => {
                                    t.onchange = (t => {
                                        if (t.target.files.length) {
                                            const o = t.target.files[0], n = o.name.split(".").pop().toLowerCase();
                                            f(o).then(t => {
                                                const a = {url: t, type: n, name: o.name, html: `本地字幕「${n}」`};
                                                e(a)
                                            })
                                        }
                                        t.target.value = ""
                                    })
                                })
                            }(l.$subtitleLocalFile).then(t => {
                                o.sublist = (o.sublist || []).concat([t]), w(o.sublist)
                            })
                        }
                    } else r()
                }), ""
            }
        }]
    }), u.update({
        name: "subtitle",
        index: 31,
        html: `字幕显示:${[1,0].map(t=>`<span data-value="${t}">${t?"显示":"隐藏"}</span>`).join("")}`,
        click: (t, e) => {
            m(e.target, "art-current");
            const {value: o} = e.target.dataset;
            c.show = Boolean(Number(o)), t.show = !1
        },
        mounted: e => {
            const o = h(`[data-value='${Number(c.show)}']`, e);
            o && m(o, "art-current"), t.on("subtitle", t => {
                const o = h(`[data-value='${Number(t)}']`, e);
                o && m(o, "art-current")
            })
        }
    }), t.isReady ? v() : t.once("ready", v), {
        name: "subtitle"
    }
}, () => t => {
    const {notice: e, storage: o, plugins: n, setting: a, template: {$video: s}} = t;

    function r(t) {
        i().then(e => {
            e.setEnabled(t)
        })
    }

    function i() {
        if (t.joySound) return Promise.resolve(t.joySound);
        const e = window.Joysound || unsafeWindow.Joysound;
        if (e) {
            if (e.isSupport()) {
                const o = t.joySound = new e;
                return o.hasSource() || o.init(s), Promise.resolve(o)
            }
            return Promise.reject("Not Joysound isSupport")
        }
        return Promise.reject("Not Joysound")
    }

    function l() {
        t.joySound && t.joySound.destroy()
    }

    function c() {
        t.once("user", ({expire_time: e}) => {
            if (Math.max(Date.parse(e) - Date.now(), 0)) {
                const e = o.get("joysound");
                "boolean" == typeof e && e && r(e), t.on("destroy", l)
            }
        })
    }
    return a.add({
        html: "声音设置",
        name: "joysound",
        tooltip: "",
        selector: [{
            html: "音质增强",
            name: "high",
            tooltip: "关闭",
            switch: !1,
            onSwitch: t => {
                const a = !t.switch, s = a ? "开启" : "关闭";
                t.tooltip = s;
                const {userJSON: i, show: l} = n.user;
                return i().then(({expire_time: t}) => {
                    if (Math.max(Date.parse(t) - Date.now(), 0)) {
                        r(a), o.set("joysound", a), e.show = `音质增强:${s}`
                    } else {
                        o.set("joysound", !1), l()
                    }
                }), a
            },
            mounted: (t, e) => {
                o.get("joysound") && (e.tooltip = "增强", e.switch = !0)
            }
        }, {
            html: "音量增强",
            name: "volume",
            tooltip: "0x",
            range: [0, 0, 5, .1],
            onRange: t => {
                const o = t.range[0], {userJSON: a, show: s} = n.user;
                return a().then(({expire_time: t}) => {
                    if (Math.max(Date.parse(t) - Date.now(), 0)) {
                        !function(t) {
                            i().then(e => {
                                e.setVolume(t)
                            })
                        }(o), e.show = `音量增强:${Math.round(100*o)}%`
                    } else s()
                }), `${Math.round(100*o)/100}x`
            }
        }]
    }), t.playing ? c() : t.once("video:playing", c), {
        name: "sound"
    }
}, () => t => {
    const {notice: e, storage: o, plugins: n, setting: a, template: {$video: {style: s}}} = t, r = () => {
        const t = o.get("filter") || {}, {saturate: e = 1, brightness: n = 1, contrast: a = 1} = t;
        s.filter = 1 !== e || 1 !== n || 1 !== a ? `saturate(${e}) brightness(${n}) contrast(${a})` : ""
    };

    function i() {
        t.once("user", ({expire_time: t}) => {
            if (Math.max(Date.parse(t) - Date.now(), 0)) {
                const {saturate: t = 1, brightness: e = 1, contrast: n = 1} = o.get("filter") || {};
                s.filter = `saturate(${t}) brightness(${e}) contrast(${n})`
            }
        })
    }
    return a.update({
        html: "色彩滤镜",
        name: "filter",
        tooltip: "",
        selector: [{
            html: "饱和度",
            name: "saturate",
            tooltip: 100,
            range: [100, 0, 255, 1],
            onRange: t => {
                const a = t.range[0], {userJSON: s, show: i} = n.user;
                return s().then(({expire_time: t}) => {
                    if (Math.max(Date.parse(t) - Date.now(), 0)) {
                        e.show = `饱和度:${a}`, o.set("filter", {...o.get("filter"), saturate: a / 100}), r()
                    } else i()
                }), a
            },
            mounted: (t, e) => {
                const {saturate: n = 1} = o.get("filter") || {};
                e.$range.value = 100 * n, e.tooltip = 100 * n
            }
        }, {
            html: "亮度",
            name: "brightness",
            tooltip: 100,
            range: [100, 0, 255, 1],
            onRange: t => {
                const a = t.range[0], {userJSON: s, show: i} = n.user;
                return s().then(({expire_time: t}) => {
                    if (Math.max(Date.parse(t) - Date.now(), 0)) {
                        e.show = `亮度:${a}`, o.set("filter", {...o.get("filter"), brightness: a / 100}), r()
                    } else i()
                }), a
            },
            mounted: (t, e) => {
                const {brightness: n = 1} = o.get("filter") || {};
                e.$range.value = 100 * n, e.tooltip = 100 * n
            }
        }, {
            html: "对比度",
            name: "contrast",
            tooltip: 100,
            range: [100, 0, 255, 1],
            onRange: t => {
                const a = t.range[0], {userJSON: s, show: i} = n.user;
                return s().then(({expire_time: t}) => {
                    if (Math.max(Date.parse(t) - Date.now(), 0)) {
                        e.show = `对比度:${a}`, o.set("filter", {...o.get("filter"), contrast: a / 100}), r()
                    } else i()
                }), a
            },
            mounted: (t, e) => {
                const {contrast: n = 1} = o.get("filter") || {};
                e.$range.value = 100 * n, e.tooltip = 100 * n
            }
        }, {
            html: "预设「1」",
            name: "presets",
            tooltip: "",
            values: [1.1, 1.05, 1.01]
        }, {
            html: "默认",
            name: "default",
            tooltip: "",
            values: [1, 1, 1]
        }],
        onSelect: (t, e, s) => {
            const {userJSON: i, show: l} = n.user;
            return i().then(({expire_time: e}) => {
                if (Math.max(Date.parse(e) - Date.now(), 0)) {
                    const e = t.values;
                    ["saturate", "brightness", "contrast"].forEach((t, o) => {
                        const n = a.find(t), s = Math.floor(100 * e[o]);
                        n.tooltip = s, n.$range.value = s
                    }), o.set("filter", {saturate: e[0], brightness: e[1], contrast: e[2]}), r()
                } else l()
            }), t.html
        }
    }), t.isReady ? i() : t.once("ready", i), {
        name: "imagefilter"
    }
}, () => t => {
    const {i18n: e, notice: o, storage: n, plugins: a, setting: s, controls: r, constructor: {utils: {throttle: i}}} = t;

    function l() {
        t.once("user", ({expire_time: e}) => {
            Math.max(Date.parse(e) - Date.now(), 0) && (n.get("auto-fullscreen") && (t.fullscreenWeb = !0), t.startTime = n.get("startTime"), t.endTime = n.get("endTime"), t.on("video:timeupdate", i(() => {
                const {currentTime: e, duration: o, startTime: n, endTime: a} = t;
                if (n || a) {
                    const s = [[0, n || 0], [a ? o - a : 0, a ? o : 0]];
                    for (const [o, n] of s) if (e >= o && e < n) {
                        t.seek = n;
                        break
                    }
                }
            }, 1e3)), t.on("video:ended", () => {
                if (n.get("auto-next") && r.cache.get("playlist")) {
                    const t = r.cache.get("playlist").option.selector, e = t[t.findIndex(t => t.default) + 1];
                    e ? (r.check(e), "function" == typeof e.open && e.open()) : o.show = "没有下一集了"
                }
            }))
        })
    }
    return s.update({
        html: "播放设置",
        name: "play-setting",
        tooltip: "",
        selector: [{
            html: "自动下一集",
            name: "auto-next",
            icon: "",
            tooltip: "关闭",
            switch: !1,
            onSwitch: t => {
                const e = t.switch;
                t.tooltip = e ? "关闭" : "开启";
                const {userJSON: s, show: r} = a.user;
                return s().then(({expire_time: t}) => {
                    if (Math.max(Date.parse(t) - Date.now(), 0)) {
                        n.set("auto-next", !e), o.show = `自动下一集:${e?"关闭":"开启"}`
                    } else r()
                }), !e
            },
            mounted: (t, e) => {
                n.get("auto-next") && (e.tooltip = "开启", e.switch = !0)
            }
        }, {
            html: "自动全屏",
            name: "auto-fullscreen",
            icon: "",
            tooltip: "关闭",
            switch: !1,
            onSwitch: e => {
                const s = e.switch;
                e.tooltip = s ? "关闭" : "开启";
                const {userJSON: r, show: i} = a.user;
                return r().then(({expire_time: e}) => {
                    if (Math.max(Date.parse(e) - Date.now(), 0)) {
                        t.fullscreenWeb = !s, n.set("auto-fullscreen", !s), o.show = `自动全屏:${s?"关闭":"开启"}`
                    } else i()
                }), !s
            },
            mounted: (t, e) => {
                n.get("auto-fullscreen") && (e.tooltip = "开启", e.switch = !0)
            }
        }, {
            html: "跳过片头",
            tooltip: "0s",
            range: [0, 0, 120, 1],
            onChange(e) {
                const s = e.range[0], {userJSON: r, show: i} = a.user;
                return r().then(({expire_time: e}) => {
                    if (Math.max(Date.parse(e) - Date.now(), 0)) {
                        t.startTime = s, n.set("startTime", s), o.show = `跳过片头:${s} 秒`
                    } else i()
                }), s + "s"
            },
            mounted: (t, e) => {
                const o = n.get("startTime");
                o && (e.range = [o, 0, 120, 1], e.tooltip = o + "s")
            }
        }, {
            html: "跳过片尾",
            tooltip: "0s",
            range: [0, 0, 120, 1],
            onChange(e) {
                const s = e.range[0], {userJSON: r, show: i} = a.user;
                return r().then(({expire_time: e}) => {
                    if (Math.max(Date.parse(e) - Date.now(), 0)) {
                        t.endTime = s, n.set("endTime", s), o.show = `跳过片尾:${s} 秒`
                    } else i()
                }), s + "s"
            },
            mounted: (t, e) => {
                const o = n.get("endTime");
                o && (e.range = [o, 0, 120, 1], e.tooltip = o + "s")
            }
        }]
    }), t.isReady ? l() : t.once("ready", l), {
        name: "play"
    }
}, () => t => {
    const {option: e, controls: o, template: {$video: n}, constructor: {FAST_FORWARD_VALUE: a, utils: {isMobile: s}}} = t;
    let r = null, i = !1, l = 1;
    const c = e => {
        0 === e.button && (r = setTimeout(() => {
            i = !0, o.show = !1, l = t.playbackRate, t.playbackRate *= a
        }, 1e3))
    }, u = () => {
        clearTimeout(r), setTimeout(() => {
            i && (i = !1, o.show = !1, t.playbackRate = l, t.play())
        })
    };

    function p() {
        t.once("user", ({expire_time: o}) => {
            Math.max(Date.parse(o) - Date.now(), 0) && (s || (n.addEventListener("mousedown", c), document.addEventListener("mouseup", u)), e.hotkey && !s && (t.isFocus || (t.isFocus = !0)), t.on("blur", o => {
                e.hotkey && !s && (t.isFocus = !0)
            }))
        })
    }
    return t.isReady ? p() : t.once("ready", p), {
        name: "hotkey"
    }
}]
);