JAV-JHS

Jav-鉴黄师 收藏、屏蔽、标记已下载; 免VIP查看热榜、Top250排行榜、Fc2ppv等数据; 可查看所有评论信息; 支持云盘备份; 以图识图; 字幕搜索;

// ==UserScript==
// @name         JAV-JHS
// @namespace    https://sleazyfork.org/zh-CN/scripts/533695-jav-jhs
// @version      2.0.8
// @author       xie bro
// @description  Jav-鉴黄师 收藏、屏蔽、标记已下载; 免VIP查看热榜、Top250排行榜、Fc2ppv等数据; 可查看所有评论信息; 支持云盘备份; 以图识图; 字幕搜索;
// @license      MIT
// @icon         https://www.google.com/s2/favicons?sz=64&domain=javdb.com
// @include      https://javdb.com/*
// @include      https://115.com/*
// @include      https://javdb*.com/*
// @include      https://www.javbus.com/*
// @include      https://*bus*/*
// @include      https://*javsee*/*
// @include      https://*seejav*/*
// @include      https://*sehuatang.*/*
// @include      https://javtrailers.com/*
// @include      https://subtitlecat.com/*
// @include      https://www.aliyundrive.com/*
// @include      https://5masterzzz.site/*
// @exclude      https://www.javbus.com/forum/*
// @exclude      https://www.javbus.com/*actresses
// @require      data:application/javascript,;(function%20hookBody()%20%7B%20if%20(document.readyState%20!%3D%3D%20%22loading%22)%20%7B%20return%3B%20%7D%20const%20initialHideStyle%20%3D%20document.createElement(%22style%22)%3B%20initialHideStyle.textContent%20%3D%20%60%20body%3A%3Abefore%20%7B%20content%3A%20%22%22%3B%20position%3A%20fixed%3B%20top%3A%200%3B%20left%3A%200%3B%20width%3A%20100%25%3B%20height%3A%20100%25%3B%20z-index%3A%209999999999%3B%20pointer-events%3A%20auto%3B%20display%3A%20block%3B%20%7D%20body.script-ready%3A%3Abefore%20%7B%20display%3A%20none%3B%20pointer-events%3A%20none%3B%20%7D%20%60%3B%20document.head.appendChild(initialHideStyle)%3B%20if%20(window.location.href.includes(%22hideNav%3D1%22))%20%7B%20const%20pollInterval%20%3D%20setInterval(()%20%3D%3E%20%7B%20const%20searchBar%20%3D%20document.querySelector(%22%23search-bar-container%22)%3B%20if%20(searchBar%20%26%26%20window.getComputedStyle(searchBar).display%20%3D%3D%3D%20%22none%22)%20%7B%20document.body.classList.add(%22script-ready%22)%3B%20clearInterval(pollInterval)%3B%20%7D%20const%20navBarDefault%20%3D%20document.querySelector(%22.navbar-default%22)%3B%20if%20(navBarDefault%20%26%26%20window.getComputedStyle(navBarDefault).display%20%3D%3D%3D%20%22none%22)%20%7B%20document.body.classList.add(%22script-ready%22)%3B%20clearInterval(pollInterval)%3B%20%7D%20%7D%2C%20200)%3B%20%7D%20else%20%7B%20setTimeout(()%20%3D%3E%20%7B%20document.body.classList.add(%22script-ready%22)%3B%20%7D%2C%201e3)%3B%20%7D%20%7D)()%3B
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/layer.min.js
// @require      https://cdn.jsdelivr.net/npm/[email protected]/js/md5.min.js
// @require      https://cdn.jsdelivr.net/npm/[email protected]/src/toastify.min.js
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/localforage.min.js
// @connect      xunlei.com
// @connect      geilijiasu.com
// @connect      aliyundrive.com
// @connect      aliyundrive.net
// @connect      ja.wikipedia.org
// @connect      beta.magnet.pics
// @connect      jdforrepam.com
// @connect      cc3001.dmm.co.jp
// @connect      cc3001.dmm.com
// @connect      www.dmm.co.jp
// @connect      special.dmm.co.jp
// @connect      adult.contents.fc2.com
// @connect      fc2ppvdb.com
// @connect      123av.com
// @connect      u3c3.com
// @connect      btsow.pics
// @connect      sukebei.nyaa.si
// @connect      3xplanet.com
// @connect      memojav.com
// @connect      missav.live
// @connect      jable.tv
// @connect      www.av.gl
// @connect      javtrailers.com
// @connect      javdb.com
// @connect      115.com
// @connect      *
// @grant        GM_xmlhttpRequest
// @grant        GM_openInTab
// @run-at       document-start
// ==/UserScript==

var t, e, n, a, i, s = Object.defineProperty, r = t => {
    throw TypeError(t);
}, o = (t, e, n) => ((t, e, n) => e in t ? s(t, e, {
    enumerable: !0,
    configurable: !0,
    writable: !0,
    value: n
}) : t[e] = n)(t, "symbol" != typeof e ? e + "" : e, n), l = (t, e, n) => e.has(t) ? r("Cannot add the same private member more than once") : e instanceof WeakSet ? e.add(t) : e.set(t, n), c = (t, e, n) => (((t, e, n) => {
    e.has(t) || r("Cannot " + n);
})(t, e, "access private method"), n);

const d = window.location.href, g = d.includes("javdb"), p = d.includes("javbus") || d.includes("seejav") || d.includes("bus") || d.includes("javsee"), h = d.includes("/search?q") || d.includes("/search/") || d.includes("/users/"), u = "filter", m = "favorite", f = "hasDown", v = "hasWatch", w = "🚫 屏蔽", b = "🚫 已屏蔽", y = "#de3333", x = "⭐ 收藏", k = "⭐ 已收藏", _ = "#25b1dc", C = "📥️ 已下载", S = "#7bc73b", P = "🔍 已观看", B = "#d7a80c", D = 2592e6;

let I = "";

window.location.href.includes("hideNav=1") && (I = "\n         .navbar-default {\n            display: none !important;\n        }\n        body {\n            padding-top:0px!important;\n        }\n    ");

const M = `\n<style>\n    \n    ${I}\n\n    .masonry {\n        height: 100% !important;\n        width: 100% !important;\n        padding: 0 15px !important;\n    }\n    .masonry {\n        display: grid;\n        column-gap: 10px; /* 列间距*/\n        row-gap: 10px; /* 行间距 */\n        grid-template-columns: repeat(4, minmax(0, 1fr));\n    }\n    .masonry .item {\n        /*position: initial !important;*/\n        top: initial !important;\n        left: initial !important;\n        float: none !important;\n        background-color:#c4b1b1;\n        position: relative !important;\n    }\n    \n    .masonry .item:hover {\n        box-shadow: 0 .5em 1em -.125em rgba(10, 10, 10, .1), 0 0 0 1px #485fc7;\n    }\n    .masonry .movie-box{\n        width: 100% !important;\n        height: 100% !important;\n        margin: 0 !important;\n        overflow: inherit !important;\n    }\n    .masonry .movie-box .photo-frame {\n        height: 70% !important;\n        margin: 0 !important;\n        position:relative; /* 方便预览视频定位*/\n    }\n    .masonry .movie-box img {\n        max-height: 300px;\n        min-height: 300px;\n        height: 100% !important;\n        object-fit: cover; /* 保持比例,裁剪多余部分 */\n        object-position: top right;\n    }\n    .masonry .movie-box img:hover {\n      transform: scale(1.04);\n      transition: transform 0.3s;\n    }\n    .masonry .photo-info{\n        height: 30% !important;\n    }\n    .masonry .photo-info span {\n      display: inline-block; /* 或者 block */\n      max-width: 100%;      /* 根据父容器限制宽度 */\n      white-space: nowrap;  /* 禁止换行 */\n      overflow: hidden;     /* 隐藏溢出内容 */\n      text-overflow: ellipsis; /* 显示省略号 */\n    }\n    \n    /* 无码页面的样式 */\n    .photo-frame .mheyzo,\n    .photo-frame .mcaribbeancom2{\n        margin-left: 0 !important;\n    }\n    .avatar-box{\n        width: 100% !important;\n        display: flex !important;\n        margin:0 !important;\n    }\n    .avatar-box .photo-info{\n        display: flex;\n        justify-content: center;\n        align-items: center;\n        gap: 30px;\n        flex-direction: row;\n        background-color:#fff !important;\n    }\n    /*.photo-info .item-tag{\n        position: relative;\n    }*/\n    footer,#related-waterfall{\n        display: none!important;\n    }\n</style>\n`;

let A = "";

window.location.href.includes("hideNav=1") && (A = "\n        .main-nav,#search-bar-container {\n            display: none !important;\n        }\n        \n        html {\n            padding-top:0px!important;\n        }\n    ");

let T = "100% 50% !important;";

window.location.href.includes("/advanced_search?type=100") && (T = "50% 50% !important;");

const E = `\n<style>\n    ${A}\n    \n    .navbar {\n        z-index: 12345679 !important;\n        padding: 0 0;\n    }\n    \n    .navbar-link:not(.is-arrowless) {\n        padding-right: 33px;\n    }\n    \n    .sub-header,\n    /*#search-bar-container, !*搜索框*!*/\n    #footer,\n    /*.search-recent-keywords, !*搜索框底部热搜词条*!*/\n    .app-desktop-banner,\n    div[data-controller="movie-tab"] .tabs,\n    h3.main-title,\n    div.video-meta-panel > div > div:nth-child(2) > nav > div.review-buttons > div:nth-child(2), /* 下载 订正 按钮*/\n    div.video-detail > div:nth-child(4) > div > div.tabs.no-bottom > ul > li:nth-child(3), /* 相关清单*/\n    div.video-detail > div:nth-child(4) > div > div.tabs.no-bottom > ul > li:nth-child(2), /* 短评按钮*/\n    div.video-detail > div:nth-child(4) > div > div.tabs.no-bottom > ul > li:nth-child(1), /*磁力面板 按钮*/\n    .top-meta,\n    .float-buttons {\n        display: none !important;\n    }\n    \n    div.tabs.no-bottom,\n    .tabs ul {\n        border-bottom: none !important;\n    }\n    \n    \n    /* 视频列表项 相对相对 方便标签绝对定位*/\n    .movie-list .item {\n        position: relative !important;\n    }\n    \n    .cover {\n        min-height: 300px !important; /*控制列多时的高度*/\n        overflow: hidden !important;\n    }\n    \n    .cover img{\n        object-fit: cover !important;\n        object-position: ${T}\n    }\n    \n    .video-title {\n      display: -webkit-box;\n      -webkit-box-orient: vertical;\n      -webkit-line-clamp: 2;  /* 限制显示2行 */\n      white-space: normal !important;\n</style>\n`;

function F(t) {
    if (t) if (t.includes("<style>")) document.head.insertAdjacentHTML("beforeend", t); else {
        const e = document.createElement("style");
        e.textContent = t, document.head.appendChild(e);
    }
}

p && F(M), g && F(E), F("\n<style>\n    .a-primary, /* 主按钮 - 浅蓝色 */\n    .a-success, /* 成功按钮 - 浅绿色 */\n    .a-danger, /* 危险按钮 - 浅粉色 */\n    .a-warning, /* 警告按钮 - 浅橙色 */\n    .a-info, /* 信息按钮 - 浅青色 */\n    .a-dark, /* 深色按钮 - 改为中等灰色(保持浅色系中的对比) */\n    .a-outline, /* 轮廓按钮 - 浅灰色边框 */\n    .a-disabled /* 禁用按钮 - 极浅灰色 */\n    {\n        display: inline-flex;\n        align-items: center;\n        justify-content: center;\n        padding: 6px 14px;\n        margin-left: 10px;\n        border-radius: 6px;\n        text-decoration: none;\n        font-size: 13px;\n        font-weight: 500;\n        transition: all 0.2s ease;\n        cursor: pointer;\n        border: 1px solid rgba(0, 0, 0, 0.08);\n        white-space: nowrap;\n    }\n    \n    .a-primary {\n        background: #e0f2fe;\n        color: #0369a1;\n        border-color: #bae6fd;\n    }\n    \n    .a-primary:hover {\n        background: #bae6fd;\n    }\n    \n    .a-success {\n        background: #dcfce7;\n        color: #166534;\n        border-color: #bbf7d0;\n    }\n    \n    .a-success:hover {\n        background: #bbf7d0;\n    }\n    \n    .a-danger {\n        background: #fee2e2;\n        color: #b91c1c;\n        border-color: #fecaca;\n    }\n    \n    .a-danger:hover {\n        background: #fecaca;\n    }\n    \n    .a-warning {\n        background: #ffedd5;\n        color: #9a3412;\n        border-color: #fed7aa;\n    }\n    \n    .a-warning:hover {\n        background: #fed7aa;\n    }\n    \n    .a-info {\n        background: #ccfbf1;\n        color: #0d9488;\n        border-color: #99f6e4;\n    }\n    \n    .a-info:hover {\n        background: #99f6e4;\n    }\n    \n    .a-dark {\n        background: #e2e8f0;\n        color: #334155;\n        border-color: #cbd5e1;\n    }\n    \n    .a-dark:hover {\n        background: #cbd5e1;\n    }\n    \n    .a-outline {\n        background: transparent;\n        color: #64748b;\n        border-color: #cbd5e1;\n    }\n    \n    .a-outline:hover {\n        background: #f8fafc;\n    }\n    \n    .a-disabled {\n        background: #f1f5f9;\n        color: #94a3b8;\n        border-color: #e2e8f0;\n        cursor: not-allowed;\n    }\n    \n    .a-disabled:hover {\n        transform: none;\n        box-shadow: none;\n        background: #f1f5f9;\n    }\n</style>\n"), 
F("\n<style>\n    /* 全局通用样式 */\n    .fr-btn {\n        float: right;\n        margin-left: 4px !important;\n    }\n    \n    .menu-box {\n        position: fixed;\n        right: 10px;\n        top: 50%;\n        transform: translateY(-50%);\n        display: flex;\n        flex-direction: column;\n        z-index: 1000;\n        gap: 6px;\n    }\n    \n    .menu-btn {\n        display: inline-block !important;\n        min-width: 80px;\n        padding: 7px 12px;\n        border-radius: 4px;\n        color: white !important;\n        text-decoration: none;\n        font-weight: bold;\n        font-size: 12px;\n        text-align: center;\n        cursor: pointer;\n        transition: all 0.3s ease;\n        box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);\n        text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);\n        border: none;\n        line-height: 1.3;\n        margin: 0;\n    }\n    \n    .menu-btn:hover {\n        transform: translateY(-1px);\n        box-shadow: 0 3px 6px rgba(0, 0, 0, 0.15);\n        opacity: 0.9;\n    }\n    \n    .menu-btn:active {\n        transform: translateY(0);\n        box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);\n    }\n    \n    .do-hide {\n        display: none !important;\n    }\n</style>\n");

t = new WeakSet, e = async function() {
    if (!window.location.hostname.includes("javdb")) return;
    (await this.forage.keys()).forEach((t => t.startsWith("SCORE_") && this.forage.removeItem(t)));
    const t = Date.now();
    try {
        const e = await this.forage.getItem("lastCleanupTime");
        if (e && t - e < 864e5) return;
        const n = await this.forage.keys();
        for (const t of n) {
            if (this.interceptedKeys.includes(t)) continue;
            const e = await this.forage.getItem(t);
            "object" == typeof e && "expires" in e && "expiresStr" in e && Date.now() > e.expires && (console.log("清理过期数据:", t), 
            await this.forage.removeItem(t));
        }
        await this.forage.setItem("lastCleanupTime", t);
    } catch (e) {
        console.error("[自动清理失败]", e), await this.forage.setItem("lastCleanupTime", t);
    }
}, n = async function(t, e, n) {
    let a;
    if (Array.isArray(t)) a = [ ...t ]; else {
        if (a = await this.forage.getItem(e) || [], a.includes(t)) {
            const e = `${t} ${n}已存在`;
            throw show.error(e), new Error(e);
        }
        a.push(t);
    }
    return await this.forage.setItem(e, a), a;
};

let L = class _StorageManager {
    constructor() {
        if (l(this, t), o(this, "car_list_key", "car_list"), o(this, "title_filter_keyword_key", "title_filter_keyword"), 
        o(this, "review_filter_keyword_key", "review_filter_keyword"), o(this, "setting_key", "setting"), 
        o(this, "filter_actress_car_list_key", "car_list_actress_"), o(this, "filter_actor_car_list_key", "car_list_actor_"), 
        o(this, "filter_actor_actress_info_list_key", "filter_actor_actress_info_list"), 
        o(this, "fold_category_key", "foldCategory"), o(this, "review_ts_key", "review_ts"), 
        o(this, "review_sign_key", "review_sign"), o(this, "actress_prefix_key", "z_actress_"), 
        o(this, "score_prefix_key", "z_score_"), o(this, "forage", localforage.createInstance({
            driver: localforage.INDEXEDDB,
            name: "JAV-JHS",
            version: 1,
            storeName: "appData"
        })), o(this, "interceptedKeys", [ this.car_list_key, this.title_filter_keyword_key, this.review_filter_keyword_key, this.setting_key ]), 
        _StorageManager.instance) throw new Error("LocalStorageManager已被实例化过了!");
        _StorageManager.instance = this, c(this, t, e).call(this).then();
    }
    async saveReviewFilterKeyword(e) {
        return c(this, t, n).call(this, e, this.review_filter_keyword_key, "评论关键词");
    }
    async saveTitleFilterKeyword(e) {
        return c(this, t, n).call(this, e, this.title_filter_keyword_key, "标题关键词");
    }
    async getTitleFilterKeyword() {
        return await this.forage.getItem(this.title_filter_keyword_key) || [];
    }
    async getSetting(t = null, e) {
        const n = await this.forage.getItem(this.setting_key) || {};
        if (null === t) return n;
        const a = n[t];
        return a ? "true" === a || "false" === a ? "true" === a.toLowerCase() : "string" != typeof a || isNaN(Number(a)) ? a : Number(a) : e;
    }
    async saveSetting(t) {
        t ? await this.forage.setItem(this.setting_key, t) : show.error("设置对象为空");
    }
    async saveSettingItem(t, e) {
        if (!t) return void show.error("key 不能为空");
        let n = await this.getSetting();
        n[t] = e, await this.saveSetting(n);
    }
    async getReviewFilterKeywordList() {
        return await this.forage.getItem(this.review_filter_keyword_key) || [];
    }
    async saveCar(t, e, n, a) {
        if (!t) throw show.error("番号为空!"), new Error("番号为空!");
        if (!e) throw show.error("url为空!"), new Error("url为空!");
        e.includes("http") || (e = window.location.origin + e), n && (n = n.trim());
        const i = await this.forage.getItem(this.car_list_key) || [];
        let s = i.find((e => e.carNum === t));
        switch (s ? (s.url = e, s.actress = n, s.updateDate = utils.getNowStr()) : (s = {
            carNum: t,
            url: e,
            actress: n,
            status: "",
            updateDate: utils.getNowStr()
        }, i.push(s)), a) {
          case u:
            if (s.status === u) {
                const e = `${t} 已在屏蔽列表中`;
                throw show.error(e), new Error(e);
            }
            s.status = u;
            break;

          case m:
            if (s.status === m) {
                const e = `${t} 已在收藏列表中`;
                throw show.error(e), new Error(e);
            }
            s.status = m;
            break;

          case f:
            s.status = f;
            break;

          case v:
            s.status = v;
            break;

          default:
            const e = "actionType错误";
            throw show.error(e), new Error(e);
        }
        await this.forage.setItem(this.car_list_key, i);
    }
    async getCarList() {
        return (await this.forage.getItem(this.car_list_key) || []).sort(((t, e) => {
            if (!t || !e) return 0;
            const n = t.updateDate ? new Date(t.updateDate).getTime() : 0;
            return (e.updateDate ? new Date(e.updateDate).getTime() : 0) - n;
        }));
    }
    async getCar(t) {
        return (await this.getCarList()).find((e => e.carNum === t));
    }
    async getActorFilterCarList(t) {
        return (await this.forage.getItem(t) || []).sort(((t, e) => {
            if (!t || !e) return 0;
            const n = t.updateDate ? new Date(t.updateDate).getTime() : 0;
            return (e.updateDate ? new Date(e.updateDate).getTime() : 0) - n;
        }));
    }
    async getAllActorFilterCarList() {
        const t = [];
        return await this.forage.iterate(((e, n) => {
            n.startsWith("car_list_") && t.push(...e);
        })), t;
    }
    async getActorFilterCarMap() {
        const t = {};
        return await this.forage.iterate(((e, n) => {
            n.startsWith(this.filter_actor_car_list_key) && (t[n] = e);
        })), t;
    }
    async getActressFilterCarMap() {
        const t = {};
        return await this.forage.iterate(((e, n) => {
            n.startsWith(this.filter_actress_car_list_key) && (t[n] = e);
        })), t;
    }
    async getActorFilterCar(t, e) {
        return (await this.getActorFilterCarList(t)).find((t => t.carNum === e));
    }
    async saveActorFilterCar(t, e, n, a) {
        if (!e) throw show.error("番号为空!"), new Error("番号为空!");
        if (!n) throw show.error("url为空!"), new Error("url为空!");
        n.includes("http") || (n = window.location.origin + n), a && (a = a.trim());
        const i = await this.forage.getItem(t) || [];
        let s = i.find((t => t.carNum === e));
        s || (s = {
            carNum: e,
            url: n,
            actress: a,
            status: u,
            updateDate: utils.getNowStr()
        }, i.push(s), await this.forage.setItem(t, i));
    }
    async removeActorFilter(t) {
        if (!t.includes("car_list_")) throw new Error("非法操作:" + t);
        await this.forage.removeItem(t);
    }
    async removeCar(t) {
        const e = await this.getCarList(), n = e.length, a = e.filter((e => e.carNum !== t));
        return a.length === n ? (show.error(`${t} 不存在`), !1) : (await this.forage.setItem(this.car_list_key, a), 
        !0);
    }
    async overrideCarList(t) {
        if (!Array.isArray(t)) throw new TypeError("必须传入数组类型数据");
        const e = t.filter((t => !t || "object" != typeof t || !t.carNum));
        if (e.length > 0) throw new Error(`缺少必要字段 carNum 的数据项: ${e.length} 条`);
        const n = new Set, a = t.filter((t => !!n.has(t.carNum) || (n.add(t.carNum), !1)));
        if (a.length > 0) throw new Error(`发现重复: ${a.slice(0, 3).map((t => t.carNum)).join(", ")}${a.length > 3 ? "..." : ""}`);
        await this.forage.setItem(this.car_list_key, t);
    }
    async getItem(t) {
        if (this.interceptedKeys.includes(t)) {
            let e = `危险操作, 该key已有方法实现获取, 请用内部方法调用!  key: ${t}`;
            throw show.error(e), new Error(e);
        }
        const e = await this.forage.getItem(t);
        return null == e ? null : "object" == typeof e && "expires" in e && "expiresStr" in e ? Date.now() > e.expires ? (await this.forage.removeItem(t), 
        null) : e.value : e;
    }
    async setItem(t, e, n = null) {
        if (this.interceptedKeys.includes(t)) {
            let e = `危险操作, 该key已有方法实现获取, 请用内部方法调用!  key: ${t}`;
            throw show.error(e), new Error(e);
        }
        let a = e;
        if (null !== n) {
            const t = Date.now() + n;
            a = {
                value: e,
                expires: t,
                expiresStr: utils.formatDate(new Date(t))
            };
        }
        return await this.forage.setItem(t, a);
    }
    async removeItem(t) {
        if (this.interceptedKeys.includes(t)) {
            let e = `危险操作, 该key不可删除!  key: ${t}`;
            throw show.error(e), new Error(e);
        }
        return await this.forage.removeItem(t);
    }
    async importData(t) {
        let e = t.filterKeywordList;
        Array.isArray(e) && await this.forage.setItem(this.title_filter_keyword_key, e), 
        e = t.reviewKeywordList, Array.isArray(e) && await this.forage.setItem(this.review_filter_keyword_key, e), 
        t.dataList && await this.overrideCarList(t.dataList), e = t[this.title_filter_keyword_key], 
        Array.isArray(e) && await this.forage.setItem(this.title_filter_keyword_key, e), 
        e = t[this.review_filter_keyword_key], Array.isArray(e) && await this.forage.setItem(this.review_filter_keyword_key, e), 
        t[this.car_list_key] && await this.overrideCarList(t[this.car_list_key]), t.setting && await this.saveSetting(t.setting);
        for (const n of Object.keys(t)) n.startsWith("car_list_") && (console.log(n), await this.forage.setItem(n, t[n]));
    }
    async exportData() {
        return {
            car_list: await this.getCarList(),
            title_filter_keyword: await this.getTitleFilterKeyword(),
            review_filter_keyword: await this.getReviewFilterKeywordList(),
            setting: await this.getSetting(),
            ...await this.getActressFilterCarMap(),
            ...await this.getActorFilterCarMap()
        };
    }
};

class Utils {
    constructor() {
        return o(this, "intervalContainer", {}), o(this, "mimeTypes", {
            txt: "text/plain",
            html: "text/html",
            css: "text/css",
            csv: "text/csv",
            json: "application/json",
            xml: "application/xml",
            jpg: "image/jpeg",
            jpeg: "image/jpeg",
            png: "image/png",
            gif: "image/gif",
            webp: "image/webp",
            svg: "image/svg+xml",
            pdf: "application/pdf",
            doc: "application/msword",
            docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
            xls: "application/vnd.ms-excel",
            xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
            ppt: "application/vnd.ms-powerpoint",
            pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
            zip: "application/zip",
            rar: "application/x-rar-compressed",
            "7z": "application/x-7z-compressed",
            mp3: "audio/mpeg",
            wav: "audio/wav",
            mp4: "video/mp4",
            webm: "video/webm",
            ogg: "audio/ogg"
        }), o(this, "insertStyle", (t => {
            t && (-1 === t.indexOf("<style>") && (t = "<style>" + t + "</style>"), $("head").append(t));
        })), Utils.instance || (Utils.instance = this), Utils.instance;
    }
    importResource(t) {
        let e;
        t.indexOf("css") >= 0 ? (e = document.createElement("link"), e.setAttribute("rel", "stylesheet"), 
        e.href = t) : (e = document.createElement("script"), e.setAttribute("type", "text/javascript"), 
        e.src = t), document.documentElement.appendChild(e);
    }
    openPage(t, e, n, a) {
        if (n || (n = !0), a && (a.ctrlKey || a.metaKey)) return void GM_openInTab(t.includes("http") ? t : window.location.origin + t, {
            insert: 0
        });
        const i = t.includes("?") ? `${t}&hideNav=1` : `${t}?hideNav=1`;
        layer.open({
            type: 2,
            title: e,
            content: i,
            scrollbar: !1,
            shadeClose: n,
            area: [ "80%", "90%" ],
            isOutAnim: !1,
            anim: -1
        });
    }
    closePage() {
        storageManager.getSetting("needClosePage", "yes").then((t => {
            if ("yes" !== t) return;
            parent.document.documentElement.style.overflow = "auto";
            [ ".layui-layer-shade", ".layui-layer-move", ".layui-layer" ].forEach((function(t) {
                const e = parent.document.querySelectorAll(t);
                if (e.length > 0) {
                    const t = e.length > 1 ? e[e.length - 1] : e[0];
                    t.parentNode.removeChild(t);
                }
            })), window.close();
        }));
    }
    loopDetector(t, e, n = 20, a = 1e4, i = !0) {
        let s = !1;
        const r = Math.random(), o = (new Date).getTime();
        this.intervalContainer[r] = setInterval((() => {
            (new Date).getTime() - o > a && (console.warn("loopDetector timeout!", t, e), s = i), 
            (t() || s) && (clearInterval(this.intervalContainer[r]), e && e(), delete this.intervalContainer[r]);
        }), n);
    }
    rightClick(t, e) {
        t && (t.jquery ? t = t.toArray() : t instanceof HTMLElement ? t = [ t ] : Array.isArray(t) || (t = [ t ]), 
        t && 0 !== t.length ? t.forEach((t => {
            t && t.addEventListener("contextmenu", (t => {
                e(t);
            }));
        })) : console.error("rightClick(), 找不到元素"));
    }
    q(t, e, n, a) {
        let i, s;
        t ? (i = t.clientX - 130, s = t.clientY - 120) : (i = window.innerWidth / 2 - 120, 
        s = window.innerHeight / 2 - 120);
        let r = layer.confirm(e, {
            offset: [ s, i ],
            title: "提示",
            btn: [ "确定", "取消" ],
            shade: 0,
            zIndex: 999999991
        }, (function() {
            n(), layer.close(r);
        }), (function() {
            a && a();
        }));
    }
    getNowStr(t = "-", e = ":", n = null) {
        let a;
        a = n ? new Date(n) : new Date;
        const i = a.getFullYear(), s = String(a.getMonth() + 1).padStart(2, "0"), r = String(a.getDate()).padStart(2, "0"), o = String(a.getHours()).padStart(2, "0"), l = String(a.getMinutes()).padStart(2, "0"), c = String(a.getSeconds()).padStart(2, "0");
        return `${[ i, s, r ].join(t)} ${[ o, l, c ].join(e)}`;
    }
    formatDate(t, e = "-", n = ":") {
        let a;
        if (t instanceof Date) a = t; else {
            if ("string" != typeof t) throw new Error("Invalid date input: must be Date object or date string");
            if (a = new Date(t), isNaN(a.getTime())) throw new Error("Invalid date string");
        }
        const i = a.getFullYear(), s = String(a.getMonth() + 1).padStart(2, "0"), r = String(a.getDate()).padStart(2, "0"), o = String(a.getHours()).padStart(2, "0"), l = String(a.getMinutes()).padStart(2, "0"), c = String(a.getSeconds()).padStart(2, "0");
        return `${[ i, s, r ].join(e)} ${[ o, l, c ].join(n)}`;
    }
    download(t, e) {
        show.info("开始请求下载...");
        const n = e.split(".").pop().toLowerCase();
        let a, i = this.mimeTypes[n] || "application/octet-stream";
        if (t instanceof Blob) console.log("blob类型"), a = t; else if (t instanceof ArrayBuffer || ArrayBuffer.isView(t)) console.log("ArrayBuffer"), 
        a = new Blob([ t ], {
            type: i
        }); else if ("string" == typeof t && t.startsWith("data:")) {
            console.log("base64");
            const e = atob(t.split(",")[1]), n = new ArrayBuffer(e.length), s = new Uint8Array(n);
            for (let t = 0; t < e.length; t++) s[t] = e.charCodeAt(t);
            a = new Blob([ s ], {
                type: i
            });
        } else console.log("其他情况按文本处理"), a = new Blob([ t ], {
            type: i
        });
        const s = URL.createObjectURL(a), r = document.createElement("a");
        r.href = s, r.download = e, document.body.appendChild(r), r.click(), setTimeout((() => {
            document.body.removeChild(r), URL.revokeObjectURL(s);
        }), 100);
    }
    smoothScrollToTop(t = 500) {
        return new Promise((e => {
            const n = performance.now(), a = window.pageYOffset;
            window.requestAnimationFrame((function i(s) {
                const r = s - n, o = Math.min(r / t, 1), l = o < .5 ? 4 * o * o * o : 1 - Math.pow(-2 * o + 2, 3) / 2;
                window.scrollTo(0, a * (1 - l)), o < 1 ? window.requestAnimationFrame(i) : e();
            }));
        }));
    }
    simpleId() {
        return Date.now().toString(36) + Math.random().toString(36).substr(2, 5);
    }
    log(...t) {
        console.groupCollapsed("📌", ...t);
        const e = (new Error).stack.split("\n").slice(2).map((t => t.trim())).filter((t => t.trim()));
        console.log(e.join("\n")), console.groupEnd();
    }
    isUrl(t) {
        try {
            return new URL(t), !0;
        } catch (e) {
            return !1;
        }
    }
    setHrefParam(t, e) {
        const n = new URL(window.location.href);
        n.searchParams.set(t, e), window.history.pushState({}, "", n.toString());
    }
    getResponsiveArea(t) {
        const e = window.innerWidth;
        return e >= 1920 ? t || [ "60%", "80%" ] : e >= 1200 ? [ "60%", "85%" ] : e >= 768 ? [ "70%", "90%" ] : [ "95%", "95%" ];
    }
    isMobile() {
        const t = navigator.userAgent.toLowerCase();
        return [ "iphone", "ipod", "ipad", "android", "blackberry", "windows phone", "nokia", "webos", "opera mini", "mobile", "mobi", "tablet" ].some((e => t.includes(e)));
    }
    copyToClipboard(t, e) {
        navigator.clipboard.writeText(e).then((() => show.info(`${t}已复制到剪切板, ${e}`))).catch((t => console.error("复制失败: ", t)));
    }
    htmlTo$dom(t) {
        const e = new DOMParser;
        return $(e.parseFromString(t, "text/html"));
    }
    addCookie(t, e = {}) {
        const {maxAge: n = 2592e3, path: a = "/", domain: i = "", secure: s = !1, sameSite: r = ""} = e;
        t.split(";").forEach((t => {
            const e = t.trim();
            if (e) {
                const [t, o] = e.split("=");
                if (t && o) {
                    let e = [ `${t}=${o}`, `max-age=${n}`, `path=${a}` ];
                    i && e.push(`domain=${i}`), s && e.push("Secure"), r && e.push(`SameSite=${r}`), 
                    document.cookie = e.join("; ");
                }
            }
        }));
    }
}

class SeHuaTangStorageManager {
    constructor() {
        if (o(this, "forage", localforage.createInstance({
            driver: localforage.INDEXEDDB,
            name: "JAV-JHS-SeHuaTang",
            version: 1,
            storeName: "appData"
        })), o(this, "article_list_key", "article_list"), SeHuaTangStorageManager.instance) throw new Error("SeHuaTangStorageManager已被实例化过了!");
        SeHuaTangStorageManager.instance = this;
    }
    async saveArticle(t, e, n, a) {
        if (!t) throw show.error("articleId为空!"), new Error("articleId为空!");
        if (!n) throw show.error("title为空!"), new Error("title为空!");
        if (!e) throw show.error("url为空!"), new Error("url为空!");
        e.includes("http") || (e = window.location.origin + e);
        const i = await this.forage.getItem(this.article_list_key) || [];
        let s = i.find((e => e.articleId === t));
        switch (s ? s.updateDate = utils.getNowStr() : (s = {
            articleId: t,
            url: e,
            status: "",
            updateDate: utils.getNowStr()
        }, i.push(s)), a) {
          case u:
            if (s.status === u) {
                const e = `${t} 已在屏蔽列表中`;
                throw show.error(e), new Error(e);
            }
            s.status = u;
            break;

          case m:
            if (s.status === m) {
                const e = `${t} 已在收藏列表中`;
                throw show.error(e), new Error(e);
            }
            s.status = m;
            break;

          default:
            const e = "actionType错误";
            throw show.error(e), new Error(e);
        }
        await this.forage.setItem(this.article_list_key, i);
    }
    async getArticleList() {
        return await this.forage.getItem(this.article_list_key) || [];
    }
    async getArticle(t) {
        return (await this.forage.getItem(this.article_list_key) || []).find((e => e.articleId === t));
    }
}

window.utils = new Utils, window.http = new class {
    get(t, e = {}, n = {}) {
        return this.jqueryRequest("GET", t, null, e, n);
    }
    post(t, e = {}, n = {}) {
        return this.jqueryRequest("POST", t, e, null, n);
    }
    put(t, e = {}, n = {}) {
        return this.jqueryRequest("PUT", t, e, null, n);
    }
    del(t, e = {}, n = {}) {
        return this.jqueryRequest("DELETE", t, null, e, n);
    }
    jqueryRequest(t, e, n = {}, a = {}, i = {}) {
        return "POST" === t && (i = {
            "Content-Type": "application/json",
            ...i
        }), new Promise(((s, r) => {
            $.ajax({
                method: t,
                url: e,
                timeout: 1e4,
                data: "GET" === t || "DELETE" === t ? a : JSON.stringify(n),
                headers: i,
                success: (t, e, n) => {
                    var a;
                    if (null == (a = n.getResponseHeader("Content-Type")) ? void 0 : a.includes("application/json")) try {
                        s("object" == typeof t ? t : JSON.parse(t));
                    } catch (i) {
                        s(t);
                    } else s(t);
                },
                error: (t, e, n) => {
                    let a = n;
                    if (t.responseText) try {
                        const e = JSON.parse(t.responseText);
                        a = e.message || e.msg || t.responseText;
                    } catch {
                        a = t.responseText;
                    }
                    r(new Error(a));
                }
            });
        }));
    }
}, window.gmHttp = new class {
    get(t, e = {}, n = {}, a) {
        return this.gmRequest("GET", t, null, e, n, a);
    }
    post(t, e = {}, n = {}, a) {
        n = {
            "Content-Type": "application/json",
            ...n
        };
        let i = JSON.stringify(e);
        return this.gmRequest("POST", t, i, null, n, a);
    }
    postForm(t, e = {}, n = {}, a) {
        n || (n = {}), n["Content-Type"] = "application/x-www-form-urlencoded";
        let i = "";
        return e && Object.keys(e).length > 0 && (i = Object.entries(e).map((([t, e]) => `${t}=${e}`)).join("&")), 
        this.gmRequest("POST", t, i, null, n, a);
    }
    checkUrlStatus(t, e = {}, n) {
        return new Promise(((a, i) => {
            GM_xmlhttpRequest({
                method: "HEAD",
                url: t,
                headers: e,
                timeout: n || 1e4,
                onload: t => {
                    a(t.status);
                },
                onerror: t => {
                    i(new Error(`请求失败: ${t}`));
                },
                ontimeout: () => {
                    i(new Error(`请求超时(${n}ms)`));
                }
            });
        }));
    }
    gmRequest(t, e, n = {}, a = {}, i = {}, s) {
        if (a && Object.keys(a).length) {
            const t = new URLSearchParams(a).toString();
            e += (e.includes("?") ? "&" : "?") + t;
        }
        return new Promise(((a, r) => {
            GM_xmlhttpRequest({
                method: t,
                url: e,
                headers: i,
                timeout: s || 1e4,
                data: n,
                onload: t => {
                    try {
                        if (t.status >= 200 && t.status < 300) if (t.responseText) try {
                            a(JSON.parse(t.responseText));
                        } catch (e) {
                            a(t.responseText);
                        } else a(t.responseText || t); else if (console.error("请求失败,状态码:", t.status), t.responseText) try {
                            const e = JSON.parse(t.responseText);
                            r(e);
                        } catch {
                            r(new Error(t.responseText || `HTTP Error ${t.status}`));
                        } else r(new Error(`HTTP Error ${t.status}`));
                    } catch (e) {
                        r(e);
                    }
                },
                onerror: t => {
                    r(new Error(t.error || "Network Error"));
                },
                ontimeout: () => {
                    r(new Error("Request Timeout"));
                }
            });
        }));
    }
}, window.storageManager = new L, window.seHuaTangStorageManager = new SeHuaTangStorageManager;

const H = new BroadcastChannel("channel-refresh");

window.refresh = function() {
    H.postMessage({
        type: "refresh"
    });
}, document.head.insertAdjacentHTML("beforeend", '\n        <style>\n            .loading-container {\n                position: fixed;\n                top: 0;\n                left: 0;\n                width: 100%;\n                height: 100%;\n                display: flex;\n                justify-content: center;\n                align-items: center;\n                background-color: rgba(0, 0, 0, 0.1);\n                z-index: 99999999;\n            }\n    \n            .loading-animation {\n                position: relative;\n                width: 60px;\n                height: 12px;\n                background: linear-gradient(90deg, #4facfe 0%, #00f2fe 100%);\n                border-radius: 6px;\n                animation: loading-animate 1.8s ease-in-out infinite;\n                box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);\n            }\n    \n            .loading-animation:before,\n            .loading-animation:after {\n                position: absolute;\n                display: block;\n                content: "";\n                animation: loading-animate 1.8s ease-in-out infinite;\n                height: 12px;\n                border-radius: 6px;\n                box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);\n            }\n    \n            .loading-animation:before {\n                top: -20px;\n                left: 10px;\n                width: 40px;\n                background: linear-gradient(90deg, #ff758c 0%, #ff7eb3 100%);\n            }\n    \n            .loading-animation:after {\n                bottom: -20px;\n                width: 35px;\n                background: linear-gradient(90deg, #ff9a9e 0%, #fad0c4 100%);\n            }\n    \n            @keyframes loading-animate {\n                0% {\n                    transform: translateX(40px);\n                }\n                50% {\n                    transform: translateX(-30px);\n                }\n                100% {\n                    transform: translateX(40px);\n                }\n            }\n        </style>\n    '), 
window.loading = function() {
    const t = document.createElement("div");
    t.className = "loading-container";
    const e = document.createElement("div");
    return e.className = "loading-animation", t.appendChild(e), document.body.appendChild(t), 
    {
        close: () => {
            t && t.parentNode && t.parentNode.removeChild(t);
        }
    };
}, function() {
    document.head.insertAdjacentHTML("beforeend", "\n        <style>\n            .data-table-container {\n                flex: 1; /* 自动填充剩余空间 */\n                overflow-y: auto; /* 保留滚动条 */\n                border: 1px solid #e2e8f0;\n            }\n            \n            .data-table {\n                width: 100%;\n                border-collapse: separate;\n                border-spacing: 0;\n                font-family: 'Helvetica Neue', Arial, sans-serif;\n                background: #fff;\n                overflow: hidden;\n                box-shadow: 0 4px 20px rgba(0, 0, 0, 0.03);\n                margin: 0 auto; /* 表格整体水平居中 */\n            }\n    \n            .data-table thead tr {\n                background: #f8fafc;\n            }\n            \n            /* 表头居中 */\n            .data-table th {\n                padding: 16px 20px;\n                text-align: center !important; /* 表头文字居中 */\n                color: #64748b;\n                font-weight: 500;\n                font-size: 14px;\n                text-transform: uppercase;\n                letter-spacing: 0.5px;\n                border-bottom: 1px solid #e2e8f0;\n            }\n            \n            /* 单元格内容居中 */\n            .data-table td {\n                padding: 14px 20px;\n                color: #334155;\n                font-size: 15px;\n                border-bottom: 1px solid #f1f5f9;\n                text-align: center !important; /* 单元格文字居中 */\n                vertical-align: middle; /* 垂直居中 */\n            }\n            \n            .data-table tbody tr:last-child td {\n                border-bottom: none;\n            }\n            \n            /* 行hover 变色*/\n            .data-table tbody tr {\n                transition: all 0.2s ease;\n            }\n            \n            .data-table tbody tr:hover {\n                background: #f8fafc;\n            }\n            \n            /* 可选:特定列左对齐/右对齐的示例 */\n            .data-table .text-left {\n                text-align: left;\n            }\n            \n            .data-table .text-right {\n                text-align: right;\n            }\n            \n            /* 添加.show-border时显示边框 */\n            .data-table.show-border {\n                border: 1px solid #e2e8f0;\n            }\n            \n            .data-table.show-border th,\n            .data-table.show-border td {\n                border: 1px solid #e2e8f0;\n            }\n            \n            \n            /* 滚动条美化 */\n            .data-table-container::-webkit-scrollbar {\n                width: 8px;\n                height: 8px;\n            }\n            \n            .data-table-container::-webkit-scrollbar-track {\n                background: #f1f1f1;\n            }\n            \n            .data-table-container::-webkit-scrollbar-thumb {\n                background: #c1c1c1;\n                border-radius: 4px;\n            }\n            \n            .data-table-container::-webkit-scrollbar-thumb:hover {\n                background: #a8a8a8;\n            }\n            \n            /* 最后一行底部边框 */\n            .data-table tbody tr:last-child td {\n                border-bottom: 1px solid #f1f5f9;\n            }\n            \n            .table-pagination {\n                display: flex;\n                align-items: center;\n                justify-content: flex-end;\n                padding: 20px 20px 0;\n                font-size: 14px;\n                flex-shrink: 0; /* 防止分页区域被压缩 */\n            }\n            \n            .pagination-info {\n                margin-right: auto;\n                color: #666;\n            }\n            \n            .pagination-controls {\n                display: flex;\n                align-items: center;\n                margin: 0 15px;\n            }\n            \n            .pagination-controls button {\n                padding: 5px 12px;\n                margin: 0 5px;\n                border: 1px solid #ddd;\n                background: #fff;\n                cursor: pointer;\n                border-radius: 4px;\n            }\n            \n            .pagination-controls button:disabled {\n                color: #ccc;\n                cursor: not-allowed;\n            }\n            \n            .pagination-current {\n                margin: 0 10px;\n            }\n            \n            .pagination-size-select {\n                padding: 5px;\n                border: 1px solid #ddd;\n                border-radius: 4px;\n            }\n        </style>\n    ");
    window.TableGenerator = class {
        constructor(t) {
            this.defaults = {
                tableClass: "data-table",
                showBorder: !1,
                buttons: [],
                pagination: {
                    enable: !1,
                    pageSize: 10,
                    pageSizeOptions: [ 10, 20, 50, 100 ],
                    currentPage: 1,
                    showTotal: !0,
                    showSizeChanger: !0,
                    showQuickJumper: !0
                }
            }, this.config = {
                ...this.defaults,
                ...t,
                pagination: {
                    ...this.defaults.pagination,
                    ...t.pagination || {}
                }
            }, this.validateConfig() && this.init();
        }
        validateConfig() {
            return this.config.containerId && this.config.columns && Array.isArray(this.config.columns) && Array.isArray(this.config.data) ? (this.container = document.getElementById(this.config.containerId), 
            !!this.container || (console.error(`未找到ID为${this.config.containerId}的容器`), !1)) : (console.error("缺少必要参数或参数类型不正确"), 
            !1);
        }
        init() {
            this.container.innerHTML = "", this.container.style.display = "flex", this.container.style.flexDirection = "column", 
            this.container.style.height = "90%";
            const t = document.createElement("div");
            t.className = "data-table-container", this.table = document.createElement("table"), 
            this.table.className = this.config.showBorder ? `${this.config.tableClass} show-border` : this.config.tableClass, 
            this.createHeader(), this.createBody(), t.appendChild(this.table), this.container.appendChild(t), 
            this.config.pagination.enable && this.createPagination();
        }
        createPagination() {
            const t = document.createElement("div");
            t.className = "table-pagination";
            const e = Math.ceil(this.config.data.length / this.config.pagination.pageSize);
            t.innerHTML = `\n                <div class="pagination-info">\n                    共 ${this.config.data.length} 条记录\n                </div>\n                <div class="pagination-controls">\n                    <button class="pagination-prev" ${this.config.pagination.currentPage <= 1 ? "disabled" : ""}>上一页</button>\n                    <span class="pagination-current">${this.config.pagination.currentPage}/${e}</span>\n                    <button class="pagination-next" ${this.config.pagination.currentPage >= e ? "disabled" : ""}>下一页</button>\n                </div>\n                ${this.config.pagination.showSizeChanger ? `\n                <div class="pagination-size">\n                    <select class="pagination-size-select">\n                        ${this.config.pagination.pageSizeOptions.map((t => `<option value="${t}" ${t === this.config.pagination.pageSize ? "selected" : ""}>${t}条/页</option>`)).join("")}\n                    </select>\n                </div>\n                ` : ""}\n            `, 
            t.querySelector(".pagination-prev").addEventListener("click", (() => {
                this.config.pagination.currentPage > 1 && (this.config.pagination.currentPage--, 
                this.update(this.config.data));
            })), t.querySelector(".pagination-next").addEventListener("click", (() => {
                this.config.pagination.currentPage < e && (this.config.pagination.currentPage++, 
                this.update(this.config.data));
            })), this.config.pagination.showSizeChanger && t.querySelector(".pagination-size-select").addEventListener("change", (t => {
                this.config.pagination.pageSize = parseInt(t.target.value), this.config.pagination.currentPage = 1, 
                this.update(this.config.data);
            })), this.container.appendChild(t);
        }
        createHeader() {
            const t = document.createElement("thead"), e = document.createElement("tr");
            if (this.config.columns.forEach((t => {
                if ("_index" === t.key) {
                    const n = document.createElement("th");
                    return n.textContent = "序号", n.style.width = t.width ? t.width : "80px", void e.appendChild(n);
                }
                const n = document.createElement("th");
                n.textContent = t.title || t.key, t.width && (n.style.width = t.width), t.headerClass && (n.className = t.headerClass), 
                e.appendChild(n);
            })), this.config.buttons && this.config.buttons.length > 0) {
                const t = document.createElement("th");
                t.textContent = "操作", this.config.buttonColumnWidth && (t.style.width = this.config.buttonColumnWidth), 
                e.appendChild(t);
            }
            t.appendChild(e), this.table.appendChild(t);
        }
        createBody() {
            const t = document.createElement("tbody");
            0 === this.config.data.length ? this.renderEmptyData(t) : this.renderDataRows(t), 
            this.table.appendChild(t);
        }
        renderEmptyData(t) {
            const e = document.createElement("tr"), n = document.createElement("td");
            n.colSpan = this.config.columns.length + (this.config.buttons.length > 0 ? 1 : 0), 
            n.textContent = "暂无数据", n.style.textAlign = "center", e.appendChild(n), t.appendChild(e);
        }
        renderDataRows(t) {
            let e = this.config.data;
            if (this.config.pagination.enable) {
                const t = (this.config.pagination.currentPage - 1) * this.config.pagination.pageSize, n = t + this.config.pagination.pageSize;
                e = this.config.data.slice(t, n);
            }
            e.forEach(((e, n) => {
                const a = document.createElement("tr");
                this.renderDataCells(a, e, n), this.config.buttons && this.config.buttons.length > 0 && this.renderButtonCells(a, e, n), 
                t.appendChild(a);
            }));
        }
        renderDataCells(t, e, n) {
            this.config.columns.forEach((a => {
                if ("_index" === a.key) {
                    const e = document.createElement("td"), a = this.config.pagination.currentPage || 1, i = this.config.pagination.pageSize || 10;
                    return e.textContent = (a - 1) * i + n + 1, void t.appendChild(e);
                }
                const i = document.createElement("td");
                a.render ? i.innerHTML = a.render(e, n) : i.textContent = e[a.key] || "", a.cellClass && (i.className = a.cellClass), 
                t.appendChild(i);
            }));
        }
        renderButtonCells(t, e, n) {
            const a = document.createElement("td");
            this.config.buttons.forEach((t => {
                const i = document.createElement("a");
                i.textContent = t.text, i.className = t.class || "a-primary", i.addEventListener("click", (a => {
                    if (t.onClick) {
                        const i = t.onClick.length;
                        3 === i ? t.onClick(a, e, n) : 2 === i ? t.onClick(a, e) : t.onClick(e);
                    }
                })), a.appendChild(i);
            })), t.appendChild(a);
        }
        update(t, e) {
            this.config.data = t, e && (this.config.pagination.currentPage = e), this.init();
        }
        getTableElement() {
            return this.table;
        }
    };
}(), function() {
    const t = (t, e, n, a, i) => {
        let s;
        "object" == typeof n ? s = n : (s = "object" == typeof a ? a : i || {}, s.gravity = n || "top", 
        s.position = "string" == typeof a ? a : "center"), s.gravity && "center" !== s.gravity || (s.offset = {
            y: "calc(50vh - 150px)"
        });
        const r = "#60A5FA", o = "#93C5FD", l = "#10B981", c = "#6EE7B7", d = "#EF4444", g = "#FCA5A5", p = {
            borderRadius: "12px",
            color: "white",
            padding: "12px 16px",
            boxShadow: "0 4px 6px rgba(0,0,0,0.1)",
            minWidth: "150px",
            textAlign: "center",
            zIndex: 999999999
        }, h = {
            text: t,
            duration: 2e3,
            close: !1,
            gravity: "top",
            position: "center",
            style: {
                info: {
                    ...p,
                    background: `linear-gradient(to right, ${r}, ${o})`
                },
                success: {
                    ...p,
                    background: `linear-gradient(to right, ${l}, ${c})`
                },
                error: {
                    ...p,
                    background: `linear-gradient(to right, ${d}, ${g})`
                }
            }[e],
            stopOnFocus: !0,
            oldestFirst: !1,
            ...s
        };
        Toastify(h).showToast();
    };
    window.show = {
        ok: (e, n = "center", a, i) => {
            t(e, "success", n, a, i);
        },
        error: (e, n = "center", a, i) => {
            t(e, "error", n, a, i);
        },
        info: (e, n = "center", a, i) => {
            t(e, "info", n, a, i);
        }
    };
}();

class PluginManager {
    constructor() {
        this.plugins = new Map;
    }
    register(t) {
        if ("function" != typeof t) throw new Error("插件必须是一个类");
        const e = t.name;
        if (!e) throw new Error("类必须要有名称");
        const n = e.toLowerCase();
        if (this.plugins.has(n)) throw new Error(`插件"${e}"已注册`);
        const a = new t;
        a.pluginManager = this, this.plugins.set(n, a);
    }
    getBean(t) {
        return this.plugins.get(t.toLowerCase());
    }
    _getDependencies(t) {
        const e = t.toString();
        return e.slice(e.indexOf("(") + 1, e.indexOf(")")).split(",").map((t => t.trim())).filter((t => t));
    }
    async process() {
        const t = (await Promise.allSettled(Array.from(this.plugins).map((async ([t, e]) => {
            try {
                if ("function" == typeof e.handle) {
                    const n = await e.initCss();
                    return utils.insertStyle(n), await e.handle(), {
                        name: t,
                        status: "fulfilled"
                    };
                }
                console.log("加载插件", t);
            } catch (n) {
                return console.error(`插件 ${t} 执行失败`, n), {
                    name: t,
                    status: "rejected",
                    error: n
                };
            }
        })))).filter((t => "rejected" === t.status));
        t.length && console.error("以下插件执行失败:", t.map((t => t.name))), document.body.classList.add("script-ready");
    }
}

class BasePlugin {
    constructor() {
        o(this, "pluginManager", null);
    }
    getBean(t) {
        let e = this.pluginManager.getBean(t);
        if (!e) {
            let e = "容器中不存在: " + t;
            throw show.error(e), new Error(e);
        }
        return e;
    }
    async initCss() {
        return "";
    }
    async handle() {}
    getPageInfo() {
        let t, e, n, a, i = window.location.href;
        return g && (t = $('a[title="複製番號"]').attr("data-clipboard-text"), e = i.split("?")[0].split("#")[0], 
        n = $(".female").prev().map(((t, e) => $(e).text())).get().join(" "), a = $(".male").prev().map(((t, e) => $(e).text())).get().join(" ")), 
        p && (e = i.split("?")[0], t = e.split("/").filter(Boolean).pop(), n = $('span[onmouseover*="star_"] a').map(((t, e) => $(e).text())).get().join(" "), 
        a = ""), {
            carNum: t,
            url: e,
            actress: n,
            actors: a
        };
    }
    getSelector() {
        if (g) return {
            boxSelector: ".movie-list",
            itemSelector: ".movie-list .item",
            coverImgSelector: ".cover img",
            requestDomItemSelector: ".movie-list .item",
            nextPageSelector: ".pagination-next"
        };
        if (p) return {
            boxSelector: ".masonry",
            itemSelector: ".masonry .item",
            coverImgSelector: ".photo-frame img",
            requestDomItemSelector: "#waterfall .item",
            nextPageSelector: "#next"
        };
        throw new Error("类型错误");
    }
    parseMovieId(t) {
        return t.split("/").pop().split(/[?#]/)[0];
    }
}

class DetailPagePlugin extends BasePlugin {
    constructor() {
        super();
    }
    handle() {
        window.isDetailPage && $(".video-meta-panel a").attr("target", "_blank");
    }
}

const N = async t => {
    const e = `dmm_video_urls_${t}`, n = sessionStorage.getItem(e);
    if (n) return JSON.parse(n);
    const a = `https://www.dmm.co.jp/search/=/searchstr=${t}`, i = await gmHttp.get(a);
    if (i.includes("このサービスはお住まいの地域からは")) return show.error("节点不可用,请分流到日本ip"), null;
    let s = $(i).find('a[href*="litevideo/freepv"]:first').attr("href");
    if (!s) return show.error("解析dmm失败, 该番号可能没有预览视频, " + a), null;
    let r = s.split("/"), o = r[r.length - 2];
    const l = o.charAt(0);
    let c = o.substring(0, 3);
    const d = [ "hhb", "hmb", "mhb", "mmb" ], g = {};
    for (const u of d) g[u] = `https://cc3001.dmm.co.jp/litevideo/freepv/${l}/${c}/${o}/${o}${u}.mp4`;
    const p = {
        403: "节点不可用,请分流到日本ip",
        404: "其它画质资源不存在",
        null: "网络错误"
    };
    try {
        const t = Object.entries(g).map((([t, e]) => gmHttp.checkUrlStatus(e).then((n => ({
            type: t,
            url: e,
            status: n
        }))).catch((n => (console.error(n), {
            type: t,
            url: e,
            status: null
        }))))), n = await Promise.all(t), a = {}, i = new Set;
        for (const {type: e, url: r, status: o} of n) if (200 === o) a[e] = r; else {
            const t = p[o] || `未知错误状态码: ${o}`;
            i.add(t);
        }
        let s = Object.keys(a).length;
        return 0 === s && i.size > 0 && i.forEach((t => show.error(t))), Object.values(a).some((t => "" !== t)) && sessionStorage.setItem(e, JSON.stringify(a)), 
        s > 0 ? a : null;
    } catch (h) {
        return console.error("并行检查URL时出错:", h), show.error(p.null), null;
    }
};

class PreviewVideoPlugin extends BasePlugin {
    async initCss() {
        return "\n            .video-control-btn {\n                position: absolute;\n                z-index: 99999999999;\n                min-width:120px;\n                padding: 8px 16px;\n                background: rgba(0,0,0,0.7);\n                color: white;\n                border: none;\n                border-radius: 4px;\n                cursor: pointer;\n            }\n            .video-control-btn.active {\n                background-color: #1890ff; /* 选中按钮的背景色 */\n                color: white;             /* 选中按钮的文字颜色 */\n                font-weight: bold;        /* 加粗显示 */\n                border: 2px solid #096dd9; /* 边框样式 */\n            }\n        ";
    }
    handle() {
        let t = $(".preview-video-container");
        t.on("click", (t => {
            utils.loopDetector((() => $(".fancybox-content #preview-video").length > 0), (() => {
                this.handleVideo().then();
            }));
        }));
        let e = window.location.href;
        (e.includes("gallery-1") || e.includes("gallery-2")) && utils.loopDetector((() => $(".fancybox-content #preview-video").length > 0), (() => {
            $(".fancybox-content #preview-video").length > 0 && this.handleVideo().then();
        })), e.includes("autoPlay=1") && t[0].click();
    }
    async handleVideo() {
        const t = $("#preview-video"), e = t.find("source"), n = t.parent();
        if (n.css("position", "relative"), !t.length || !e.length) return;
        e.attr("src");
        const a = t[0];
        a.muted = !1, a.play();
        let i = this.getPageInfo().carNum;
        const s = await N(i);
        let r = "";
        const o = "-133";
        if (s) {
            let t = await storageManager.getSetting("videoQuality") || "hhb";
            s[t] || (t = Object.keys(s)[0]);
            let n = s[t];
            e.attr("src", n);
            let a = 0;
            [ {
                id: "video-mmb",
                quality: "mmb",
                text: "中画质 (432p)"
            }, {
                id: "video-mhb",
                quality: "mhb",
                text: "高画质 (576p)"
            }, {
                id: "video-hmb",
                quality: "hmb",
                text: "HD (720p)"
            }, {
                id: "video-hhb",
                quality: "hhb",
                text: "FullHD (1080p)"
            } ].forEach((e => {
                let n = s[e.quality];
                if (n) {
                    const i = t === e.quality;
                    r += `\n                    <button class="video-control-btn${i ? " active" : ""}" \n                            id="${e.id}" \n                            data-quality="${e.quality}"\n                            data-video-src="${n}"\n                            style="bottom: ${50 * a}px; right: ${o}px;">\n                        ${e.text}\n                    </button>\n                `, 
                    a++;
                }
            }));
        }
        let l = s ? Object.keys(s).length : 0;
        r = `<button class="menu-btn" id="speed-btn" style="position: absolute; min-width: 120px; background-color:#76b45d;bottom: ${50 * (l + 2)}px; right: ${o + "px"};">快进(z)</button>` + r, 
        r = `<button class="menu-btn" id="video-filterBtn" style="position: absolute; min-width: 120px; background-color:#de3333;bottom: ${50 * (l + 1)}px; right: ${o + "px"};">屏蔽(a)</button>` + r, 
        r = `<button class="menu-btn" id="video-favoriteBtn" style="position: absolute; min-width: 120px; background-color:#25b1dc;bottom: ${50 * l}px; right: ${o + "px"};">收藏(s)</button>` + r, 
        n.append(r);
        const c = n.find(".video-control-btn");
        n.on("click", ".video-control-btn", (async t => {
            const n = $(t.currentTarget), i = n.data("video-src");
            if (!n.hasClass("active")) try {
                e.attr("src", i), a.load(), a.muted = !1, await a.play(), c.removeClass("active"), 
                n.addClass("active");
            } catch (s) {
                console.error("切换画质失败:", s);
            }
        })), $("#speed-btn").on("click", (() => {
            this.getBean("DetailPageButtonPlugin").speedVideo();
        })), utils.rightClick($("#speed-btn"), (t => {
            this.getBean("DetailPageButtonPlugin").filterOne(t);
        })), $("#video-filterBtn").on("click", (t => {
            this.getBean("DetailPageButtonPlugin").filterOne(t);
        })), $("#video-favoriteBtn").on("click", (t => {
            this.getBean("DetailPageButtonPlugin").favoriteOne(t);
        }));
    }
}

const z = class _HotkeyManager {
    constructor() {
        if (new.target === _HotkeyManager) throw new Error("HotkeyManager cannot be instantiated.");
    }
    static registerHotkey(t, e, n = null) {
        if (Array.isArray(t)) {
            let a = [];
            return t.forEach((t => {
                if (!this.isHotkeyFormat(t)) throw new Error("快捷键格式错误");
                let i = this.recordHotkey(t, e, n);
                a.push(i);
            })), a;
        }
        if (!this.isHotkeyFormat(t)) throw new Error("快捷键格式错误");
        return this.recordHotkey(t, e, n);
    }
    static recordHotkey(t, e, n) {
        let a = Math.random().toString(36).substr(2);
        return this.registerHotKeyMap.set(a, {
            hotkeyString: t,
            callback: e,
            keyupCallback: n
        }), a;
    }
    static unregisterHotkey(t) {
        this.registerHotKeyMap.has(t) && this.registerHotKeyMap.delete(t);
    }
    static isHotkeyFormat(t) {
        return t.toLowerCase().split("+").map((t => t.trim())).every((t => [ "ctrl", "shift", "alt" ].includes(t) || 1 === t.length));
    }
    static judgeHotkey(t, e) {
        const n = t.toLowerCase().split("+").map((t => t.trim())), a = n.includes("ctrl"), i = n.includes("shift"), s = n.includes("alt"), r = n.find((t => "ctrl" !== t && "shift" !== t && "alt" !== t));
        return (this.isMac ? e.metaKey : e.ctrlKey) === a && e.shiftKey === i && e.altKey === s && e.key.toLowerCase() === r;
    }
};

o(z, "isMac", 0 === navigator.platform.indexOf("Mac")), o(z, "registerHotKeyMap", new Map), 
o(z, "handleKeydown", (t => {
    for (const [e, n] of z.registerHotKeyMap) {
        let e = n.hotkeyString, a = n.callback;
        z.judgeHotkey(e, t) && a(t);
    }
})), o(z, "handleKeyup", (t => {
    for (const [e, n] of z.registerHotKeyMap) {
        let e = n.hotkeyString, a = n.keyupCallback;
        a && (z.judgeHotkey(e, t) && a(t));
    }
}));

let j = z;

document.addEventListener("keydown", (t => {
    j.handleKeydown(t);
})), document.addEventListener("keyup", (t => {
    j.handleKeyup(t);
}));

class JavTrailersPlugin extends BasePlugin {
    constructor() {
        super(), this.hasBand = !1;
    }
    handle() {
        let t = window.location.href;
        if (!t.includes("handle=1")) return;
        if ($("h1:contains('Page not found')").length) {
            console.log("番号无法匹配, 跳搜索");
            let e = t.split("?")[0].split("video/")[1].toLowerCase().replace("00", "-");
            return void (window.location.href = "/search/" + e + "?handle=1");
        }
        let e = $(".videos-list .video-link").toArray();
        if (e.length) {
            const n = t.split("?")[0].split("search/")[1].toLowerCase(), a = e.find((t => $(t).find(".vid-title").text().toLowerCase().includes(n)));
            if (a) return console.log("点击搜索页的第一个"), void (window.location.href = $(a).attr("href") + "?handle=1");
        }
        this.handlePlayJavTrailers(), $("#videoPlayerContainer").on("click", (() => {
            this.handlePlayJavTrailers();
        })), window.addEventListener("message", (t => {
            let e = document.getElementById("vjs_video_3_html5_api");
            e && (e.currentTime += 5);
        })), j.registerHotkey("z", (() => {
            const t = document.getElementById("vjs_video_3_html5_api");
            t && (t.currentTime += 5);
        })), j.registerHotkey("a", (() => window.parent.postMessage("a", "*"))), j.registerHotkey("s", (() => window.parent.postMessage("s", "*")));
    }
    handlePlayJavTrailers() {
        console.log("进入"), this.hasBand || (utils.loopDetector((() => 0 !== $("#vjs_video_3_html5_api").length), (() => {
            setTimeout((() => {
                this.hasBand = !0;
                let t = document.getElementById("vjs_video_3_html5_api");
                console.log(t), t.play(), t.currentTime = 5, t.addEventListener("timeupdate", (function() {
                    t.currentTime >= 14 && t.currentTime < 16 && (t.currentTime += 2);
                })), $("#vjs_video_3_html5_api").css({
                    position: "fixed",
                    width: "100vw",
                    height: "100vh",
                    objectFit: "cover",
                    zIndex: "999999999"
                }), $(".vjs-control-bar").css({
                    position: "fixed",
                    bottom: "20px",
                    zIndex: "999999999"
                });
            }), 100);
        })), utils.loopDetector((() => $("#vjs_video_3 canvas").length > 0), (() => {
            0 !== $("#vjs_video_3 canvas").length && $("#vjs_video_3 canvas").css({
                position: "fixed",
                width: "100vw",
                height: "100vh",
                objectFit: "cover",
                top: "0",
                right: "0",
                zIndex: "999999998"
            });
        })));
    }
}

class SubTitleCatPlugin extends BasePlugin {
    handle() {
        $(".t-banner-inner").hide(), $("#navbar").hide();
        let t = window.location.href.split("=")[1].toLowerCase();
        $(".sub-table tr td a").toArray().forEach((e => {
            let n = $(e);
            n.text().toLowerCase().includes(t) || n.parent().parent().hide();
        }));
    }
}

const U = "https://jdforrepam.com/api";

async function R() {
    const t = Math.floor(Date.now() / 1e3);
    if (t - (await storageManager.getItem(storageManager.review_ts_key) || 0) <= 20) return await storageManager.getItem(storageManager.review_sign_key);
    const e = `${t}.lpw6vgqzsp.${md5(`${t}71cf27bb3c0bcdf207b64abecddc970098c7421ee7203b9cdae54478478a199e7d5a6e1a57691123c1a931c057842fb73ba3b3c83bcd69c17ccf174081e3d8aa`)}`;
    return await storageManager.setItem(storageManager.review_ts_key, t), await storageManager.setItem(storageManager.review_sign_key, e), 
    e;
}

const O = async (t, e = 1, n = 20) => {
    let a = `${U}/v1/movies/${t}/reviews`, i = {
        jdSignature: await R()
    };
    return (await http.get(a, {
        page: e,
        sort_by: "hotly",
        limit: n
    }, i)).data.reviews;
}, W = async t => {
    let e = `${U}/v4/movies/${t}`, n = {
        jdSignature: await R()
    };
    const a = await http.get(e, null, n);
    if (!a.data) throw show.error("获取视频详情失败: " + a.message), new Error(a.message);
    const i = a.data.movie, s = i.preview_images, r = [];
    return s.forEach((t => {
        r.push(t.large_url.replace("https://tp-iu.cmastd.com/rhe951l4q", "https://c0.jdbstatic.com"));
    })), {
        movieId: i.id,
        actors: i.actors,
        title: i.origin_title,
        carNum: i.number,
        score: i.score,
        releaseDate: i.release_date,
        watchedCount: i.watched_count,
        imgList: r
    };
}, q = async (t, e = 1, n = 20) => {
    let a = `${U}/v1/lists/related?movie_id=${t}&page=${e}&limit=${n}`, i = {
        jdSignature: await R()
    };
    const s = await gmHttp.get(a, null, i), r = [];
    return s.data.lists.forEach((t => {
        r.push({
            relatedId: t.id,
            name: t.name,
            movieCount: t.movies_count,
            collectionCount: t.collections_count,
            viewCount: t.views_count,
            createTime: utils.formatDate(t.created_at)
        });
    })), r;
};

class Fc2Plugin extends BasePlugin {
    handle() {
        let t = "/advanced_search?type=3&score_min=3&d=1";
        if ($('.navbar-item:contains("FC2")').attr("href", t), $('.tabs a:contains("FC2")').attr("href", t), 
        window.location.href.includes("collection_codes?movieId")) {
            $("section").html("");
            const t = new URLSearchParams(window.location.search);
            let e = t.get("movieId"), n = t.get("carNum"), a = t.get("url");
            e && n && a && this.openFc2Dialog(e, n, a);
        }
    }
    async initCss() {
        return "\n            /* 弹层样式 */\n            .movie-detail-layer .layui-layer-title {\n                font-size: 18px;\n                color: #333;\n                background: #f8f8f8;\n            }\n            \n            \n            /* 容器样式 */\n            .movie-detail-container {\n                display: flex;\n                height: 100%;\n                background: #fff;\n            }\n            \n            .movie-poster-container {\n                flex: 0 0 60%;\n                padding: 15px;\n            }\n            \n            .right-box {\n                flex: 1;\n                padding: 20px;\n                overflow-y: auto;\n            }\n            \n            /* 预告片iframe */\n            .movie-trailer {\n                width: 100%;\n                height: 100%;\n                min-height: 400px;\n                background: #000;\n                border-radius: 4px;\n            }\n            \n            /* 电影信息样式 */\n            .movie-title {\n                font-size: 24px;\n                margin-bottom: 15px;\n                color: #333;\n            }\n            \n            .movie-meta {\n                margin-bottom: 20px;\n                color: #666;\n            }\n            \n            .movie-meta span {\n                margin-right: 15px;\n            }\n            \n            /* 演员列表 */\n            .actor-list {\n                display: flex;\n                flex-wrap: wrap;\n                gap: 8px;\n                margin-top: 10px;\n            }\n            \n            .actor-tag {\n                padding: 4px 12px;\n                background: #f0f0f0;\n                border-radius: 15px;\n                font-size: 12px;\n                color: #555;\n            }\n            \n            /* 图片列表 */\n            .image-list {\n                display: flex;\n                flex-wrap: wrap;\n                gap: 10px;\n                margin-top: 10px;\n            }\n            \n            .movie-image-thumb {\n                width: 120px;\n                height: 80px;\n                object-fit: cover;\n                border-radius: 4px;\n                cursor: pointer;\n                transition: transform 0.3s;\n            }\n            \n            .movie-image-thumb:hover {\n                transform: scale(1.05);\n            }\n            \n            /* 加载中和错误状态 */\n            .search-loading, .movie-error {\n                padding: 40px;\n                text-align: center;\n                color: #999;\n            }\n            \n            .movie-error {\n                color: #f56c6c;\n            }\n            \n            .fancybox-container{\n                z-index:99999999\n             }\n             \n             \n             /* 错误提示样式 */\n            .movie-not-found, .movie-error {\n                text-align: center;\n                padding: 30px;\n                color: #666;\n            }\n            \n            .movie-not-found h3, .movie-error h3 {\n                color: #f56c6c;\n                margin: 15px 0;\n            }\n            \n            .icon-warning, .icon-error {\n                font-size: 50px;\n                color: #e6a23c;\n            }\n            \n            .icon-error {\n                color: #f56c6c;\n            }\n\n        ";
    }
    openFc2Dialog(t, e, n) {
        if (n.includes("123av")) return void this.getBean("Fc2By123AvPlugin").open123AvFc2Dialog(e, n);
        let a = `\n            <div class="movie-detail-container">\n                <div class="movie-poster-container">\n                    <iframe class="movie-trailer" frameborder="0" allowfullscreen scrolling="no"></iframe>\n                </div>\n                <div class="right-box">\n                    <div class="movie-info-container">\n                        <div class="search-loading">加载中...</div>\n                    </div>\n                    <div style="margin: 10px 0">\n                        <a id="filterBtn" class="menu-btn" style="background-color:${y}"><span>${w}</span></a>\n                        <a id="favoriteBtn" class="menu-btn" style="background-color:${_}"><span>${x}</span></a>\n                        <a id="hasDownBtn" class="menu-btn" style="background-color:${S}"><span>${C}</span></a>\n                        <a id="hasWatchBtn" class="menu-btn" style="background-color:${B};"><span>${P}</span></a>\n                        \n\x3c!--                        <a id="enable-magnets-filter" class="menu-btn fr-btn" style="background-color:#c2bd4c">--\x3e\n\x3c!--                            <span id="magnets-span">关闭磁力过滤</span>--\x3e\n\x3c!--                        </a>--\x3e\n\n                        <a id="search-subtitle-btn" class="menu-btn fr-btn" style="background:linear-gradient(to bottom, #8d5656, rgb(196,159,91))">\n                            <span>字幕 (SubTitleCat)</span>\n                        </a>\n                        <a id="xunLeiSubtitleBtn" class="menu-btn fr-btn" style="background:linear-gradient(to left, #375f7c, #2196F3)">\n                            <span>字幕 (迅雷)</span>\n                        </a>\n                    </div>\n                    <div class="message video-panel" style="margin-top:20px">\n                        <div id="magnets-content" class="magnet-links" style="margin: 0 0.75rem">\n                            <div class="search-loading">加载中...</div>\n                        </div>\n                    </div>\n                    <div id="reviews-content">\n                    </div>\n                    <div id="related-content">\n                    </div>\n                    <span id="data-actress" style="display: none"></span>\n                </div>\n            </div>\n        `;
        layer.open({
            type: 1,
            title: e,
            content: a,
            area: [ "80%", "90%" ],
            skin: "movie-detail-layer",
            scrollbar: !1,
            success: (a, i) => {
                this.loadData(t, e), $("#favoriteBtn").on("click", (async t => {
                    const a = $("#data-actress").text();
                    await storageManager.saveCar(e, n, a, m), window.refresh(), layer.closeAll();
                })), $("#filterBtn").on("click", (t => {
                    utils.q(t, `是否屏蔽${e}?`, (async () => {
                        const t = $("#data-actress").text();
                        await storageManager.saveCar(e, n, t, u), window.refresh(), layer.closeAll(), window.location.href.includes("collection_codes?movieId") && utils.closePage();
                    }));
                })), $("#hasDownBtn").on("click", (async t => {
                    const a = $("#data-actress").text();
                    await storageManager.saveCar(e, n, a, f), window.refresh(), layer.closeAll();
                })), $("#hasWatchBtn").on("click", (async t => {
                    const a = $("#data-actress").text();
                    await storageManager.saveCar(e, n, a, v), window.refresh(), layer.closeAll();
                })), $("#enable-magnets-filter").on("click", (t => {
                    let e = $("#magnets-span");
                    const n = this.getBean("HighlightMagnetPlugin");
                    "关闭磁力过滤" === e.text() ? (n.showAll(), e.text("开启磁力过滤")) : (n.handle(), e.text("关闭磁力过滤"));
                })), $("#search-subtitle-btn").on("click", (t => utils.openPage(`https://subtitlecat.com/index.php?search=${e}`, e, !1, t))), 
                $("#xunLeiSubtitleBtn").on("click", (() => this.getBean("DetailPageButtonPlugin").searchXunLeiSubtitle(e)));
            },
            end() {
                window.location.href.includes("collection_codes?movieId") && utils.closePage();
            }
        });
    }
    loadData(t, e) {
        this.handleVideo(e.replace("FC2-", "")), this.handleMovieDetail(t), this.handleMagnets(t);
        this.getBean("reviewPlugin").showReview(t, $("#reviews-content")).then(), this.getBean("RelatedPlugin").showRelated($("#related-content")).then();
    }
    handleMovieDetail(t) {
        W(t).then((t => {
            const e = t.actors || [], n = t.imgList || [];
            let a = "";
            if (e.length > 0) {
                let t = "";
                for (let n = 0; n < e.length; n++) {
                    let i = e[n];
                    a += `<span class="actor-tag"><a href="/actors/${i.id}" target="_blank">${i.name}</a></span>`, 
                    0 === i.gender && (t += i.name + " ");
                }
                $("#data-actress").text(t);
            } else a = '<span class="no-data">暂无演员信息</span>';
            let i = "";
            i = Array.isArray(n) && n.length > 0 ? n.map(((t, e) => `\n                <a href="${t}" data-fancybox="movie-gallery" data-caption="剧照 ${e + 1}">\n                    <img src="${t}" class="movie-image-thumb"  alt=""/>\n                </a>\n            `)).join("") : '<div class="no-data">暂无剧照</div>', 
            $(".movie-info-container").html(`\n                <h3 class="movie-title">${t.title || "无标题"}</h3>\n                <div class="movie-meta">\n                    <span>番号: ${t.carNum || "未知"}</span>\n                    <span>年份: ${t.releaseDate || "未知"}</span>\n                    <span>评分: ${t.score || "无"}</span>\n                </div>\n                <div class="movie-meta">\n                    <span>\n                        站点: \n                        <a href="https://fc2ppvdb.com/articles/${t.carNum.replace("FC2-", "")}" target="_blank">fc2ppvdb</a>\n                        <a style="margin-left: 5px;" href="https://adult.contents.fc2.com/article/${t.carNum.replace("FC2-", "")}/" target="_blank">fc2电子市场</a>\n                    </span>\n                </div>\n                <div class="movie-actors">\n                    <div class="actor-list">主演: ${a}</div>\n                </div>\n                <div class="movie-gallery" style="margin-top:10px">\n                    <h4>剧照: </h4>\n                    <div class="image-list">${i}</div>\n                </div>\n            `);
        })).catch((t => {
            console.error(t), $(".movie-info-container").html(`\n                <div class="movie-error">加载失败: ${t.message}</div>\n            `);
        }));
    }
    handleMagnets(t) {
        (async t => {
            let e = `${U}/v1/movies/${t}/magnets`, n = {
                jdSignature: await R()
            };
            return (await http.get(e, null, n)).data.magnets;
        })(t).then((t => {
            let e = "";
            if (t.length > 0) for (let n = 0; n < t.length; n++) {
                let a = t[n], i = "";
                n % 2 == 0 && (i = "odd"), e += `\n                        <div class="item columns is-desktop ${i}">\n                            <div class="magnet-name column is-four-fifths">\n                                <a href="magnet:?xt=urn:btih:${a.hash}" title="右鍵點擊並選擇「複製鏈接地址」">\n                                    <span class="name">${a.name}</span>\n                                    <br>\n                                    <span class="meta">\n                                        ${(a.size / 1024).toFixed(2)}GB, ${a.files_count}個文件 \n                                     </span>\n                                    <br>\n                                    <div class="tags">\n                                        ${a.hd ? '<span class="tag is-primary is-small is-light">高清</span>' : ""}\n                                        ${a.cnsub ? '<span class="tag is-warning is-small is-light">字幕</span>' : ""}\n                                    </div>\n                                </a>\n                            </div>\n                            <div class="buttons column">\n                                <button class="button is-info is-small copy-to-clipboard" data-clipboard-text="magnet:?xt=urn:btih:${a.hash}" type="button">&nbsp;複製&nbsp;</button>\n                            </div>\n                            <div class="date column"><span class="time">${a.created_at}</span></div>\n                        </div>\n                    `;
            } else e = '<span class="no-data">暂无磁力信息</span>';
            $("#magnets-content").html(e), this.getBean("HighlightMagnetPlugin").handleDb();
        })).catch((t => {
            console.error(t), $("#magnets-content").html(`\n                <div class="movie-error">加载失败: ${t.message}</div>\n            `);
        }));
    }
    async handleVideo(t) {
        const e = this.getBean("Fc2By123AvPlugin");
        let n = loading();
        try {
            const n = await e.getBaseUrl();
            let a = `${n}/search?keyword=${t}`;
            const i = await gmHttp.get(a);
            const s = $(i).find(".box-item");
            if (0 === s.length) throw new Error("搜索无结果");
            for (let r = 0; r < s.length; r++) {
                const a = $(s[r]);
                let i = a.find("img").attr("title");
                const o = a.find(".detail a").attr("href"), l = n + (o.startsWith("/") ? o : "/" + o);
                if (i && i.includes(t)) {
                    const {id: t, publishDate: n, title: a, moviePoster: i} = await e.get123AvVideoInfo(l), s = await e.getMovie(t, i);
                    if (s.length > 0) {
                        $(".movie-trailer").attr("src", s[0].url);
                        let t = '\n                            <div class="movie-gallery" style="margin-bottom: 10px"> \n                            <span>影片: </span> \n                            <div class="movie-parts-list">\n                        ';
                        s.forEach(((e, n) => {
                            t += `\n                                <a class="movie-part a-outline" data-url="${e.url}" style="margin-left: 0">\n                                    部分 ${n + 1}\n                                </a>\n                            `;
                        })), t += "</div> </div> ", $(".movie-gallery").after(t), $(".movie-parts-list").on("click", ".movie-part", (function() {
                            const t = $(this).data("url");
                            $(".movie-trailer").attr("src", t);
                        }));
                        break;
                    }
                }
            }
        } catch (a) {
            console.error(a);
            const e = this.getBean("OtherSitePlugin"), n = await e.getMissAvUrl();
            $(".movie-poster-container").html(`\n                <div class="movie-not-found">\n                    <i class="icon-warning"></i>\n                    <h3>未找到相关视频信息</h3>\n                    <p>123Av 中没有找到与当前番号相关的影片信息</p>\n                    <p style="margin:20px">请尝试以下网站</p>\n                    <p><a class="menu-btn" style="background:linear-gradient(to right, #d29494, rgb(254,98,142))" href="${n}/dm3/fc2-ppv-${t}" target="_blank">missav</a></p>\n                </div>\n            `), 
            $(".movie-trailer").hide();
        } finally {
            n.close();
        }
    }
    async openFc2Page(t, e, n) {
        const a = this.getBean("OtherSitePlugin");
        let i = await a.getJavDbUrl();
        window.open(`${i}/users/collection_codes?movieId=${t}&carNum=${e}&url=${n}`);
    }
}

class FoldCategoryPlugin extends BasePlugin {
    async handle() {
        if (!window.isListPage) return;
        let t, e = $(".tabs ul");
        if (e.length > 0) {
            t = $("#tags");
            let n = $("#tags dl div.tag.is-info").map((function() {
                return $(this).text().replaceAll("\n", "").replaceAll(" ", "");
            })).get().join(" ");
            if (!n) return;
            e.append('\n                <li class="is-active" id="foldCategoryBtn">\n                    <a class="menu-btn" style="background-color:#d23e60 !important;margin-left: 20px;border-bottom:none !important;border-radius:3px;">\n                        <span></span>\n                        <i style="margin-left: 10px"></i>\n                    </a>\n                </li>\n            '), 
            $(".tabs").append(`<div style="padding-top:10px"><span>已选分类: ${n}</span></div>`);
        }
        let n = $("h2.section-title");
        if (n.length > 0 && (n.append('\n                <div id="foldCategoryBtn">\n                    <a class="menu-btn" style="background-color:#d23e60 !important;margin-left: 20px;border-bottom:none !important;border-radius:3px;">\n                        <span></span>\n                        <i style="margin-left: 10px"></i>\n                    </a>\n                </div>\n            '), 
        t = $("section > div > div.box")), !t) return;
        let a = $("#foldCategoryBtn"), i = "yes" === await storageManager.getItem(storageManager.fold_category_key), [s, r] = i ? [ "展开", "icon-angle-double-down" ] : [ "折叠", "icon-angle-double-up" ];
        a.find("span").text(s).end().find("i").attr("class", r), window.location.href.includes("noFold=1") || t[i ? "hide" : "show"](), 
        a.on("click", (async e => {
            e.preventDefault(), i = !i, await storageManager.setItem(storageManager.fold_category_key, i ? "yes" : "no");
            const [n, s] = i ? [ "展开", "icon-angle-double-down" ] : [ "折叠", "icon-angle-double-up" ];
            a.find("span").text(n).end().find("i").attr("class", s), t[i ? "hide" : "show"]();
        }));
    }
}

class ActressInfoPlugin extends BasePlugin {
    constructor() {
        super(...arguments), o(this, "apiUrl", "https://ja.wikipedia.org/wiki/");
    }
    handle() {
        this.handleDetailPage().then(), this.handleStarPage().then();
    }
    async initCss() {
        return "\n            <style>\n                .info-tag {\n                    background-color: #ecf5ff;\n                    display: inline-block;\n                    height: 32px;\n                    padding: 0 10px;\n                    line-height: 30px;\n                    font-size: 12px;\n                    color: #409eff;\n                    border: 1px solid #d9ecff;\n                    border-radius: 4px;\n                    box-sizing: border-box;\n                    white-space: nowrap;\n                }\n            </style>\n        ";
    }
    async handleDetailPage() {
        let t = $(".female").prev().map(((t, e) => $(e).text().trim())).get();
        if (!t.length) return;
        let e = null, n = "";
        for (let i = 0; i < t.length; i++) {
            let s = t[i];
            if (e = await storageManager.getItem(storageManager.actress_prefix_key + s), !e) try {
                e = await this.searchInfo(s), e && await storageManager.setItem(storageManager.actress_prefix_key + s, e, D);
            } catch (a) {
                console.error("该名称查询失败,尝试其它名称");
            }
            let r = "";
            r = e ? `\n                    <div class="panel-block">\n                        <strong>${s}:</strong>\n                        <a href="${e.url}" style="margin-left: 5px" target="_blank">\n                            <span class="info-tag">${e.birthday} ${e.age}</span>\n                            <span class="info-tag">${e.height} ${e.weight}</span>\n                            <span class="info-tag">${e.threeSizeText} ${e.braSize}</span>\n                        </a>\n                    </div>\n                ` : `<div class="panel-block"><a href="${this.apiUrl + s}" target="_blank"><strong>${s}:</strong></a></div> `, 
            n += r;
        }
        $('strong:contains("演員")').parent().after(n);
    }
    async handleStarPage() {
        let t = [], e = $(".actor-section-name");
        e.length && e.text().trim().split(",").forEach((e => {
            t.push(e.trim());
        }));
        let n = $(".section-meta:not(:contains('影片'))");
        if (n.length && n.text().trim().split(",").forEach((e => {
            t.push(e.trim());
        })), !t.length) return;
        let a = null;
        for (let r = 0; r < t.length; r++) {
            let e = t[r];
            if (a = await storageManager.getItem(storageManager.actress_prefix_key + e), a) break;
            try {
                a = await this.searchInfo(e);
            } catch (s) {
                console.error("该名称查询失败,尝试其它名称");
            }
            if (a) break;
        }
        a && t.forEach((t => {
            storageManager.setItem(storageManager.actress_prefix_key + t, a, D);
        }));
        let i = '<div style="font-size: 17px; font-weight: normal; margin-top: 5px;">无此相关演员信息</div>';
        a && (i = `\n                <a href="${a.url}" target="_blank">\n                    <div style="font-size: 17px; font-weight: normal; margin-top: 5px;">\n                        <div style="display: flex; margin-bottom: 10px;">\n                            <span style="width: 300px;">出生日期: ${a.birthday}</span>\n                            <span style="width: 200px;">年龄: ${a.age}</span>\n                            <span style="width: 200px;">身高: ${a.height}</span>\n                        </div>\n                        <div style="display: flex; margin-bottom: 10px;">\n                            <span style="width: 300px;">体重: ${a.weight}</span>\n                            <span style="width: 200px;">三围: ${a.threeSizeText}</span>\n                            <span style="width: 200px;">罩杯: ${a.braSize}</span>\n                        </div>\n                    </div>\n                </a>\n            `), 
        e.parent().append(i);
    }
    async searchInfo(t) {
        "三上悠亞" === t && (t = "三上悠亜");
        let e = this.apiUrl + t;
        const n = await gmHttp.get(e), a = new DOMParser, i = $(a.parseFromString(n, "text/html"));
        let s = i.find('a[title="誕生日"]').parent().parent().find("td").text().trim(), r = i.find("th:contains('現年齢')").parent().find("td").text().trim() ? parseInt(i.find("th:contains('現年齢')").parent().find("td").text().trim()) + "岁" : "", o = i.find('tr:has(a[title="身長"]) td').text().trim().split(" ")[0] + "cm", l = i.find('tr:has(a[title="体重"]) td').text().trim().split("/")[1].trim();
        return "― kg" === l && (l = ""), {
            birthday: s,
            age: r,
            height: o,
            weight: l,
            threeSizeText: i.find('a[title="スリーサイズ"]').closest("tr").find("td").text().replace("cm", "").trim(),
            braSize: i.find('th:contains("ブラサイズ")').next("td").contents().first().text().trim(),
            url: e
        };
    }
}

class AliyunPanPlugin extends BasePlugin {
    handle() {
        $("body").append('<a class="a-success" id="refresh-token-btn" style="position:fixed; right: 0; top:50%;z-index:99999">获取refresh_token</a>'), 
        $("#refresh-token-btn").on("click", (t => {
            let e = localStorage.getItem("token");
            if (!e) return void alert("请先登录!");
            let n = JSON.parse(e).refresh_token;
            navigator.clipboard.writeText(n).then((() => {
                alert("已复制到剪切板 如失败, 请手动复制: " + n);
            })).catch((t => {
                console.error("Failed to copy refresh token: ", t);
            }));
        }));
    }
}

class HitShowPlugin extends BasePlugin {
    constructor() {
        super();
    }
    handle() {
        $('a[href*="rankings/playback"]').on("click", (t => {
            t.preventDefault(), t.stopPropagation(), window.location.href = "/?handlePlayback=1&period=daily";
        })), this.handlePlayback().then();
    }
    async handlePlayback() {
        if (!window.location.href.includes("handlePlayback=1")) return;
        let t = new URLSearchParams(window.location.search).get("period");
        this.toolBar(t);
        let e = $(".movie-list");
        e.html("");
        let n = loading();
        try {
            const n = await (async (t = "daily", e = "high_score") => {
                let n = `${U}/v1/rankings/playback?period=${t}&filter_by=${e}`, a = {
                    jdSignature: await R()
                };
                return (await http.get(n, null, a)).data.movies;
            })(t);
            let a = this.markDataListHtml(n);
            e.html(a), window.refresh(), this.loadScore(n);
        } finally {
            n.close();
        }
    }
    toolBar(t) {
        $(".pagination").remove(), $(".main-tabs ul li").removeClass("is-active"), $(".main-tabs ul li:first").addClass("is-active");
        let e = `\n            <div class="button-group" style="margin-top:18px">\n                <div class="buttons has-addons" id="conditionBox">\n                    <a style="padding:18px 18px !important;" class="button is-small ${"daily" === t ? "is-info" : ""}" href="/?handlePlayback=1&period=daily">日榜</a>\n                    <a style="padding:18px 18px !important;" class="button is-small ${"weekly" === t ? "is-info" : ""}" href="/?handlePlayback=1&period=weekly">周榜</a>\n                    <a style="padding:18px 18px !important;" class="button is-small ${"monthly" === t ? "is-info" : ""}" href="/?handlePlayback=1&period=monthly">月榜</a>\n                </div>\n            </div>\n        `;
        $(".toolbar").html(e);
    }
    getStarRating(t) {
        let e = "";
        const n = Math.floor(t);
        for (let a = 0; a < n; a++) e += '<i class="icon-star"></i>';
        for (let a = 0; a < 5 - n; a++) e += '<i class="icon-star gray"></i>';
        return e;
    }
    loadScore(t) {
        if (0 === t.length) return;
        (async () => {
            const e = [];
            for (const a of t) try {
                const t = a.id;
                if ($(`#${t}`).is(":hidden")) continue;
                const e = await storageManager.getItem(storageManager.score_prefix_key + t);
                if (e) {
                    this.appendScoreHtml(t, e);
                    continue;
                }
                for (;!document.hasFocus(); ) await new Promise((t => setTimeout(t, 500)));
                const n = await W(t);
                let i = n.score, s = n.watchedCount, r = `\n                        <span class="value">\n                            <span class="score-stars">${this.getStarRating(i)}</span> \n                            &nbsp; ${i}分,由${s}人評價\n                        </span>\n                    `;
                this.appendScoreHtml(t, r), await storageManager.setItem(storageManager.score_prefix_key + t, r, 6048e5), 
                await new Promise((t => setTimeout(t, 1e3)));
            } catch (n) {
                e.push({
                    carNum: a.number,
                    error: n.message,
                    stack: n.stack
                }), console.error(`🚨 解析评分数据失败 | 编号: ${a.number}\n`, `错误详情: ${n.message}\n`, n.stack ? `调用栈:\n${n.stack}` : "");
            }
            e.length > 0 && (show.error("解析评分数据失败, 个数:", e.length), console.table(e));
        })();
    }
    appendScoreHtml(t, e) {
        let n = $(`#score_${t}`);
        "" === n.html().trim() && n.slideUp(0, (function() {
            $(this).html(e).slideDown(500);
        }));
    }
    markDataListHtml(t) {
        let e = "";
        return t.forEach((t => {
            e += `\n                <div class="item" id="${t.id}">\n                    <a href="/v/${t.id}" class="box" title="${t.origin_title}">\n                        <div class="cover ">\n                            <img loading="lazy" src="${t.cover_url.replace("https://tp-iu.cmastd.com/rhe951l4q", "https://c0.jdbstatic.com")}" alt="">\n                        </div>\n                        <div class="video-title"><strong>${t.number}</strong> ${t.origin_title}</div>\n                        <div class="score" id="score_${t.id}">\n                        </div>\n                        <div class="meta">\n                            ${t.release_date}\n                        </div>\n                        <div class="tags has-addons">\n                           ${t.has_cnsub ? '<span class="tag is-warning">含中字磁鏈</span>' : t.magnets_count > 0 ? '<span class="tag is-success">含磁鏈</span>' : '<span class="tag is-info">无磁鏈</span>'}\n                           ${t.new_magnets ? '<span class="tag is-info">今日新種</span>' : ""}\n                        </div>\n                    </a>\n                </div>\n            `;
        })), e;
    }
}

class TOP250Plugin extends BasePlugin {
    constructor() {
        super(), o(this, "has_cnsub", ""), o(this, "movies", []);
    }
    handle() {
        $('.main-tabs ul li:contains("猜你喜歡")').html('<a href="/rankings/top"><span>Top250</span></a>'), 
        $('a[href*="rankings/top"]').on("click", (t => {
            t.preventDefault(), t.stopPropagation();
            const e = $(t.target), n = (e.is("a") ? e : e.closest("a")).attr("href");
            let a = n.includes("?") ? n.split("?")[1] : n;
            const i = new URLSearchParams(a);
            this.checkLogin(t, i);
        })), this.handleTop().then();
    }
    async handleTop() {
        if (!window.location.href.includes("handleTop=1")) return;
        const t = new URLSearchParams(window.location.search);
        let e = t.get("type") || "all", n = t.get("type_value") || "";
        this.has_cnsub = t.get("has_cnsub") || "";
        let a = t.get("page") || 1;
        this.toolBar(e, n, a);
        let i = $(".movie-list");
        i.html("");
        let s = loading();
        try {
            const t = await (async (t = "all", e = "", n = 1, a = 40) => {
                let i = `${U}/v1/movies/top?start_rank=1&type=${t}&type_value=${e}&ignore_watched=false&page=${n}&limit=${a}`, s = {
                    "user-agent": "Dart/3.5 (dart:io)",
                    "accept-language": "zh-TW",
                    host: "jdforrepam.com",
                    authorization: "Bearer " + await storageManager.getItem("appAuthorization"),
                    jdsignature: await R()
                };
                return await gmHttp.get(i, null, s);
            })(e, n, a, 50);
            let s = t.success, r = t.message, o = t.action;
            if (1 === s) {
                let e = t.data.movies;
                if (0 === e.length) return void show.error("无数据");
                this.movies = e;
                const n = this.getBean("hitShowPlugin");
                let a = n.markDataListHtml(e);
                i.html(a), window.refresh(), "1" === this.has_cnsub ? ($(".item:contains('含中字磁鏈')").show(), 
                $(".item:contains('含磁鏈')").hide()) : "0" === this.has_cnsub ? ($(".item:contains('含中字磁鏈')").hide(), 
                $(".item:contains('含磁鏈')").show()) : ($(".item:contains('含中字磁鏈')").show(), $(".item:contains('含磁鏈')").show()), 
                n.loadScore(e);
            } else console.error(t), i.html(`<h3>${r}</h3>`), show.error(r);
            "JWTVerificationError" === o && (await storageManager.removeItem("appAuthorization"), 
            await this.checkLogin(null, new URLSearchParams(window.location.search)));
        } catch (r) {
            console.error("获取Top数据失败:", r), show.error(`获取Top数据失败: ${r ? r.message : r}`);
        } finally {
            s.close();
        }
    }
    toolBar(t, e, n) {
        $(".main-tabs ul li").removeClass("is-active"), $(".main-tabs ul li:eq(1)").addClass("is-active"), 
        "5" === n.toString() && $(".pagination-next").remove(), $(".pagination-ellipsis").closest("li").remove(), 
        $(".pagination-list li a").each((function() {
            parseInt($(this).text()) > 5 && $(this).closest("li").remove();
        }));
        let a = "";
        for (let s = (new Date).getFullYear(); s >= 2008; s--) a += `\n                <a style="padding:18px 18px !important;" \n                   class="button is-small ${e === s.toString() ? "is-info" : ""}" \n                   href="/?handleTop=1&type=year&type_value=${s}&has_cnsub=${this.has_cnsub}">\n                  ${s}\n                </a>\n            `;
        let i = `\n            <div class="button-group">\n                <div class="buttons has-addons" id="conditionBox" style="margin-bottom: 0!important;">\n                    <a style="padding:18px 18px !important;" class="button is-small ${"all" === t ? "is-info" : ""}" href="/?handleTop=1&type=all&type_value=&has_cnsub=${this.has_cnsub}">全部</a>\n                    <a style="padding:18px 18px !important;" class="button is-small ${"0" === e ? "is-info" : ""}" href="/?handleTop=1&type=video_type&type_value=0&has_cnsub=${this.has_cnsub}">有码</a>\n                    <a style="padding:18px 18px !important;" class="button is-small ${"1" === e ? "is-info" : ""}" href="/?handleTop=1&type=video_type&type_value=1&has_cnsub=${this.has_cnsub}">无码</a>\n                    <a style="padding:18px 18px !important;" class="button is-small ${"2" === e ? "is-info" : ""}" href="/?handleTop=1&type=video_type&type_value=2&has_cnsub=${this.has_cnsub}">欧美</a>\n                    <a style="padding:18px 18px !important;" class="button is-small ${"3" === e ? "is-info" : ""}" href="/?handleTop=1&type=video_type&type_value=3&has_cnsub=${this.has_cnsub}">Fc2</a>\n                    \n                    <a style="padding:18px 18px !important;margin-left: 50px" class="button is-small ${"1" === this.has_cnsub ? "is-info" : ""}" data-cnsub-value="1">含中字磁鏈</a>\n                    <a style="padding:18px 18px !important;" class="button is-small ${"0" === this.has_cnsub ? "is-info" : ""}" data-cnsub-value="0">无字幕</a>\n                    <a style="padding:18px 18px !important;" class="button is-small" data-cnsub-value="">重置</a>\n                </div>\n                \n                <div class="buttons has-addons" id="conditionBox">\n                    ${a}\n                </div>\n            </div>\n        `;
        $(".toolbar").html(i), $("a[data-cnsub-value]").on("click", (t => {
            const e = $(t.currentTarget).data("cnsub-value");
            this.has_cnsub = e.toString(), $("a[data-cnsub-value]").removeClass("is-info"), 
            $(t.currentTarget).addClass("is-info"), $(".toolbar a.button").not("[data-cnsub-value]").each(((t, n) => {
                const a = $(n), i = new URL(a.attr("href"), window.location.origin);
                i.searchParams.set("has_cnsub", e), a.attr("href", i.toString());
            }));
            const n = new URL(window.location.href);
            n.searchParams.set("has_cnsub", e), history.pushState({}, "", n.toString()), "1" === this.has_cnsub ? ($(".item:contains('含中字磁鏈')").show(), 
            $(".item:contains('含磁鏈')").hide()) : "0" === this.has_cnsub ? ($(".item:contains('含中字磁鏈')").hide(), 
            $(".item:contains('含磁鏈')").show()) : ($(".item:contains('含中字磁鏈')").show(), $(".item:contains('含磁鏈')").show());
            this.getBean("hitShowPlugin").loadScore(this.movies);
        }));
    }
    async checkLogin(t, e) {
        if (!(await storageManager.getItem("appAuthorization"))) return show.error("未登录手机端接口, 无法查看"), 
        void this.openLoginDialog();
        let n = "all", a = "", i = e.get("t") || "";
        /^y\d+$/.test(i) ? (n = "year", a = i.substring(1)) : "" !== i && (n = "video_type", 
        a = i);
        let s = `/?handleTop=1&type=${n}&type_value=${a}`;
        t && (t.ctrlKey || t.metaKey) ? GM_openInTab(window.location.origin + s, {
            insert: 0
        }) : window.location.href = s;
    }
    openLoginDialog() {
        layer.open({
            type: 1,
            title: "JavDB",
            closeBtn: 1,
            area: [ "360px", "auto" ],
            shadeClose: !1,
            content: '\n                <div style="padding: 30px; font-family: \'Helvetica Neue\', Arial, sans-serif;">\n                    <div style="margin-bottom: 25px;">\n                        <input type="text" id="username" name="username" \n                            style="width: 100%; padding: 12px 15px; border: 1px solid #e0e0e0; border-radius: 4px; \n                                   box-sizing: border-box; transition: all 0.3s; font-size: 14px;\n                                   background: #f9f9f9; color: #333;"\n                            placeholder="用户名 | 邮箱"\n                            onfocus="this.style.borderColor=\'#4a8bfc\'; this.style.background=\'#fff\'"\n                            onblur="this.style.borderColor=\'#e0e0e0\'; this.style.background=\'#f9f9f9\'">\n                    </div>\n                    \n                    <div style="margin-bottom: 15px;">\n                        <input type="password" id="password" name="password" \n                            style="width: 100%; padding: 12px 15px; border: 1px solid #e0e0e0; border-radius: 4px; \n                                   box-sizing: border-box; transition: all 0.3s; font-size: 14px;\n                                   background: #f9f9f9; color: #333;"\n                            placeholder="密码"\n                            onfocus="this.style.borderColor=\'#4a8bfc\'; this.style.background=\'#fff\'"\n                            onblur="this.style.borderColor=\'#e0e0e0\'; this.style.background=\'#f9f9f9\'">\n                    </div>\n                    \n                    <button id="loginBtn" \n                            style="width: 100%; padding: 12px; background: #4a8bfc; color: white; \n                                   border: none; border-radius: 4px; font-size: 15px; cursor: pointer;\n                                   transition: background 0.3s;"\n                            onmouseover="this.style.background=\'#3a7be0\'"\n                            onmouseout="this.style.background=\'#4a8bfc\'">\n                        登录\n                    </button>\n                </div>\n            ',
            success: (t, e) => {
                $("#loginBtn").click((function() {
                    const t = $("#username").val(), n = $("#password").val();
                    if (!t || !n) return void show.error("请输入用户名和密码");
                    let a = loading();
                    (async (t, e) => {
                        let n = `${U}//v1/sessions?username=${t}&password=${e}&device_uuid=04b9534d-5118-53de-9f87-2ddded77111e&device_name=iPhone&device_model=iPhone&platform=ios&system_version=17.4&app_version=official&app_version_number=1.9.29&app_channel=official`, a = {
                            "user-agent": "Dart/3.5 (dart:io)",
                            "accept-language": "zh-TW",
                            "content-type": "multipart/form-data; boundary=--dio-boundary-2210433284",
                            jdsignature: await R()
                        };
                        return await gmHttp.post(n, null, a);
                    })(t, n).then((async t => {
                        let n = t.success;
                        if (0 === n) show.error(t.message); else {
                            if (1 !== n) throw console.error("登录失败", t), new Error(t.message);
                            {
                                let n = t.data.token;
                                await storageManager.setItem("appAuthorization", n), await storageManager.setItem("appUser", t.data), 
                                show.ok("登录成功"), layer.close(e), window.location.href = "/?handleTop=1&period=daily";
                            }
                        }
                    })).catch((t => {
                        console.error("登录异常:", t), show.error(t.message);
                    })).finally((() => {
                        a.close();
                    }));
                }));
            }
        });
    }
}

class NavBarPlugin extends BasePlugin {
    handle() {
        if (this.margeNav(), this.hookSearch(), this.hookOldSearch(), this.toggleOtherNavItem(), 
        $(window).resize(this.toggleOtherNavItem), window.location.href.includes("/search?q")) {
            const t = new URLSearchParams(window.location.search);
            let e = t.get("q"), n = t.get("f");
            $("#search-keyword").val(e), $("#search-type").val(n);
        }
    }
    hookSearch() {
        $("#navbar-menu-hero").after('\n            <div class="navbar-menu" id="search-box">\n                <div class="navbar-start" style="display: flex; align-items: center; gap: 5px;">\n                    <select id="search-type" style="padding: 8px 12px; border: 1px solid #555; border-radius: 4px; background-color: #333; color: #eee; font-size: 14px; outline: none;">\n                        <option value="all">影片</option>\n                        <option value="actor">演員</option>\n                        <option value="series">系列</option>\n                        <option value="maker">片商</option>\n                        <option value="director">導演</option>\n                        <option value="code">番號</option>\n                        <option value="list">清單</option>\n                    </select>\n                    <input id="search-keyword" type="text" placeholder="輸入影片番號,演員名等關鍵字進行檢索" style="padding: 8px 12px; border: 1px solid #555; border-radius: 4px; flex-grow: 1; font-size: 14px; background-color: #333; color: #eee; outline: none;">\n                    <a href="/advanced_search?noFold=1" title="進階檢索" style="padding: 6px 12px; background-color: #444; border-radius: 4px; text-decoration: none; color: #ddd; font-size: 14px; border: 1px solid #555;"><span>...</span></a>\n                    <a id="search-img-btn" style="padding: 6px 16px; background-color: #444; color: #fff; border-radius: 4px; text-decoration: none; font-weight: 500; cursor: pointer; border: 1px solid #555;">识图</a>\n                    <a id="search-btn" style="padding: 6px 16px; background-color: #444; color: #fff; border-radius: 4px; text-decoration: none; font-weight: 500; cursor: pointer; border: 1px solid #555;">檢索</a>\n                </div>\n            </div>\n        '), 
        $("#search-keyword").on("paste", (t => {
            setTimeout((() => {
                $("#search-btn").click();
            }), 0);
        })).on("keypress", (t => {
            "Enter" === t.key && setTimeout((() => {
                $("#search-btn").click();
            }), 0);
        })), $("#search-btn").on("click", (t => {
            let e = $("#search-keyword").val(), n = $("#search-type option:selected").val();
            "" !== e && (window.location.href.includes("/search?q") ? window.location.href = "/search?q=" + e + "&f=" + n : window.open("/search?q=" + e + "&f=" + n));
        })), $("#search-img-btn").on("click", (() => {
            this.getBean("SearchByImagePlugin").open();
        }));
    }
    hookOldSearch() {
        const t = document.querySelector(".search-image");
        if (!t) return;
        const e = t.cloneNode(!0);
        t.parentNode.replaceChild(e, t), $("#button-search-image").attr("data-tooltip", "以图识图"), 
        $(".search-image").on("click", (t => {
            this.getBean("SearchByImagePlugin").open();
        }));
    }
    margeNav() {
        $('a[href*="/feedbacks/new"]').remove(), $('a[href*="theporndude.com"]').remove(), 
        $('a.navbar-link[href="/makers"]').parent().after('\n            <div class="navbar-item has-dropdown is-hoverable">\n                <a class="navbar-link">其它</a>\n                <div class="navbar-dropdown is-boxed">\n                  <a class="navbar-item" href="/feedbacks/new" target="_blank" >反饋</a>\n                  <a class="navbar-item" rel="nofollow noopener" target="_blank" href="https://theporndude.com/zh">ThePornDude</a>\n                </div>\n              </div>\n        ');
    }
    toggleOtherNavItem() {
        let t = $("#search-box"), e = $("#search-bar-container");
        $(window).width() < 1600 && $(window).width() > 1023 && (t.hide(), e.show()), $(window).width() > 1600 && (t.show(), 
        e.hide());
    }
}

class OtherSitePlugin extends BasePlugin {
    constructor() {
        super(...arguments), o(this, "okBackgroundColor", "#7bc73b"), o(this, "errorBackgroundColor", "#de3333"), 
        o(this, "timeout", "1000"), o(this, "retry", 5), o(this, "settingCache", null), 
        o(this, "lastFetchTime", 0), o(this, "CACHE_DURATION", 1e4);
    }
    async initCss() {
        return "\n            <style>\n                .site-btn {\n                    position: relative !important;\n                    min-width: 80px;\n                    display: inline-block !important;\n                    padding: 5px 10px;\n                    color: white !important;\n                    background-color:#938585;\n                    text-decoration: none;\n                    border-radius: 4px;\n                    text-align: center;\n                    margin-bottom: 5px;\n                }\n                .site-btn:hover {\n                    color: white;\n                    transform: translateY(-2px);\n                    box-shadow: 0 2px 5px rgba(0,0,0,0.1);\n                }\n                .site-tag {\n                    position: absolute; \n                    top: -15px; \n                    right: 0; \n                    background-color: #ffc107; \n                    color: #333; \n                    font-size: 12px; \n                    padding: 2px 6px; \n                    border-radius: 4px;\n                }\n            </style>\n        ";
    }
    async handle() {
        if (!isDetailPage) return;
        const t = [ {
            id: "javTrailersBtn",
            getUrl: async () => await this.getJavTrailersUrl(),
            boxSelector: ".videos-list .video-link",
            searchPath: (t, e) => `${t}/search/${e}`,
            getHref: t => t.attr("href"),
            getTitle: t => t.find("p.card-text").text()
        }, {
            id: "missAvBtn",
            getUrl: async () => await this.getMissAvUrl(),
            boxSelector: ".text-secondary",
            searchPath: (t, e) => `${t}/search/${e}`,
            getHref: t => t.attr("href"),
            getTitle: t => t.text()
        }, {
            id: "123Av",
            getUrl: async () => await this.getAv123Url() + "/ja",
            boxSelector: ".box-item",
            searchPath: (t, e) => `${t}/search?keyword=${e}`,
            getHref: t => t.find(".detail a").attr("href"),
            getTitle: t => t.find("img").attr("title")
        }, {
            id: "jableBtn",
            getUrl: async () => await this.getjableUrl(),
            boxSelector: "#list_videos_videos_list_search_result .detail .title a",
            searchPath: (t, e) => `${t}/search/${e}/`,
            getHref: t => t.attr("href"),
            getTitle: t => t.text()
        }, {
            id: "avgleBtn",
            getUrl: async () => await this.getAvgleUrl(),
            boxSelector: ".text-secondary",
            searchPath: (t, e) => `${t}/vod/search.html?wd=${e}`,
            getHref: t => t.attr("href"),
            getTitle: t => t.text()
        }, {
            id: "javDbBtn",
            getUrl: async () => await this.getJavDbUrl(),
            boxSelector: ".movie-list .item",
            searchPath: (t, e) => `${t}/search?q=${e}`,
            getHref: t => t.find("a").attr("href"),
            getTitle: t => t.find(".video-title").text(),
            condition: p
        } ], e = this.getPageInfo().carNum, n = `\n            <div class="panel-block" style="${g ? "margin-top:8px;font-size:13px" : "margin-top:10px;font-size:13px"}">\n                <div style="display: flex;gap: 5px;flex-wrap: wrap">\n                    ${t.map((t => !1 === t.condition ? "" : `<a target="_blank" class="site-btn" id="${t.id}"><span>${t.id.replace("Btn", "")}</span></a>`)).join("")}\n                </div>\n            </div>\n        `;
        $(".movie-panel-info").append(n), $(".container .info").append(n), $("#javTrailersBtn").on("click", (t => {
            t.preventDefault(), utils.openPage($("#javTrailersBtn").attr("href") + "?handle=1", e, !1, t);
        })), await Promise.all(t.map((async t => {
            !1 !== t.condition && await this.handleSite(e, t);
        })));
    }
    async handleSite(t, e) {
        const n = $(`#${e.id}`);
        try {
            const a = await e.getUrl(), i = e.searchPath(a, t);
            n.attr("href", i);
            const s = await this.retryWithTimeout((() => gmHttp.get(i, null, null, this.timeout)), this.retry, e), r = utils.htmlTo$dom(s), o = [];
            r.find(e.boxSelector).each(((n, i) => {
                const s = $(i), r = e.getTitle(s).toLowerCase();
                if (!r.includes(t.toLowerCase())) return;
                let l = e.getHref(s);
                if (!l) throw console.log(s, r, l), new Error("解析href失败");
                l.includes("http") || (l = a + (l.startsWith("/") ? l : "/" + l)), o.push(l);
            }));
            let l = "";
            1 === o.length ? n.attr("href", o[0]) : o.length > 1 ? (n.attr("href", i), l += '<span class="site-tag" style="top:-15px">多结果</span>') : n.attr("href", i), 
            l && n.append(l), n.css("backgroundColor", this.okBackgroundColor);
        } catch (a) {
            console.error(a, e), n.css("backgroundColor", this.errorBackgroundColor);
        }
    }
    async getSettingCache() {
        const t = Date.now();
        return (!this.settingCache || t - this.lastFetchTime > this.CACHE_DURATION) && (this.settingCache = await storageManager.getSetting(), 
        this.lastFetchTime = t), this.settingCache;
    }
    async getMissAvUrl() {
        return (await this.getSettingCache()).missAvUrl || "https://missav.live";
    }
    async getjableUrl() {
        return (await this.getSettingCache()).jableUrl || "https://jable.tv";
    }
    async getAvgleUrl() {
        return (await this.getSettingCache()).avgleUrl || "https://www.av.gl";
    }
    async getJavTrailersUrl() {
        return (await this.getSettingCache()).javTrailersUrl || "https://javtrailers.com";
    }
    async getAv123Url() {
        return (await this.getSettingCache()).av123Url || "https://123av.com";
    }
    async getJavDbUrl() {
        return (await this.getSettingCache()).javDbUrl || "https://javdb.com";
    }
    async getJavBusUrl() {
        return (await this.getSettingCache()).javBusUrl || "https://javbus.com";
    }
    async retryWithTimeout(t, e, n) {
        let a = 0;
        for (;a < e; ) try {
            return await Promise.race([ t() ]);
        } catch (i) {
            if (console.log("请求失败", n.id), a++, a === e) throw i;
        }
    }
}

class BusDetailPagePlugin extends BasePlugin {
    async initCss() {
        return window.isDetailPage ? ($("h4:contains('論壇熱帖')").hide(), $("h4:contains('同類影片')").hide(), 
        $("h4:contains('推薦')").hide(), "") : "";
    }
    async handle() {
        if (window.location.href.includes("/star/")) {
            const t = $(".avatar-box");
            if (t.length > 0) {
                let e = t.parent();
                e.css("position", "initial"), e.insertBefore(e.parent());
            }
        }
        $(".genre a").attr("target", "_blank");
    }
}

class DetailPageButtonPlugin extends BasePlugin {
    constructor() {
        super(), this.answerCount = 1;
    }
    handle() {
        this.bindHotkey(), this.hideVideoControls(), window.isDetailPage && this.createMenuBtn();
    }
    createMenuBtn() {
        const t = this.getPageInfo(), e = t.carNum, n = `\n            <div style="margin: 10px auto;">\n                <a id="filterBtn" class="menu-btn" style="background-color:${y}; color:white">\n                    <span>${w}(a)</span>\n                </a>\n                <a id="favoriteBtn" class="menu-btn" style="background-color:${_}; color:white">\n                    <span>${x}(s)</span>\n                </a>\n                <a id="hasDownBtn" class="menu-btn" style="background-color:${S}; color:white">\n                    <span>${C}</span>\n                </a>\n                <a id="hasWatchBtn" class="menu-btn" style="background-color:${B}; color:white">\n                    <span>${P}</span>\n                </a>\n\n\n\n                <a id="search-subtitle-btn" class="menu-btn fr-btn" style="background:linear-gradient(to bottom, #8d5656, rgb(196,159,91))">\n                    <span>字幕 (SubTitleCat)</span>\n                </a>\n                <a id="xunLeiSubtitleBtn" class="menu-btn fr-btn" style="background:linear-gradient(to left, #375f7c, #2196F3)">\n                    <span>字幕 (迅雷)</span>\n                </a>\n                <a id="magnetSearchBtn" class="menu-btn fr-btn" style="background:linear-gradient(to right, rgb(245,140,1), rgb(84,161,29))">\n                    <span>磁力搜索</span>\n                </a>\n                <a id="enable-magnets-filter" class="menu-btn fr-btn" style="background-color:#c2bd4c">\n                    <span id="magnets-span">关闭磁力过滤</span>\n                </a>\n            </div>\n        `;
        g && $(".tabs").after(n), p && $("#mag-submit-show").before(n), $("#favoriteBtn").on("click", (() => this.favoriteOne())), 
        $("#filterBtn").on("click", (t => this.filterOne(t))), $("#hasDownBtn").on("click", (async () => {
            await storageManager.saveCar(t.carNum, t.url, t.actress, f), this.showStatus(e).then(), 
            window.refresh(), utils.closePage();
        })), $("#hasWatchBtn").on("click", (async () => {
            await storageManager.saveCar(t.carNum, t.url, t.actress, v), this.showStatus(e).then(), 
            window.refresh(), utils.closePage();
        })), $("#magnetSearchBtn").on("click", (() => {
            let e = this.getBean("MagnetHubPlugin").createMagnetHub(t.carNum);
            layer.open({
                type: 1,
                title: "磁力搜索",
                content: '<div id="magnetHubBox"></div>',
                area: utils.getResponsiveArea(),
                scrollbar: !1,
                success: () => {
                    $("#magnetHubBox").append(e);
                }
            });
        })), $("#enable-magnets-filter").on("click", (t => {
            let e = $("#magnets-span");
            const n = this.getBean("HighlightMagnetPlugin");
            "关闭磁力过滤" === e.text() ? (n.showAll(), e.text("开启磁力过滤")) : (n.handle(), e.text("关闭磁力过滤"));
        })), $("#search-subtitle-btn").on("click", (t => utils.openPage(`https://subtitlecat.com/index.php?search=${e}`, e, !1, t))), 
        $("#xunLeiSubtitleBtn").on("click", (() => this.searchXunLeiSubtitle(e))), this.showStatus(e).then();
    }
    async showStatus(t) {
        const e = $("#filterBtn span"), n = $("#favoriteBtn span"), a = $("#hasDownBtn span"), i = $("#hasWatchBtn span");
        e.text(`${w}(a)`), n.text(`${x}(s)`), a.text(C), i.text(P);
        const s = await storageManager.getCar(t);
        if (s) switch (s.status) {
          case u:
            e.text(`${b}(a)`);
            break;

          case m:
            n.text(`${k}(s)`);
            break;

          case f:
            a.text("📥️ 已标记下载");
            break;

          case v:
            i.text("🔍 已标记观看");
        }
    }
    async favoriteOne() {
        let t = this.getPageInfo();
        await storageManager.saveCar(t.carNum, t.url, t.actress, m), this.showStatus(t.carNum).then(), 
        window.refresh(), utils.closePage();
    }
    searchXunLeiSubtitle(t) {
        let e = loading();
        gmHttp.get(`https://api-shoulei-ssl.xunlei.com/oracle/subtitle?gcid=&cid=&name=${t}`).then((e => {
            let n = e.data;
            n && 0 !== n.length ? layer.open({
                type: 1,
                title: "迅雷字幕",
                content: '<div id="table-container"></div>',
                area: [ "50%", "70%" ],
                success: e => {
                    new TableGenerator({
                        containerId: "table-container",
                        columns: [ {
                            key: "name",
                            title: "文件名"
                        }, {
                            key: "ext",
                            title: "类型"
                        }, {
                            key: "extra_name",
                            title: "来源"
                        } ],
                        data: n,
                        buttons: [ {
                            text: "预览",
                            class: "a-primary",
                            onClick: e => {
                                let n = e.url, a = t + "." + e.ext;
                                this.previewSubtitle(n, a);
                            }
                        }, {
                            text: "下载",
                            class: "a-success",
                            onClick: async e => {
                                let n = e.url, a = t + "." + e.ext, i = await gmHttp.get(n);
                                utils.download(i, a);
                            }
                        } ]
                    });
                }
            }) : show.error("迅雷中找不到相关字幕!");
        })).catch((t => {
            console.error(t), show.error(t);
        })).finally((() => {
            e.close();
        }));
    }
    async filterOne(t, e) {
        t && t.preventDefault();
        let n = this.getPageInfo();
        e ? (await storageManager.saveCar(n.carNum, n.url, n.actress, u), this.showStatus(n.carNum).then(), 
        window.refresh(), utils.closePage()) : utils.q(t, `是否屏蔽${n.carNum}?`, (async () => {
            await storageManager.saveCar(n.carNum, n.url, n.actress, u), this.showStatus(n.carNum).then(), 
            window.refresh(), utils.closePage();
        }), (() => {
            this.answerCount = 1;
        }));
    }
    speedVideo() {
        if ($("#preview-video").is(":visible")) {
            const t = document.getElementById("preview-video");
            return void (t && (t.muted = !1, t.controls = !1, t.currentTime + 5 < t.duration ? t.currentTime += 5 : (show.info("预览视频结束, 已回到开头"), 
            t.currentTime = 1)));
        }
        const t = $('iframe[id^="layui-layer-iframe"]');
        if (t.length > 0) return void t[0].contentWindow.postMessage("speedVideo", "*");
        let e = $(".preview-video-container");
        if (e.length > 0) {
            e[0].click();
            const t = document.getElementById("preview-video");
            t && (t.currentTime += 5, t.muted = !1);
        } else $("#javTrailersBtn").click();
    }
    hideVideoControls() {
        $(document).on("mouseenter", "#preview-video", (function() {
            $(this).prop("controls", !0);
        }));
    }
    bindHotkey() {
        const t = {
            a: () => {
                this.answerCount >= 2 ? this.filterOne(null, !0) : this.filterOne(null), this.answerCount++;
            },
            s: () => this.favoriteOne(null),
            z: () => this.speedVideo()
        }, e = (t, e) => {
            j.registerHotkey(t, (n => {
                const a = document.activeElement;
                "INPUT" === a.tagName || "TEXTAREA" === a.tagName || a.isContentEditable || (window.isDetailPage ? e() : (t => {
                    const e = $(".layui-layer-content iframe");
                    0 !== e.length && e[0].contentWindow.postMessage(t, "*");
                })(t));
            }));
        };
        window.isDetailPage && window.addEventListener("message", (e => {
            t[e.data] && t[e.data]();
        })), Object.entries(t).forEach((([t, n]) => {
            e(t, n);
        }));
    }
    previewSubtitle(t, e) {
        if (!t) return void console.error("未提供文件URL");
        const n = t.split(".").pop().toLowerCase();
        "ass" === n || "srt" === n ? gmHttp.get(t).then((t => {
            let a = t, i = "字幕预览";
            if ("ass" === n) {
                i = "ASS字幕预览 - " + e;
                const n = t.match(/\[Events][\s\S]*?(?=\[|$)/i);
                n && (a = n[0]);
            } else "srt" === n && (i = "SRT字幕预览 - " + e);
            layer.open({
                type: 1,
                title: i,
                area: [ "80%", "80%" ],
                scrollbar: !1,
                content: `<div style="padding:15px;background:#1E1E1E;color:#FFF;font-family:Consolas,Monaco,monospace;white-space:pre-wrap;overflow:auto;height:100%;">${a}</div>`,
                btn: [ "下载", "关闭" ],
                btn1: function(n, a, i) {
                    return utils.download(t, e), !1;
                }
            });
        })).catch((t => {
            show.error(`预览失败: ${t.message}`), console.error("预览字幕文件出错:", t);
        })) : alert("仅支持预览ASS和SRT字幕文件");
    }
}

class HistoryPlugin extends BasePlugin {
    constructor() {
        super(...arguments), o(this, "dataType", "all"), o(this, "tableObj", null);
    }
    handle() {
        if (g) {
            let t = function() {
                $(".navbar-search").is(":hidden") ? ($(".historyBtnBox").show(), $(".miniHistoryBtnBox").hide()) : ($(".historyBtnBox").hide(), 
                $(".miniHistoryBtnBox").show());
            };
            $(".navbar-end").prepend('<div class="navbar-item has-dropdown is-hoverable historyBtnBox">\n                    <a id="historyBtn" class="navbar-link nav-btn" style="color: #aade66 !important;padding-right:15px !important;">\n                        历史列表\n                    </a>\n                </div>'), 
            $(".navbar-search").css("margin-left", "0").before('\n                <div class="navbar-item miniHistoryBtnBox">\n                    <a id="miniHistoryBtn" class="navbar-link nav-btn" style="color: #aade66 !important;padding-left:0 !important;padding-right:0 !important;">\n                        历史列表\n                    </a>\n                </div>\n            '), 
            t(), $(window).resize(t);
        }
        p && $("#navbar").append('\n                <ul class="nav navbar-nav navbar-right" style="margin-right: 10px">\n                    <li><a id="historyBtn" style="color: #86e114 !important;padding-right:15px !important;" role="button">历史列表</a></li>\n                </ul>\n           '), 
        $("#historyBtn,#miniHistoryBtn").on("click", (t => this.openHistory()));
    }
    openHistory() {
        let t = `\n            <div style="margin: 10px;display: flex;gap: 5px;">\n                <a class="menu-btn history-btn" data-action="all" style="background-color:#d3c8a5">所有</a>\n                <a class="menu-btn history-btn" data-action="filter" style="background-color:${y}">${b}</a>\n                <a class="menu-btn history-btn" data-action="favorite" style="background-color:${_};">${k}</a>\n                <a class="menu-btn history-btn" data-action="hasDown" style="background-color:${S};">${C}</a>\n                <a class="menu-btn history-btn" data-action="hasWatch" style="background-color:${B};">${P}</a>\n                <input id="searchCarNum" type="text" placeholder="搜索番号" style="padding: 4px 5px;margin-left: auto; margin-right: 0">\n                <a id="clearSearchbtn" class="a-dark" style="margin-left: 0">重置</a>\n            </div>\n            <div id="table-container"></div>\n        `;
        layer.open({
            type: 1,
            title: "历史列表",
            content: t,
            scrollbar: !1,
            area: utils.getResponsiveArea([ "60%", "90%" ]),
            success: async t => {
                const e = await this.getDataList();
                this.loadTableData(e), $(".layui-layer-content").on("click", ".history-btn", (async t => {
                    this.dataType = $(t.target).data("action"), await this.reloadTable();
                })).on("click", "#clearSearchbtn", (async t => {
                    $("#searchCarNum").val(""), await this.reloadTable();
                })).on("keydown", "#searchCarNum", (async t => {
                    await this.reloadTable();
                }));
            },
            end: async () => window.refresh()
        });
    }
    async handleClickDetail(t, e) {
        if (g) if (e.carNum.includes("FC2-")) {
            const t = this.parseMovieId(e.url);
            this.getBean("fc2Plugin").openFc2Dialog(t, e.carNum, e.url);
        } else utils.openPage(e.url, e.carNum, !1, t);
        if (p) {
            let n = e.url;
            if (n.includes("javdb")) if (e.carNum.includes("FC2-")) {
                const t = this.parseMovieId(n);
                await this.getBean("Fc2Plugin").openFc2Page(t, e.carNum, n);
            } else window.open(n, "_blank"); else utils.openPage(e.url, e.carNum, !1, t);
        }
    }
    async reloadTable() {
        const t = await this.getDataList();
        this.tableObj.update(t, 1);
    }
    handleDelete(t, e) {
        utils.q(t, `是否移除${e.carNum}?`, (async () => {
            await storageManager.removeCar(e.carNum), this.getBean("listPagePlugin").showCarNumBox(e.carNum), 
            this.reloadTable().then();
        }));
    }
    async getDataList() {
        let t = await storageManager.getCarList();
        this.allCount = t.length, this.filterCount = 0, this.favoriteCount = 0, this.hasDownCount = 0, 
        this.hasWatchCount = 0, t.forEach((t => {
            switch (t.status) {
              case u:
                this.filterCount++;
                break;

              case m:
                this.favoriteCount++;
                break;

              case f:
                this.hasDownCount++;
                break;

              case v:
                this.hasWatchCount++;
            }
        })), $('a[data-action="all"]').text(`所有 (${this.allCount})`), $('a[data-action="filter"]').text(`${b} (${this.filterCount})`), 
        $('a[data-action="favorite"]').text(`${k} (${this.favoriteCount})`), $('a[data-action="hasDown"]').text(`${C} (${this.hasDownCount})`), 
        $('a[data-action="hasWatch"]').text(`${P} (${this.hasWatchCount})`);
        const e = $("#searchCarNum").val().trim();
        return e ? t.filter((t => t.carNum.toLowerCase().includes(e.toLowerCase()))) : "all" === this.dataType ? t : t.filter((t => t.status === this.dataType));
    }
    loadTableData(t) {
        this.tableObj = new TableGenerator({
            containerId: "table-container",
            columns: [ {
                title: "序号",
                key: "_index",
                width: "70px"
            }, {
                key: "carNum",
                title: "番号"
            }, {
                key: "actress",
                title: "演员",
                width: "250px"
            }, {
                key: "updateDate",
                title: "操作日期",
                width: "185px"
            }, {
                key: "url",
                title: "来源",
                render: t => {
                    let e = t.url;
                    return e.includes("javdb") ? '<span style="color:#d34f9e">Javdb</span>' : e.includes("javbus") ? '<span style="color:#eaa813">JavBus</span>' : e.includes("123av") ? '<span style="color:#eaa813">123Av</span>' : `<span style="color:#050505">${e}</span>`;
                }
            }, {
                key: "status",
                title: "状态",
                width: "250px",
                render: t => {
                    let e, n = "";
                    switch (t.status) {
                      case "filter":
                        e = "#ec4949", n = b;
                        break;

                      case "favorite":
                        e = "#50adb9", n = k;
                        break;

                      case "hasDown":
                        e = "#8ebd6e", n = C;
                        break;

                      case "hasWatch":
                        e = "#8ebd6e", n = P;
                    }
                    return `<span style="color:${e}">${n}</span>`;
                }
            } ],
            data: t,
            buttons: [ {
                text: "移除",
                class: "a-danger",
                onClick: (t, e) => {
                    this.handleDelete(t, e);
                }
            }, {
                text: "详情页",
                class: "a-info",
                onClick: (t, e) => {
                    this.handleClickDetail(t, e).then();
                }
            } ],
            pagination: {
                enable: !0,
                pageSize: 10,
                pageSizeOptions: [ 10, 20, 50, 100 ],
                currentPage: 1,
                showTotal: !0,
                showSizeChanger: !0,
                showQuickJumper: !0
            }
        });
    }
}

class ReviewPlugin extends BasePlugin {
    constructor() {
        super(...arguments), o(this, "floorIndex", 1);
    }
    async handle() {
        if (window.isDetailPage) {
            if (g) {
                const t = this.parseMovieId(window.location.href);
                await this.showReview(t), await this.getBean("RelatedPlugin").showRelated();
            }
            if (p) {
                let t = this.getPageInfo().carNum;
                const e = await (async t => {
                    let e = `${U}/v2/search`, n = {
                        "user-agent": "Dart/3.5 (dart:io)",
                        "accept-language": "zh-TW",
                        host: "jdforrepam.com",
                        jdsignature: await R()
                    }, a = {
                        q: t,
                        page: 1,
                        type: "movie",
                        limit: 1,
                        movie_type: "all",
                        from_recent: "false",
                        movie_filter_by: "all",
                        movie_sort_by: "relevance"
                    };
                    return (await gmHttp.get(e, a, n)).data.movies;
                })(t);
                let n = null;
                for (let a = 0; a < e.length; a++) {
                    let i = e[a];
                    if (i.number.toLowerCase() === t.toLowerCase()) {
                        n = i.id;
                        break;
                    }
                }
                if (!n) return void show.error("解析视频ID失败, 该视频可能不存在, 无法获取评论数据");
                this.showReview(n, $("#sample-waterfall")).then();
            }
        }
    }
    async showReview(t, e) {
        const n = e || $("#magnets-content");
        n.append('\n            <div style="display: flex; align-items: center; margin: 16px 0; color: #666; font-size: 14px;">\n                <span style="flex: 1; height: 1px; background: linear-gradient(to right, transparent, #999, transparent);"></span>\n                <span style="padding: 0 10px;">评论区</span>\n                <a id="reviewsFold" style="margin-left: 8px; color: #1890ff; text-decoration: none; display: flex; align-items: center;">\n                    <span class="toggle-text">折叠</span>\n                    <span class="toggle-icon" style="margin-left: 4px;">▲</span>\n                </a>\n                <span style="flex: 1; height: 1px; background: linear-gradient(to right, transparent, #999, transparent);"></span>\n            </div>\n        '), 
        $("#reviewsFold").on("click", (t => {
            t.preventDefault(), t.stopPropagation();
            const e = $("#reviewsFold .toggle-text"), n = $("#reviewsFold .toggle-icon"), a = "展开" === e.text();
            e.text(a ? "折叠" : "展开"), n.text(a ? "▲" : "▼"), a ? ($("#reviewsContainer").show(), 
            $("#reviewsFooter").show()) : ($("#reviewsContainer").hide(), $("#reviewsFooter").hide());
        })), n.append('<div id="reviewsContainer"></div>'), n.append('<div id="reviewsFooter"></div>'), 
        await this.fetchAndDisplayReviews(t);
    }
    async fetchAndDisplayReviews(t) {
        const e = $("#reviewsContainer"), n = $("#reviewsFooter");
        e.append('<div id="reviewsLoading" style="margin-top:15px;background-color:#ffffff;padding:10px;margin-left: -10px;">获取评论中...</div>');
        const a = await storageManager.getSetting("reviewCount", 20);
        let i = null;
        try {
            i = await O(t, 1, a);
        } catch (r) {
            console.error("获取评论失败:", r);
        } finally {
            $("#reviewsLoading").remove();
        }
        if (!i) return e.append('\n                <div style="margin-top:15px;background-color:#ffffff;padding:10px;margin-left: -10px;">\n                    获取评论失败\n                    <a id="retryFetchReviews" href="javascript:;" style="margin-left: 10px; color: #1890ff; text-decoration: none;">重试</a>\n                </div>\n            '), 
        void $("#retryFetchReviews").on("click", (async () => {
            $("#retryFetchReviews").parent().remove(), await this.fetchAndDisplayReviews(t);
        }));
        if (0 === i.length) return void e.append('<div style="margin-top:15px;background-color:#ffffff;padding:10px;margin-left: -10px;">无评论</div>');
        const s = await storageManager.getReviewFilterKeywordList();
        if (this.displayReviews(i, e, s), i.length === a) {
            n.html('\n                <button id="loadMoreReviews" style="width:100%; background-color: #e1f5fe; border:none; padding:10px; margin-top:10px; cursor:pointer; color:#0277bd; font-weight:bold; border-radius:4px;">\n                    加载更多评论\n                </button>\n                <div id="reviewsEnd" style="display:none; text-align:center; padding:10px; color:#666; margin-top:10px;">已加载全部评论</div>\n            ');
            let i = 1, o = $("#loadMoreReviews");
            o.on("click", (async () => {
                let n;
                o.text("加载中...").prop("disabled", !0), i++;
                try {
                    n = await O(t, i, a);
                } catch (r) {
                    console.error("加载更多评论失败:", r);
                } finally {
                    o.text("加载失败, 请点击重试").prop("disabled", !1);
                }
                n && (this.displayReviews(n, e, s), n.length < a ? (o.remove(), $("#reviewsEnd").show()) : o.text("加载更多评论").prop("disabled", !1));
            }));
        } else n.html('<div style="text-align:center; padding:10px; color:#666; margin-top:10px;">已加载全部评论</div>');
    }
    displayReviews(t, e, n) {
        t.length && (t.forEach((t => {
            if (n.some((e => t.content.includes(e)))) return;
            const a = Array(t.score).fill('<i class="icon-star"></i>').join(""), i = t.content.replace(/(https?:\/\/[^\s]+|magnet:\?[^\s"'\u4e00-\u9fa5,。?!()【】]+)/gi, (t => `<a href="${t}" class="a-primary" style="padding:0; word-break: break-all; white-space: pre-wrap;" target="_blank" rel="noopener noreferrer">${t}</a>`)), s = `\n            <div class="item columns is-desktop" style="display:block;margin-top:6px;background-color:#ffffff;padding:10px;margin-left: -10px;word-break: break-word;position:relative;">\n                <span style="position:absolute;top:5px;right:10px;color:#999;font-size:12px;">#${this.floorIndex++}楼</span>\n                ${t.username} &nbsp;&nbsp; <span class="score-stars">${a}</span> \n                <span class="time">${utils.formatDate(t.created_at)}</span> \n                &nbsp;&nbsp; 点赞:${t.likes_count}\n                <p class="review-content" style="margin-top: 5px;"> ${i} </p>\n            </div>\n        `;
            e.append(s);
        })), utils.rightClick($(".review-content"), (async t => {
            const e = window.getSelection().toString();
            if (e) {
                t.preventDefault();
                await utils.q(t, `是否将 '${e}' 加入评论区关键词?`) && (await storageManager.saveReviewFilterKeyword(e), 
                show.ok("操作成功, 刷新页面后生效"));
            }
        })));
    }
}

class FilterTitleKeywordPlugin extends BasePlugin {
    handle() {
        if (!window.isDetailPage) return;
        let t;
        g && (t = $("h2"), $(".male").prev()), p && (t = $("h3")), utils.rightClick(t, (t => {
            const e = window.getSelection().toString();
            if (e) {
                t.preventDefault();
                let n = {
                    clientX: t.clientX,
                    clientY: t.clientY + 80
                };
                utils.q(n, `是否屏蔽标题关键词 ${e}?`, (async () => {
                    await storageManager.saveTitleFilterKeyword(e), window.refresh(), utils.closePage();
                }));
            }
        }));
    }
}

class ListPageButtonPlugin extends BasePlugin {
    handle() {
        window.isListPage && this.createMenuBtn();
    }
    createMenuBtn() {
        if (g) {
            if (window.location.href.includes("/actors/")) $(".toolbar .buttons").append('\n                    <a class="menu-btn" id="waitCheckBtn" \n                       style="background-color:#56c938 !important;; margin-left: 40px;margin-bottom: 8px; border-bottom:none !important; border-radius:3px;">\n                        <span>打开待鉴定</span>\n                    </a>\n                    <a class="menu-btn" id="waitDownBtn" \n                       style="background-color:#2caac0 !important;; margin-left: 10px;margin-bottom: 8px; border-bottom:none !important; border-radius:3px;">\n                      <span>打开已收藏</span>\n                    </a>\n                '), 
            h || $(".toolbar .buttons").append(`\n                        <a class="menu-btn" id="sort-toggle-btn" \n                           style="background-color:#8783ab !important; margin-left: 50px;margin-bottom: 8px; border-bottom:none !important; border-radius:3px;">当前排序方式: ${"rateCount" === localStorage.getItem("sortMethod") ? "评价人数" : "date" === localStorage.getItem("sortMethod") ? "时间" : "默认"}</a>\n                    `); else if (window.location.href.includes("advanced_search")) {
                let t = $("h2.section-title");
                t.css({
                    display: "grid",
                    "grid-template-columns": "auto auto 1fr",
                    width: "100%"
                }), t.append(`\n                    <div>\n                        <a class="menu-btn" id="waitCheckBtn" \n                           style="background-color:#56c938 !important;; margin-left: 10px;border-bottom:none !important; border-radius:3px;">\n                            <span>打开待鉴定</span>\n                        </a>\n                        <a class="menu-btn" id="waitDownBtn" \n                           style="background-color:#2caac0 !important;; margin-left: 10px;border-bottom:none !important; border-radius:3px;">\n                          <span>打开已收藏</span>\n                        </a>\n                        <a class="menu-btn" id="sort-toggle-btn" \n                           style="background-color:#8783ab !important; margin-left: 20px; border-bottom:none !important; border-radius:3px; float: right">\n                          当前排序方式: ${"rateCount" === localStorage.getItem("sortMethod") ? "评价人数" : "date" === localStorage.getItem("sortMethod") ? "时间" : "默认"}\n                        </a>\n                    </div>\n                `);
            } else $(".tabs ul").append('\n                    <li class="is-active" id="waitCheckBtn">\n                        <a class="menu-btn" style="background-color:#56c938 !important;margin-left: 20px;border-bottom:none !important;border-radius:3px;">\n                            <span>打开待鉴定</span>\n                        </a>\n                    </li>\n                     <li class="is-active" id="waitDownBtn">\n                        <a class="menu-btn" style="background-color:#2caac0 !important;margin-left: 20px;border-bottom:none !important;border-radius:3px;">\n                            <span>打开已收藏</span>\n                        </a>\n                    </li>\n                '), 
            h || $(".tabs ul").after(`\n                      <div style="padding:10px">\n                        <a class="menu-btn" id="sort-toggle-btn" \n                           style="background-color:#8783ab !important; margin-left: 20px; border-bottom:none !important; border-radius:3px;">\n                          当前排序方式: ${"rateCount" === localStorage.getItem("sortMethod") ? "评价人数" : "date" === localStorage.getItem("sortMethod") ? "时间" : "默认"}\n                        </a>\n                      </div>\n                    `);
            this.sortItems();
        }
        if (p) {
            const t = '\n                <div style="margin-top: 10px">\n                    <a id="waitCheckBtn" class="menu-btn" style="background-color:#56c938 !important;margin-left: 14px;border-bottom:none !important;border-radius:3px;">\n                        <span>打开待鉴定</span>\n                    </a>\n                    <a id="waitDownBtn" class="menu-btn" style="background-color:#2caac0 !important;margin-left: 5px;border-bottom:none !important;border-radius:3px;">\n                        <span>打开已收藏</span>\n                    </a>\n                </div>\n            ';
            $(".masonry").parent().prepend(t);
        }
        $("#waitCheckBtn").on("click", (t => {
            this.openWaitCheck(t).then();
        })), $("#waitDownBtn").on("click", (t => {
            this.openFavorite(t).then();
        })), $("#sort-toggle-btn").on("click", (t => {
            const e = localStorage.getItem("sortMethod");
            let n;
            n = e && "default" !== e ? "rateCount" === e ? "date" : "default" : "rateCount";
            const a = {
                default: "默认",
                rateCount: "评价人数",
                date: "时间"
            }[n];
            $(t.target).text(`当前排序方式: ${a}`), localStorage.setItem("sortMethod", n), this.sortItems();
        }));
    }
    sortItems() {
        if (h) return;
        const t = localStorage.getItem("sortMethod");
        if (!t) return;
        $(".movie-list .item").each((function(t) {
            $(this).attr("data-original-index") || $(this).attr("data-original-index", t);
        }));
        const e = $(".movie-list"), n = $(".item", e);
        if ("default" === t) n.sort((function(t, e) {
            return $(t).data("original-index") - $(e).data("original-index");
        })).appendTo(e); else {
            const a = n.get();
            a.sort((function(e, n) {
                if ("rateCount" === t) {
                    const t = t => {
                        const e = $(t).find(".score .value").text().match(/由(\d+)人/);
                        return e ? parseFloat(e[1]) : 0;
                    };
                    return t(n) - t(e);
                }
                {
                    const t = t => {
                        const e = $(t).find(".meta").text().trim();
                        return new Date(e);
                    };
                    return t(n) - t(e);
                }
            })), e.empty().append(a);
        }
    }
    async openWaitCheck() {
        let t = this.getSelector();
        const e = await storageManager.getSetting("waitCheckCount", 5), n = [ b, k, C, P ];
        let a = 0;
        $(`${t.itemSelector}:visible`).each(((t, i) => {
            if (a >= e) return !1;
            const s = $(i);
            if (n.some((t => s.find(`span.tag:contains('${t}')`).length > 0))) return;
            const {carNum: r, aHref: o, title: l} = this.getBean("ListPagePlugin").findCarNumAndHref(s);
            if (r.includes("FC2-")) {
                const t = this.parseMovieId(o);
                this.getBean("Fc2Plugin").openFc2Page(t, r, o);
            } else {
                let t = o + (o.includes("?") ? "&autoPlay=1" : "?autoPlay=1");
                window.open(t);
            }
            a++;
        })), 0 === a && show.info("没有需鉴定的视频");
    }
    async openFavorite() {
        let t = await storageManager.getSetting("waitCheckCount", 5);
        const e = (await storageManager.getCarList()).filter((t => t.status === m));
        for (let n = 0; n < t; n++) {
            if (n >= e.length) return;
            let t = e[n], a = t.carNum, i = t.url;
            if (a.includes("FC2-")) {
                const t = this.parseMovieId(i);
                await this.getBean("Fc2Plugin").openFc2Page(t, a, i);
            } else window.open(i);
        }
    }
}

class ListPagePlugin extends BasePlugin {
    async handle() {
        this.cleanRepeatId(), this.replaceHdImg(), await this.doFilter(), this.showNoItem(), 
        this.bindClick().then();
        new BroadcastChannel("channel-refresh").addEventListener("message", (async t => {
            "refresh" === t.data.type && await this.doFilter();
        })), $(this.getSelector().itemSelector + " a").attr("target", "_blank"), this.checkDom();
    }
    checkDom() {
        if (!window.isListPage) return;
        const t = this.getSelector(), e = document.querySelector(t.boxSelector), n = new MutationObserver((t => {
            n.disconnect();
            try {
                this.replaceHdImg(), this.doFilter().then(), this.getBean("ListPageButtonPlugin").sortItems(), 
                this.showNoItem(), this.getBean("CopyTitleOrDownImgPlugin").addSvgBtn(), $(this.getSelector().itemSelector + " a").attr("target", "_blank");
            } finally {
                n.observe(e, a);
            }
        })), a = {
            childList: !0,
            subtree: !1
        };
        n.observe(e, a);
    }
    showNoItem() {
        if ($(this.getSelector().itemSelector).length > 0) {
            0 === $(this.getSelector().itemSelector).filter((function() {
                return "none" !== $(this).css("display");
            })).length && show.info("当前内容已标记, 无内容可鉴定, 请进入下一页");
        }
    }
    cleanRepeatId() {
        if (!p) return;
        $("#waterfall_h").removeAttr("id").attr("id", "no-page");
        const t = $('[id="waterfall"]');
        0 !== t.length && t.each((function() {
            const t = $(this);
            if (!t.hasClass("masonry")) {
                t.children().insertAfter(t), t.remove();
            }
        }));
    }
    async doFilter() {
        if (!window.isListPage) return;
        let t = $(this.getSelector().itemSelector).toArray();
        await this.filterMovieList(t), await this.getBean("autoPagePlugin").handlePaging();
    }
    async filterMovieList(t) {
        const e = await storageManager.getCarList(), n = await storageManager.getTitleFilterKeyword(), a = e.filter((t => t.status === u)).map((t => t.carNum)), i = e.filter((t => t.status === m)).map((t => t.carNum)), s = e.filter((t => t.status === f)).map((t => t.carNum)), r = e.filter((t => t.status === v)).map((t => t.carNum)), o = (await storageManager.getAllActorFilterCarList()).map((t => t.carNum));
        let l = await storageManager.getSetting("hideFilterItem", "yes");
        h && (l = "no"), t.forEach((t => {
            let e = $(t);
            if (p && e.find(".avatar-box").length > 0) return;
            const {carNum: c, aHref: d, title: h} = this.findCarNumAndHref(e), u = `${c}-hide`, m = `${c}-keywordHide`;
            if ("no" === l && e.attr("data-hide") === u && (e.show(), e.removeAttr("data-hide")), 
            n.some((t => h.includes(t) || c.includes(t))) && e.attr("data-keyword-hide") !== m) return e.hide(), 
            void e.attr("data-keyword-hide", m);
            if (a.includes(c) && "yes" === l && e.attr("data-hide") !== u) return e.hide(), 
            void e.attr("data-hide", u);
            if (s.includes(c) && "yes" === l && e.attr("data-hide") !== u) return e.hide(), 
            void e.attr("data-hide", u);
            if (r.includes(c) && "yes" === l && e.attr("data-hide") !== u) return e.hide(), 
            void e.attr("data-hide", u);
            if (o.includes(c) && "yes" === l && e.attr("data-hide") !== u) return e.hide(), 
            void e.attr("data-hide", u);
            let f = "", v = "";
            if (a.includes(c) ? (f = b, v = y) : i.includes(c) ? (f = k, v = _) : s.includes(c) ? (f = C, 
            v = S) : r.includes(c) && (f = P, v = B), e.find(".status-tag").remove(), f && (g && e.find(".tags").append(`\n                    <span class="tag is-success status-tag" \n                        style="margin-right: 5px; border-radius:10px; position:absolute; right: 0; top:5px;z-index:10;background-color: ${v} !important;">\n                        ${f}\n                    </span>`), 
            p)) {
                let t = `<a class="a-primary status-tag" style="margin-right: 5px; padding: 0 5px;color: #fff; border-radius:10px; position:absolute; right: 0; top:5px;z-index:10;background-color: ${v} !important;"><span class="tag">${f}</span></a>`;
                e.find(".item-tag").append(t);
            }
        })), $("#waitDownBtn span").text(`打开已收藏 (${i.length})`);
    }
    async bindClick() {
        let t = this.getSelector();
        $(t.boxSelector).on("click", ".item img", (async t => {
            if (t.preventDefault(), t.stopPropagation(), $(t.target).closest("div.meta-buttons").length) return;
            const e = $(t.target).closest(".item"), {carNum: n, aHref: a} = this.findCarNumAndHref(e);
            let i = await storageManager.getSetting("dialogOpenDetail", "yes");
            if (n.includes("FC2-")) {
                let t = this.parseMovieId(a);
                this.getBean("fc2Plugin").openFc2Dialog(t, n, a);
            } else "yes" === i ? utils.openPage(a, n, !1, t) : window.open(a);
        })), $(t.boxSelector).on("click", ".item video", (async t => {
            const e = t.currentTarget;
            e.paused ? e.play().catch((t => console.error("播放失败:", t))) : e.pause(), t.preventDefault(), 
            t.stopPropagation();
        })), $(t.boxSelector).on("click", ".item .video-title", (async t => {
            const e = $(t.target).closest(".item"), {carNum: n, aHref: a} = this.findCarNumAndHref(e);
            if (n.includes("FC2-")) {
                t.preventDefault();
                let e = this.parseMovieId(a);
                this.getBean("fc2Plugin").openFc2Dialog(e, n, a);
            }
        })), $(t.boxSelector).on("contextmenu", ".item img, .item video", (t => {
            t.preventDefault();
            const e = $(t.target).closest(".item"), {carNum: n, aHref: a} = this.findCarNumAndHref(e);
            utils.q(t, `是否屏蔽番号 ${n}?`, (async () => {
                await storageManager.saveCar(n, a, "", u), window.refresh(), show.ok("操作成功");
            }));
        }));
    }
    findCarNumAndHref(t) {
        let e, n, a = t.find("a").attr("href");
        if (g) {
            e = t.find(".video-title").find("strong").text(), n = t.find(".video-title").text();
        }
        if (p && (e = a.split("/").filter(Boolean).pop(), n = t.find("img").attr("title")), 
        !e) {
            const t = "提取番号信息失败";
            throw show.error(t), new Error(t);
        }
        return {
            carNum: e,
            aHref: a,
            title: n
        };
    }
    showCarNumBox(t) {
        const e = $(".movie-list .item").toArray().find((e => $(e).find(".video-title strong").text() === t));
        if (e) {
            const n = $(e);
            n.attr("data-hide") === `${t}-hide` && (n.show(), n.removeAttr("data-hide"));
        }
    }
    replaceHdImg(t) {
        if (t || (t = document.querySelectorAll(this.getSelector().coverImgSelector)), g && t.forEach((t => {
            t.src = t.src.replace("thumbs", "covers");
        })), p) {
            const e = /\/(imgs|pics)\/(thumb|thumbs)\//, n = /(\.jpg|\.jpeg|\.png)$/i, a = t => {
                t.src && e.test(t.src) && "true" !== t.dataset.hdReplaced && (t.src = t.src.replace(e, "/$1/cover/").replace(n, "_b$1"), 
                t.dataset.hdReplaced = "true", t.loading = "lazy");
            };
            t.forEach((t => {
                a(t);
            }));
        }
    }
}

class AutoPagePlugin extends BasePlugin {
    constructor() {
        super(), this.paging = !1;
    }
    async handle() {
        window.isListPage && !this.shouldDisablePaging() && this.bindPageClick().then();
    }
    async bindPageClick() {
        $(".pagination-link, .pagination-next, .pagination-previous, .pagination li a").on("click", (t => {
            t.preventDefault(), t.stopPropagation();
            let e = $(t.target).attr("href");
            this.parsePage(e).then();
        }));
    }
    async parsePage(t) {
        let e = loading();
        try {
            const e = await http.get(t), n = (new DOMParser).parseFromString(e, "text/html");
            p && $(n).find(".avatar-box").length > 0 && $(n).find(".avatar-box").parent().remove();
            let a = n.querySelectorAll(this.getSelector().requestDomItemSelector), i = n.querySelectorAll(".pagination");
            const s = this.getBean("listPagePlugin");
            await s.filterMovieList(a);
            let r = n.querySelectorAll(this.getSelector().coverImgSelector);
            s.replaceHdImg(r);
            let o = $(this.getSelector().boxSelector);
            if (o.fadeOut(300, (() => {
                o.html(a).fadeIn(300, (async () => {}));
            })), $(".pagination").replaceWith(i), window.history.pushState({}, "", t), p) {
                const e = this.getPageNumberFromUrl(t);
                document.title = document.title.replace(/第\d+頁/, "第" + e + "頁");
            }
            await utils.smoothScrollToTop(), await this.bindPageClick(), await this.handlePaging();
        } finally {
            e.close();
        }
    }
    getPageNumberFromUrl(t) {
        const e = t.match(/\/(page|star\/.*?)\/(\d+)/);
        return e ? parseInt(e[2], 10) : null;
    }
    shouldDisablePaging() {
        return [ "search?q", "handlePlayback=1", "handleTop=1", "/want_watch_videos", "/watched_videos", "/advanced_search?type=100" ].some((t => window.location.href.includes(t)));
    }
    async handlePaging() {
        if (this.shouldDisablePaging()) return;
        if ($("#no-page").length) return;
        if (0 === $(this.getSelector().boxSelector).length) return;
        if (!window.isListPage) return;
        if (this.paging) return;
        let t = !0;
        if ($(`${this.getSelector().itemSelector}:visible`).each(((e, n) => {
            0 === $(n).find(`span.tag:contains(${b})`).length && 0 === $(n).find(`span.tag:contains(${k})`).length && 0 === $(n).find(`span.tag:contains(${C})`).length && 0 === $(n).find(`span.tag:contains(${P})`).length && (t = !1);
        })), !t) return;
        if ("yes" !== await storageManager.getSetting("autoPage", "no")) return;
        let e = null;
        g && (e = $(".pagination-next")), p && (e = $("#next")), e && 0 !== e.length && (this.paging = !0, 
        show.info("下一页....", {
            duration: 100,
            callback: () => {
                e[0].click(), this.paging = !1;
            }
        }));
    }
}

class HighlightMagnetPlugin extends BasePlugin {
    handle() {
        this.handleDb(), this.handleBus();
    }
    handleDb() {
        let t = $("#magnets-content .name").toArray();
        if (0 === t.length) return;
        let e = !1;
        t.forEach((t => {
            let n = $(t), a = n.text().toLowerCase();
            a.includes("4k") && n.css("color", "#f40"), (a.includes("-c") || a.includes("-u") || a.includes("-uc") || a.includes("4k")) && (e = !0);
        })), e ? t.forEach((t => {
            let e = $(t), n = e.text().toLowerCase();
            n.includes("-c") || n.includes("-u") || n.includes("-uc") || n.includes("4k") || e.parent().parent().parent().hide();
        })) : $("#enable-magnets-filter").addClass("do-hide");
    }
    handleBus() {
        p && isDetailPage && utils.loopDetector((() => $("#magnet-table td a").length > 0), (() => {
            let t = $("#magnet-table tr td:first-child a:first-child").toArray(), e = !1;
            t.forEach((t => {
                let n = $(t), a = n.text().toLowerCase();
                a.includes("4k") && n.css("color", "#f40"), (a.includes("-c") || a.includes("-u") || a.includes("-uc") || a.includes("4k")) && (e = !0);
            })), e ? t.forEach((t => {
                let e = $(t), n = e.text().toLowerCase();
                n.includes("-c") || n.includes("-u") || n.includes("-uc") || n.includes("4k") || e.parent().parent().hide();
            })) : $("#enable-magnets-filter").addClass("do-hide");
        }));
    }
    showAll() {
        if (g) {
            $("#magnets-content .item").toArray().forEach((t => $(t).show()));
        }
        p && $("#magnet-table tr").toArray().forEach((t => $(t).show()));
    }
}

class AliyunApi {
    constructor(t) {
        this.baseApiUrl = "https://api.aliyundrive.com", this.refresh_token = t, this.authorization = null, 
        this.default_drive_id = null, this.backupFolderId = null;
    }
    async getDefaultDriveId() {
        return this.default_drive_id || (this.userInfo = await this.getUserInfo(), this.default_drive_id = this.userInfo.default_drive_id), 
        this.default_drive_id;
    }
    async getHeaders() {
        return this.authorization || (this.authorization = await this.getAuthorization()), 
        {
            authorization: this.authorization
        };
    }
    async getAuthorization() {
        let t = this.baseApiUrl + "/v2/account/token", e = {
            refresh_token: this.refresh_token,
            grant_type: "refresh_token"
        };
        try {
            return "Bearer " + (await http.post(t, e)).access_token;
        } catch (n) {
            throw n.message.includes("is not valid") ? new Error("refresh_token无效, 请重新填写并保存") : n;
        }
    }
    async getUserInfo() {
        const t = await this.getHeaders();
        let e = this.baseApiUrl + "/v2/user/get";
        return await http.post(e, {}, t);
    }
    async deleteFile(t, e = null) {
        if (!t) throw new Error("未传入file_id");
        e || (e = await this.getDefaultDriveId());
        let n = {
            file_id: t,
            drive_id: e
        }, a = this.baseApiUrl + "/v2/recyclebin/trash";
        const i = await this.getHeaders();
        return await gmHttp.post(a, n, i), {};
    }
    async createFolder(t, e = null, n = "root") {
        e || (e = await this.getDefaultDriveId());
        let a = this.baseApiUrl + "/adrive/v2/file/createWithFolders", i = {
            name: t,
            type: "folder",
            parent_file_id: n,
            check_name_mode: "auto_rename",
            content_hash_name: "sha1",
            drive_id: e
        };
        const s = await this.getHeaders();
        return await gmHttp.post(a, i, s);
    }
    async getFileList(t = "root", e = null) {
        e || (e = await this.getDefaultDriveId());
        let n = this.baseApiUrl + "/adrive/v3/file/list";
        const a = {
            drive_id: e,
            parent_file_id: t,
            limit: 200,
            all: !1,
            url_expire_sec: 14400,
            image_thumbnail_process: "image/resize,w_256/format,avif",
            image_url_process: "image/resize,w_1920/format,avif",
            video_thumbnail_process: "video/snapshot,t_120000,f_jpg,m_lfit,w_256,ar_auto,m_fast",
            fields: "*",
            order_by: "updated_at",
            order_direction: "DESC"
        }, i = await this.getHeaders();
        return (await gmHttp.post(n, a, i)).items;
    }
    async uploadFile(t, e, n, a = null) {
        let i = this.baseApiUrl + "/adrive/v2/file/createWithFolders";
        a || (a = await this.getDefaultDriveId());
        let s = {
            drive_id: a,
            part_info_list: [ {
                part_number: 1
            } ],
            parent_file_id: t,
            name: e,
            type: "file",
            check_name_mode: "auto_rename"
        };
        const r = await this.getHeaders(), o = await gmHttp.post(i, s, r), l = o.upload_id, c = o.file_id, d = o.part_info_list[0].upload_url;
        console.log("创建完成: ", o), await this._doUpload(d, n);
        const g = await gmHttp.post("https://api.aliyundrive.com/v2/file/complete", s = {
            drive_id: "745851",
            file_id: c,
            upload_id: l
        }, r);
        console.log("标记完成:", g);
    }
    _doUpload(t, e) {
        return new Promise(((n, a) => {
            $.ajax({
                type: "PUT",
                url: t,
                data: e,
                contentType: " ",
                processData: !1,
                success: (t, e, i) => {
                    200 === i.status ? (console.log("上传成功:", t), n({})) : a(i);
                },
                error: t => {
                    console.error("上传失败", t.responseText), a(t);
                }
            });
        }));
    }
    async getDownloadUrl(t, e = null) {
        e || (e = await this.getDefaultDriveId());
        let n = this.baseApiUrl + "/v2/file/get_download_url";
        const a = await this.getHeaders();
        let i = {
            file_id: t,
            drive_id: e
        };
        return (await gmHttp.post(n, i, a)).url;
    }
    async _createBackupFolder(t) {
        const e = await this.getFileList();
        let n = null;
        for (let a = 0; a < e.length; a++) {
            let i = e[a];
            if (i.name === t) {
                n = i;
                break;
            }
        }
        n || (console.log("不存在目录, 进行创建"), n = await this.createFolder(t)), this.backupFolderId = n.file_id;
    }
    async backup(t, e, n) {
        this.backupFolderId || await this._createBackupFolder(t), await this.uploadFile(this.backupFolderId, e, n);
    }
    async getBackupList(t) {
        let e;
        this.backupFolderId || await this._createBackupFolder(t), e = await this.getFileList(this.backupFolderId);
        const n = [];
        return e.forEach((t => {
            n.push({
                name: t.name,
                fileId: t.file_id,
                createTime: t.created_at,
                size: t.size
            });
        })), n;
    }
}

class WebDavApi {
    constructor(t, e, n) {
        this.davUrl = t.endsWith("/") ? t : t + "/", this.username = e, this.password = n, 
        this.folderName = null;
    }
    _getAuthHeaders() {
        return {
            Authorization: `Basic ${btoa(`${this.username}:${this.password}`)}`,
            Depth: "1"
        };
    }
    _sendRequest(t, e, n = {}, a) {
        return new Promise(((i, s) => {
            const r = this.davUrl + e, o = {
                ...this._getAuthHeaders(),
                ...n
            };
            GM_xmlhttpRequest({
                method: t,
                url: r,
                headers: o,
                data: a,
                onload: t => {
                    t.status >= 200 && t.status < 300 ? i(t) : s(new Error(`Request failed with status ${t.status}: ${t.statusText}`));
                },
                onerror: t => {
                    console.error("请求WebDav发生错误:", t), s(new Error("请求WebDav失败, 请检查服务是否启动, 凭证是否正确"));
                }
            });
        }));
    }
    async backup(t, e, n) {
        await this._sendRequest("MKCOL", t);
        const a = t + "/" + e;
        await this._sendRequest("PUT", a, {
            "Content-Type": "text/plain"
        }, n);
    }
    async getFileList(t) {
        var e, n;
        const a = (await this._sendRequest("PROPFIND", t, {
            "Content-Type": "application/xml"
        }, '\n                <?xml version="1.0"?>\n                <d:propfind xmlns:d="DAV:">\n                    <d:prop>\n                        <d:displayname />\n                        <d:getcontentlength />\n                        <d:creationdate />\n                        <d:iscollection />\n                    </d:prop>\n                </d:propfind>\n            ')).responseText, i = (new DOMParser).parseFromString(a, "text/xml").getElementsByTagNameNS("DAV:", "response"), s = [];
        for (let r = 0; r < i.length; r++) {
            if (0 === r) continue;
            if ("1" === i[r].getElementsByTagNameNS("DAV:", "iscollection")[0].textContent) continue;
            const t = i[r].getElementsByTagNameNS("DAV:", "displayname")[0].textContent, a = (null == (e = i[r].getElementsByTagNameNS("DAV:", "getcontentlength")[0]) ? void 0 : e.textContent) || "0", o = (null == (n = i[r].getElementsByTagNameNS("DAV:", "creationdate")[0]) ? void 0 : n.textContent) || "";
            s.push({
                fileId: t,
                name: t,
                size: a,
                createTime: o
            });
        }
        return s.reverse(), s;
    }
    async deleteFile(t) {
        let e = this.folderName + "/" + encodeURI(t);
        await this._sendRequest("DELETE", e, {
            "Cache-Control": "no-cache"
        });
    }
    async getBackupList(t) {
        return this.folderName = t, await this._sendRequest("MKCOL", t), this.getFileList(t);
    }
    async getFileContent(t) {
        let e = this.folderName + "/" + t;
        return (await this._sendRequest("GET", e, {
            Accept: "application/octet-stream"
        })).responseText;
    }
}

class SettingPlugin extends BasePlugin {
    constructor() {
        super(...arguments), l(this, a), o(this, "folderName", "JHS-数据备份");
    }
    getDefaultGridColumns() {
        return window.innerWidth < 600 ? 1 : window.innerWidth < 900 ? 2 : window.innerWidth < 1e3 ? 3 : window.innerWidth < 1200 ? 4 : 5;
    }
    async initCss() {
        let t = await storageManager.getSetting("containerWidth", "100"), e = await storageManager.getSetting("containerColumns", this.getDefaultGridColumns()), n = `\n            section .container{\n                max-width: 1000px !important;\n                min-width: ${t}%;\n            }\n            .movie-list{\n                grid-template-columns: repeat(${e}, minmax(0, 1fr));\n            }\n        `;
        return p && (n = `\n                .container-fluid .row{\n                    max-width: 1000px !important;\n                    min-width: ${t}%;\n                    margin: auto auto;\n                }\n                \n                .masonry {\n                    grid-template-columns: repeat(${e}, minmax(0, 1fr));\n                }\n            `), 
        `\n            <style>\n                ${n}\n                .nav-btn::after {\n                    content:none !important;\n                }\n                \n                .setting-item {\n                    display: flex;\n                    align-items: center;\n                    justify-content: space-between;\n                    margin-bottom: 10px;\n                    padding: 10px;\n                    border: 1px solid #ddd;\n                    border-radius: 5px;\n                    /*background-color: #f9f9f9;*/\n                }\n                .setting-label {\n                    font-size: 14px;\n                    min-width: 180px;\n                    font-weight: bold;\n                    margin-right: 10px;\n                }\n                .form-content{\n                    max-width: 160px;\n                    min-width: 160px;\n                }\n                .form-content * {\n                    width: 100%;\n                    padding: 5px;\n                    margin-right: 10px;\n                    text-align: center;\n                }\n                .keyword-label {\n                    display: inline-flex;\n                    align-items: center;\n                    padding: 4px 8px;\n                    border-radius: 4px;\n                    color: white;\n                    font-size: 14px;\n                    position: relative;\n                    margin-left: 8px;\n                    margin-bottom: 2px;\n                }\n                \n                .keyword-remove {\n                    margin-left: 6px;\n                    cursor: pointer;\n                    font-size: 12px;\n                    line-height: 1;\n                }\n                \n                .keyword-input {\n                    padding: 6px 12px;\n                    border: 1px solid #ccc;\n                    border-radius: 4px;\n                    font-size: 14px;\n                    float:right;\n                }\n                \n                .add-tag-btn {\n                    padding: 6px 12px;\n                    background-color: #45d0b6;\n                    color: white;\n                    border: none;\n                    border-radius: 4px;\n                    cursor: pointer;\n                    font-size: 14px;\n                    margin-left: 8px;\n                    float:right;\n                }\n                \n                .add-tag-btn:hover {\n                    background-color: #3fceb7;\n                }\n                #saveBtn,#moreBtn {\n                    padding: 8px 20px;\n                    background-color: #4CAF50;\n                    color: white;\n                    border: none;\n                    border-radius: 4px;\n                    cursor: pointer;\n                    font-size: 16px;\n                    margin-top: 10px;\n                }\n                #saveBtn:hover {\n                    background-color: #45a049;\n                }\n                #moreBtn{\n                    background-color: #ad8731;\n                    color: white;\n                }\n                #moreBtn:hover {\n                    background-color: #dc9f11;\n                }\n                \n                .simple-setting, .mini-simple-setting {\n                    display: none; /* 默认隐藏 */\n                    background: rgba(255,255,255,0.9); \n                    position: absolute;\n                    top: 35px; /* 在按钮正下方显示 */\n                    right: -200%;\n                    z-index: 1000;\n                    border: 1px solid #ddd;\n                    border-radius: 4px;\n                    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);\n                    padding: 0;\n                    margin-top: 5px; /* 稍微拉开一点距离 */\n                    color: #363131;\n                }\n                \n                .mini-switch {\n                  appearance: none;\n                  -webkit-appearance: none;\n                  width: 40px;\n                  height: 20px;\n                  background: #e0e0e0;\n                  border-radius: 20px;\n                  position: relative;\n                  cursor: pointer;\n                  outline: none;\n                  /*transition: all 0.2s ease;*/\n                }\n                \n                .mini-switch:checked {\n                  background: #4CAF50;\n                }\n                \n                .mini-switch::before {\n                  content: "";\n                  position: absolute;\n                  width: 16px;\n                  height: 16px;\n                  border-radius: 50%;\n                  background: white;\n                  top: 2px;\n                  left: 2px;\n                  box-shadow: 0 1px 3px rgba(0,0,0,0.2);\n                  /*transition: all 0.2s ease;*/\n                }\n                \n                .mini-switch:checked::before {\n                  left: calc(100% - 18px);\n                }\n            </style\n        `;
    }
    handle() {
        if (g) {
            let t = function() {
                $(".navbar-search").is(":hidden") ? ($(".mini-setting-box").hide(), $(".setting-box").show()) : ($(".mini-setting-box").show(), 
                $(".setting-box").hide());
            };
            $("#navbar-menu-user .navbar-end").prepend('<div class="navbar-item has-dropdown is-hoverable setting-box" style="position:relative;">\n                    <a id="setting-btn" class="navbar-link nav-btn" style="color: #ff8400 !important;padding-right:15px !important;">\n                        设置\n                    </a>\n                    <div class="simple-setting"></div>\n                </div>'), 
            utils.loopDetector((() => $("#miniHistoryBtn").length > 0), (() => {
                $(".miniHistoryBtnBox").before('\n                    <div class="navbar-item mini-setting-box" style="position:relative;margin-left: auto;">\n                        <a id="mini-setting-btn" class="navbar-link nav-btn" style="color: #ff8400 !important;padding-left:0 !important;padding-right:0 !important;">\n                            设置\n                        </a>\n                        <div class="mini-simple-setting"></div>\n                    </div>\n                '), 
                t();
            })), $(window).resize(t);
        }
        p && $("#navbar").append(`\n                <ul class="nav navbar-nav navbar-right setting-box">\n                    <li><a id="setting-btn" style="color: #ff8400 !important;padding-right:15px !important;" role="button">设置</a><div class="simple-setting">${this.simpleSetting()}</div></li>\n                </ul>\n           `);
        $(".main-nav, .top-bar").on("click", "#setting-btn, #mini-setting-btn", (() => {
            layer.open({
                type: 1,
                title: "设置",
                content: '\n            <div style=" display: flex; flex-direction: column; height: 100%; ">\n                <div style="margin: 20px">\n                  <a id="importBtn" class="menu-btn" style="background-color:#d25a88"><span>导入数据</span></a>\n                  <a id="exportBtn" class="menu-btn" style="background-color:#85d0a3"><span>导出数据</span></a>\n                  <a id="syncDataBtn" class="menu-btn" style="background-color:#387ca9"><span>同步数据</span></a>\n                  <a id="getRefreshTokenBtn" class="menu-btn fr-btn" style="background-color:#c4a35e"><span>获取refresh_token</span></a>\n\n                </div>\n                <div style=" flex: 1; overflow-y: auto; margin: 0 20px; padding-bottom: 20px; ">\n                    <div class="setting-item">\n                        <span class="setting-label">阿里云盘备份</span>\n                        <div>\n                            <a id="backupBtn" class="menu-btn" style="background-color:#5d87c2"><span>备份数据</span></a>\n                            <a id="backupListBtn" class="menu-btn" style="background-color:#48c554"><span>查看备份</span></a>\n                        </div>\n                    </div>\n                    \n                    <div class="setting-item">\n                        <span class="setting-label">refresh_token:</span>\n                        <div class="form-content">\n                            <input id="refresh_token" >\n                        </div>\n                    </div>\n                    \n                    <hr style="border: 0; height: 2px; margin:20px 0;background-image: linear-gradient(to right, rgba(0,0,0,0), rgba(0,0,0,0.75), rgba(0,0,0,0));"/>\n                    \n                    <div class="setting-item">\n                        <span class="setting-label">WebDav备份</span>\n                        <div>\n                            <a id="webdavBackupBtn" class="menu-btn" style="background-color:#5d87c2"><span>备份数据</span></a>\n                            <a id="webdavBackupListBtn" class="menu-btn" style="background-color:#48c554"><span>查看备份</span></a>\n                        </div>\n                    </div>\n                    \n                    <div class="setting-item">\n                        <span class="setting-label">服务地址:</span>\n                        <div class="form-content">\n                            <input id="webDavUrl" >\n                        </div>\n                    </div>\n                    \n                    <div class="setting-item">\n                        <span class="setting-label">用户名:</span>\n                        <div class="form-content">\n                            <input id="webDavUsername" >\n                        </div>\n                    </div>\n                    \n                    <div class="setting-item">\n                        <span class="setting-label">密码:</span>\n                        <div class="form-content">\n                            <input id="webDavPassword" >\n                        </div>\n                    </div>\n                    \n                    <hr style="border: 0; height: 2px; margin:20px 0;background-image: linear-gradient(to right, rgba(0,0,0,0), rgba(0,0,0,0.75), rgba(0,0,0,0));"/>\n                    \n                    <div class="setting-item">\n                        <span class="setting-label">域名-MissAv:</span>\n                        <div class="form-content">\n                            <input id="missAvUrl" >\n                        </div>\n                    </div>\n                    <div class="setting-item">\n                        <span class="setting-label">域名-Jable:</span>\n                        <div class="form-content">\n                            <input id="jableUrl" >\n                        </div>\n                    </div>\n                    <div class="setting-item">\n                        <span class="setting-label">域名-Avgle:</span>\n                        <div class="form-content">\n                            <input id="avgleUrl" >\n                        </div>\n                    </div>\n                    <div class="setting-item">\n                        <span class="setting-label">域名-JavTrailer:</span>\n                        <div class="form-content">\n                            <input id="javTrailersUrl" >\n                        </div>\n                    </div>\n                     <div class="setting-item">\n                        <span class="setting-label">域名-123Av:</span>\n                        <div class="form-content">\n                            <input id="av123Url" >\n                        </div>\n                    </div>\n                    \n                    <div class="setting-item">\n                        <span class="setting-label">域名-JavDb:</span>\n                        <div class="form-content">\n                            <input id="javDbUrl" >\n                        </div>\n                    </div>\n                    \n                    <div class="setting-item">\n                        <span class="setting-label">域名-JavBus:</span>\n                        <div class="form-content">\n                            <input id="javBusUrl" >\n                        </div>\n                    </div>\n                    \n                    <hr style="border: 0; height: 2px; margin:20px 0;background-image: linear-gradient(to right, rgba(0,0,0,0), rgba(0,0,0,0.75), rgba(0,0,0,0));"/>\n                    \n                    <div class="setting-item">\n                        <span class="setting-label">115网盘-cookie (快捷登录,需包含"UID", "CID", "KID", "SEID"):</span>\n                        <div class="form-content">\n                            <input id="cookie115" >\n                        </div>\n                    </div>\n                    \n                    <hr style="border: 0; height: 2px; margin:20px 0;background-image: linear-gradient(to right, rgba(0,0,0,0), rgba(0,0,0,0.75), rgba(0,0,0,0));"/>\n                    \n                    <div class="setting-item">\n                        <span class="setting-label">预览视频默认画质:</span>\n                        <div class="form-content">\n                            <select id="videoQuality">\n                                <option value="mmb">中画质 (432p)</option>\n                                <option value="mhb">高画质 (576p)</option>\n                                <option value="hmb">HD (720p)</option>\n                                <option value="hhb">FullHD (1080p)</option>\n                            </select>\n                        </div>\n                    </div>\n                    \n                    <div class="setting-item">\n                        <span class="setting-label">评论区条数:</span>\n                        <div class="form-content">\n                            <select id="reviewCount">\n                                <option value="10">10条</option>\n                                <option value="20">20条</option>\n                                <option value="30">30条</option>\n                                <option value="40">40条</option>\n                                <option value="50">50条</option>\n                            </select>\n                        </div>\n                    </div>\n                    \n                    <div class="setting-item">\n                        <span class="setting-label">每次打开待鉴定待下载数量:</span>\n                        <div class="form-content">\n                            <input type="number" id="waitCheckCount" min="1" max="20" style="width: 100%;">\n                        </div>\n                    </div>\n                    \n                    <hr style="border: 0; height: 2px; margin:20px 0;background-image: linear-gradient(to right, rgba(0,0,0,0), rgba(0,0,0,0.75), rgba(0,0,0,0));"/>\n                    \n                    <div class="setting-item">\n                        <span class="setting-label">评论区屏蔽词:</span>\n                        <div id="reviewKeywordContainer" style="max-width: 50%;min-width: 50%;">\n                            <div class="tag-box">\n                            </div>\n                            <div style="margin-top: 10px;">\n                                <button class="add-tag-btn">添加</button>\n                                <input type="text" class="keyword-input" placeholder="添加屏蔽词">\n                            </div>\n                        </div>\n                    </div>\n                    \n                    <div class="setting-item">\n                        <span class="setting-label">视频标题屏蔽词:</span>\n                        <div id="filterKeywordContainer" style="max-width: 50%;min-width: 50%;">\n                            <div class="tag-box">\n                            </div>\n                            <div style="margin-top: 10px;">\n                                <button class="add-tag-btn">添加</button>\n                                <input type="text" class="keyword-input" placeholder="添加屏蔽词">\n                            </div>\n                        </div>\n                    </div>\n                    \n                    <div class="setting-item">\n                        <span class="setting-label">屏蔽男演员:</span>\n                        <div id="filterActorContainer" style="max-width: 50%;min-width: 50%;">\n                            <div class="tag-box">\n                            </div>\n                        </div>\n                    </div>\n                    \n                    <div class="setting-item">\n                        <span class="setting-label">屏蔽女演员:</span>\n                        <div id="filterActressContainer" style="max-width: 50%;min-width: 50%;">\n                            <div class="tag-box">\n                            </div>\n                        </div>\n                    </div>\n                </div>\n                <div style=" flex-shrink: 0; padding: 15px 20px; text-align: right; border-top: 1px solid #eee; background: white; ">   \n                    <button id="saveBtn">保存设置</button>\n                </div>\n            </div>\n        ',
                area: utils.getResponsiveArea(),
                scrollbar: !1,
                success: (t, e) => {
                    $(t).find(".layui-layer-content").css("position", "relative"), this.loadForm(), 
                    this.bindClick();
                }
            });
        })), $(".main-nav, .top-bar").on("mouseenter", ".setting-box", (() => {
            $(".simple-setting").html(this.simpleSetting()).show(), this.initSimpleSettingForm().then();
        })).on("mouseleave", ".setting-box", (() => {
            $(".simple-setting").html("").hide();
        })), $(".main-nav, .top-bar").on("mouseenter", ".mini-setting-box", (() => {
            $(".mini-simple-setting").html(this.simpleSetting()).show(), this.initSimpleSettingForm().then();
        })).on("mouseleave", ".mini-setting-box", (() => {
            $(".mini-simple-setting").html("").hide();
        }));
    }
    simpleSetting() {
        return '\n             <div style="display: flex; flex-direction: column; height: 100%;margin-top:20px">\n                <div style=" flex: 1; overflow-y: auto; margin: 0 10px; ">\n                    <div class="setting-item">\n                        <span class="setting-label">隐藏已鉴定内容:</span>\n                        <div class="form-content">\n                            <input type="checkbox" id="hideFilterItem" class="mini-switch">\n                        </div>\n                    </div>\n                    \n                    <div class="setting-item">\n                        <span class="setting-label">弹窗方式打开页面:</span>\n                        <div class="form-content">\n                             <input type="checkbox" id="dialogOpenDetail" class="mini-switch">\n                        </div>\n                    </div>      \n                    \n                    <div class="setting-item">\n                        <span class="setting-label">鉴定后立即关闭当前页面:</span>\n                        <div class="form-content">\n                            <input type="checkbox" id="needClosePage" class="mini-switch">\n                        </div>\n                    </div>\n                    \n                    <div class="setting-item">\n                        <span class="setting-label">自动翻页(当前页全鉴定完时触发):</span>\n                        <div class="form-content">\n                            <input type="checkbox" id="autoPage" class="mini-switch">\n                        </div>\n                    </div>\n       \n                                    \n                    <div class="setting-item">\n                        <span class="setting-label">页面列数: <span id="showContainerColumns"></span></span>\n                        <div class="form-content">\n                            <input type="range" id="containerColumns" min="3" max="10" step="1" style="padding:5px 0">\n                        </div>\n                    </div>\n                    \n                    <div class="setting-item">\n                        <span class="setting-label">页面宽度: <span id="showContainerWidth"></span></span>\n                        <div class="form-content">\n                            <input type="range" id="containerWidth" min="0" max="30" step="1" style="padding:5px 0">\n                        </div>\n                    </div>\n                </div>\n                <div style="flex-shrink: 0; padding: 0 20px 15px; text-align: right; border-top: 1px solid #eee;">   \n                    <button id="moreBtn">更多设置</button>\n                </div>\n            </div>\n        ';
    }
    async loadForm() {
        let t = await storageManager.getSetting();
        $("#videoQuality").val(t.videoQuality || "hhb"), $("#reviewCount").val(t.reviewCount || 20), 
        $("#waitCheckCount").val(t.waitCheckCount || 5), $("#refresh_token").val(t.refresh_token || ""), 
        $("#webDavUrl").val(t.webDavUrl || ""), $("#webDavUsername").val(t.webDavUsername || ""), 
        $("#webDavPassword").val(t.webDavPassword || ""), $("#cookie115").val(t.cookie115 || "");
        const e = this.getBean("OtherSitePlugin"), n = await e.getMissAvUrl(), a = await e.getjableUrl(), i = await e.getAvgleUrl(), s = await e.getJavTrailersUrl(), r = await e.getAv123Url(), o = await e.getJavDbUrl(), l = await e.getJavBusUrl();
        $("#missAvUrl").val(n), $("#jableUrl").val(a), $("#avgleUrl").val(i), $("#javTrailersUrl").val(s), 
        $("#av123Url").val(r), $("#javDbUrl").val(o), $("#javBusUrl").val(l);
        const c = await storageManager.getActressFilterCarMap();
        Object.keys(c).forEach((t => {
            let e = c[t].length;
            this.addLabelTag("#filterActressContainer", t.split("_").filter(Boolean).pop() + ` (${e})`, `当前已屏蔽数量:${e}`, t);
        }));
        const d = await storageManager.getActorFilterCarMap();
        Object.keys(d).forEach((t => {
            let e = d[t].length;
            this.addLabelTag("#filterActorContainer", t.split("_").filter(Boolean).pop() + ` (${e})`, `当前已屏蔽数量:${e}`, t);
        }));
        let g = await storageManager.getReviewFilterKeywordList(), p = await storageManager.getTitleFilterKeyword();
        g && g.forEach((t => {
            this.addLabelTag("#reviewKeywordContainer", t);
        })), p && p.forEach((t => {
            this.addLabelTag("#filterKeywordContainer", t);
        })), [ "#reviewKeywordContainer", "#filterKeywordContainer", "#filterActorContainer", "#filterActressContainer" ].forEach((t => {
            $(`${t} .add-tag-btn`).on("click", (e => this.addKeyword(e, t))), $(`${t} .keyword-input`).on("keypress", (e => {
                "Enter" === e.key && this.addKeyword(e, t);
            }));
        }));
    }
    async initSimpleSettingForm() {
        let t = await storageManager.getSetting();
        $("#hideFilterItem").prop("checked", !t.hideFilterItem || "yes" === t.hideFilterItem), 
        $("#containerColumns").val(t.containerColumns || 4), $("#showContainerColumns").text(t.containerColumns || 4), 
        $("#containerWidth").val((t.containerWidth || 100) - 70), $("#showContainerWidth").text((t.containerWidth || 100) + "%"), 
        $("#dialogOpenDetail").prop("checked", !t.dialogOpenDetail || "yes" === t.dialogOpenDetail), 
        $("#needClosePage").prop("checked", !t.needClosePage || "yes" === t.needClosePage), 
        $("#autoPage").prop("checked", !!t.autoPage && "yes" === t.autoPage), $("#containerColumns").on("input", (t => {
            let e = $("#containerColumns").val();
            if ($("#showContainerColumns").text(e), g) {
                document.querySelector(".movie-list").style.gridTemplateColumns = `repeat(${e}, minmax(0, 1fr))`;
            }
            if (p) {
                document.querySelector(".masonry").style.gridTemplateColumns = `repeat(${e}, minmax(0, 1fr))`;
            }
            storageManager.saveSettingItem("containerColumns", e);
        })), $("#containerWidth").on("input", (t => {
            let e = parseInt($(t.target).val());
            const n = e + 70 + "%";
            if ($("#showContainerWidth").text(n), g) {
                document.querySelector("section .container").style.minWidth = n;
            }
            if (p) {
                document.querySelector(".container-fluid .row").style.minWidth = n;
            }
            storageManager.saveSettingItem("containerWidth", e + 70);
        })), $("#dialogOpenDetail").on("change", (t => {
            let e = $("#dialogOpenDetail").is(":checked") ? "yes" : "no";
            storageManager.saveSettingItem("dialogOpenDetail", e);
        })), $("#hideFilterItem").on("change", (t => {
            let e = $("#hideFilterItem").is(":checked") ? "yes" : "no";
            storageManager.saveSettingItem("hideFilterItem", e), window.refresh();
        })), $("#needClosePage").on("change", (t => {
            storageManager.saveSettingItem("needClosePage", $("#needClosePage").is(":checked") ? "yes" : "no"), 
            window.refresh();
        })), $("#autoPage").on("change", (async t => {
            const e = $("#autoPage").is(":checked") ? "yes" : "no";
            await storageManager.saveSettingItem("autoPage", e), "yes" === e && await this.getBean("autoPagePlugin").handlePaging();
        })), $("#moreBtn").on("click", (() => {
            $(".simple-setting").html("").hide(), $("#setting-btn")[0].click();
        }));
    }
    bindClick() {
        $("#importBtn").on("click", (t => this.importData(t))), $("#exportBtn").on("click", (t => this.exportData(t))), 
        $("#syncDataBtn").on("click", (t => this.syncData(t))), $("#backupBtn").on("click", (t => this.backupData(t))), 
        $("#backupListBtn").on("click", (t => this.backupListBtn(t))), $("#webdavBackupBtn").on("click", (t => this.backupDataByWebDav(t))), 
        $("#webdavBackupListBtn").on("click", (t => this.backupListBtnByWebDav(t))), $("#getRefreshTokenBtn").on("click", (t => layer.alert("即将跳转阿里云盘, 请登录后, 点击最右侧悬浮按钮获取refresh_token", {
            yes: function(t, e, n) {
                window.open("https://www.aliyundrive.com/drive/home"), layer.close(t);
            }
        }))), $("#saveBtn").on("click", (() => this.saveForm()));
    }
    async saveForm() {
        let t = await storageManager.getSetting();
        t.videoQuality = $("#videoQuality").val(), t.reviewCount = $("#reviewCount").val(), 
        t.waitCheckCount = $("#waitCheckCount").val(), t.refresh_token = $("#refresh_token").val(), 
        t.webDavUrl = $("#webDavUrl").val(), t.webDavUsername = $("#webDavUsername").val(), 
        t.webDavPassword = $("#webDavPassword").val(), t.missAvUrl = $("#missAvUrl").val(), 
        t.jableUrl = $("#jableUrl").val(), t.avgleUrl = $("#avgleUrl").val(), t.javTrailersUrl = $("#javTrailersUrl").val(), 
        t.av123Url = $("#av123Url").val(), t.javDbUrl = new URL($("#javDbUrl").val()).origin, 
        t.javBusUrl = $("#javBusUrl").val(), t.cookie115 = $("#cookie115").val(), await storageManager.saveSetting(t);
        let e = [];
        $("#reviewKeywordContainer .keyword-label").toArray().forEach((t => {
            let n = $(t).text().replace("×", "").replace(/[\r\n]+/g, " ").replace(/\s{2,}/g, " ").trim();
            e.push(n);
        })), await storageManager.saveReviewFilterKeyword(e);
        let n = [];
        $("#filterKeywordContainer .keyword-label").toArray().forEach((t => {
            let e = $(t).text().replace("×", "").replace(/[\r\n]+/g, " ").replace(/\s{2,}/g, " ").trim();
            n.push(e);
        })), await storageManager.saveTitleFilterKeyword(n), show.ok("保存成功"), window.refresh();
    }
    addLabelTag(t, e, n, a) {
        const i = $(`${t} .tag-box`), s = $(`\n            <div class="keyword-label" data-keyword="${e}" data-key="${a}" style="background-color: #c5b9a0" title="${n || ""}">\n                ${e}\n                <span class="keyword-remove">×</span>\n            </div>\n        `);
        s.find(".keyword-remove").click((t => {
            const e = $(t.currentTarget);
            if (e.closest("#filterActressContainer, #filterActorContainer").length > 0) {
                let n = e.closest(".keyword-label");
                const a = n.attr("data-keyword").split(" ")[0], i = n.attr("data-key");
                utils.q(t, `是否移除对 ${a} 的屏蔽?  <br/>注意:该操作即时生效, 无需保存设置`, (async () => {
                    await storageManager.removeActorFilter(i), e.parent().remove();
                }));
            } else e.parent().remove();
        })), i.append(s);
    }
    addKeyword(t, e) {
        let n = $(`${e} .keyword-input`);
        const a = n.val().trim();
        a && (this.addLabelTag(e, a), n.val(""));
    }
    importData() {
        try {
            const t = document.createElement("input");
            t.type = "file", t.accept = ".json", t.onchange = t => {
                const e = t.target.files[0];
                if (!e) return;
                const n = new FileReader;
                n.onload = t => {
                    try {
                        const e = t.target.result.toString(), n = JSON.parse(e);
                        layer.confirm("确定是否要覆盖导入?", {
                            icon: 3,
                            title: "确认覆盖",
                            btn: [ "确定", "取消" ]
                        }, (async function(t) {
                            await storageManager.importData(n), show.ok("数据导入成功"), layer.close(t), location.reload();
                        }));
                    } catch (e) {
                        console.error(e), show.error("导入失败:文件内容不是有效的JSON格式 " + e);
                    }
                }, n.onerror = () => {
                    show.error("读取文件时出错");
                }, n.readAsText(e);
            }, document.body.appendChild(t), t.click(), setTimeout((() => document.body.removeChild(t)), 1e3);
        } catch (t) {
            console.error(t), show.error("导入数据时出错: " + t.message);
        }
    }
    async backupData(t) {
        const e = await storageManager.getSetting("refresh_token");
        if (!e) return void show.error("请填写refresh_token并保存后, 再试此功能");
        let n = utils.getNowStr("_", "_") + ".json", a = JSON.stringify(await storageManager.exportData());
        a = K(a);
        let i = loading();
        try {
            const t = new AliyunApi(e);
            await t.backup(this.folderName, n, a), show.ok("备份完成");
        } catch (s) {
            console.error(s), show.error(s.toString());
        } finally {
            i.close();
        }
    }
    async backupListBtn(t) {
        const e = await storageManager.getSetting("refresh_token");
        if (!e) return void show.error("请填写refresh_token并保存后, 再试此功能");
        let n = loading();
        try {
            const t = new AliyunApi(e), n = await t.getBackupList(this.folderName);
            this.openFileListDialog(n, t, "阿里云盘");
        } catch (a) {
            console.error(a), show.error(`发生错误: ${a ? a.message : a}`);
        } finally {
            n.close();
        }
    }
    async backupDataByWebDav(t) {
        const e = await storageManager.getSetting(), n = e.webDavUrl;
        if (!n) return void show.error("请填写webDav服务地址并保存后, 再试此功能");
        const a = e.webDavUsername;
        if (!a) return void show.error("请填写webDav用户名并保存后, 再试此功能");
        const i = e.webDavPassword;
        if (!i) return void show.error("请填写webDav密码并保存后, 再试此功能");
        let s = utils.getNowStr("_", "_") + ".json", r = JSON.stringify(await storageManager.exportData());
        r = K(r);
        let o = loading();
        try {
            const t = new WebDavApi(n, a, i);
            await t.backup(this.folderName, s, r), show.ok("备份完成");
        } catch (l) {
            console.error(l), show.error(l.toString());
        } finally {
            o.close();
        }
    }
    async backupListBtnByWebDav(t) {
        const e = await storageManager.getSetting(), n = e.webDavUrl;
        if (!n) return void show.error("请填写webDav服务地址并保存后, 再试此功能");
        const a = e.webDavUsername;
        if (!a) return void show.error("请填写webDav用户名并保存后, 再试此功能");
        const i = e.webDavPassword;
        if (!i) return void show.error("请填写webDav密码并保存后, 再试此功能");
        let s = loading();
        try {
            const t = new WebDavApi(n, a, i), e = await t.getBackupList(this.folderName);
            this.openFileListDialog(e, t, "WebDav");
        } catch (r) {
            console.error(r), show.error(`发生错误: ${r ? r.message : r}`);
        } finally {
            s.close();
        }
    }
    openFileListDialog(t, e, n) {
        layer.open({
            type: 1,
            title: n + "备份文件",
            content: '<div id="table-container"></div>',
            area: [ "40%", "70%" ],
            success: a => {
                const i = new TableGenerator({
                    containerId: "table-container",
                    columns: [ {
                        key: "name",
                        title: "文件名"
                    }, {
                        key: "createTime",
                        title: "备份日期",
                        render: t => `${utils.getNowStr("-", ":", t.createTime)}`
                    }, {
                        key: "size",
                        title: "文件大小",
                        render: t => {
                            const e = [ "B", "KB", "MB", "GB", "TB", "PB" ];
                            let n = 0, a = t.size;
                            for (;a >= 1024 && n < e.length - 1; ) a /= 1024, n++;
                            return `${a % 1 == 0 ? a.toFixed(0) : a.toFixed(2)} ${e[n]}`;
                        }
                    } ],
                    data: t,
                    buttons: [ {
                        text: "删除",
                        class: "a-danger",
                        onClick: async (t, a) => {
                            layer.confirm(`是否删除 ${a.name} ?`, {
                                icon: 3,
                                title: "提示",
                                btn: [ "确定", "取消" ]
                            }, (async t => {
                                layer.close(t);
                                let s = loading();
                                try {
                                    await e.deleteFile(a.fileId);
                                    let t = await e.getBackupList(this.folderName);
                                    i.update(t), "阿里云盘" === n ? layer.alert("已移至回收站, 请到阿里云盘回收站二次删除") : layer.alert("删除成功");
                                } catch (r) {
                                    console.error(r), show.error(`发生错误: ${r ? r.message : r}`);
                                } finally {
                                    s.close();
                                }
                            }));
                        }
                    }, {
                        text: "下载",
                        class: "a-primary",
                        onClick: t => {
                            let a = loading();
                            try {
                                "阿里云盘" === n ? e.getDownloadUrl(t.fileId).then((e => {
                                    gmHttp.get(e, null, {
                                        Referer: "https://www.aliyundrive.com/"
                                    }).then((e => {
                                        e = J(e), utils.download(e, t.name);
                                    }));
                                })).catch((t => {
                                    console.error(t), show.error("下载失败: " + t);
                                })) : e.getFileContent(t.fileId).then((e => {
                                    e = J(e), utils.download(e, t.name);
                                }));
                            } catch (i) {
                                console.error(i), show.error("下载失败: " + i);
                            } finally {
                                a.close();
                            }
                        }
                    }, {
                        text: "导入",
                        class: "a-success",
                        onClick: t => {
                            layer.confirm(`是否将该云备份数据 ${t.name} 导入?`, {
                                icon: 3,
                                title: "提示",
                                btn: [ "确定", "取消" ]
                            }, (async a => {
                                layer.close(a);
                                let i = loading();
                                try {
                                    let a;
                                    if ("阿里云盘" === n) {
                                        const n = await e.getDownloadUrl(t.fileId);
                                        a = await gmHttp.get(n, null, {
                                            Referer: "https://www.aliyundrive.com/"
                                        });
                                    } else a = await e.getFileContent(t.fileId);
                                    a = J(a);
                                    const i = JSON.parse(a);
                                    await storageManager.importData(i), show.ok("导入成功!"), window.location.reload();
                                } catch (s) {
                                    console.error(s), show.error(s);
                                } finally {
                                    i.close();
                                }
                            }));
                        }
                    } ]
                });
            }
        });
    }
    async exportData(t) {
        try {
            const t = JSON.stringify(await storageManager.exportData()), e = `${utils.getNowStr("_", "_")}.json`;
            utils.download(t, e), show.ok("数据导出成功");
        } catch (e) {
            console.error(e), show.error("导出数据时出错: " + e.message);
        }
    }
    async syncData(t) {
        let e = null, n = null;
        const s = this.getBean("OtherSitePlugin");
        g && (e = "是否将JavBus的数据及配置同步到本站中? ", n = await s.getJavBusUrl() + "/temp?syncData=1"), 
        p && (e = "是否将JavDB的数据及配置同步到本站中? ", n = await s.getJavDbUrl() + "/feedbacks/new?syncData=1"), 
        utils.q(t, e, (() => {
            const t = window.open(n);
            let e = new URL(n).origin;
            console.log("开始连接接受方:", e);
            let s, r = 0;
            this.hasListenMsg || (window.addEventListener("message", (n => {
                if (n.origin === e) if ("ok" === n.data) clearInterval(s), console.log("连接确认,开始同步数据"), 
                t.postMessage("syncData", e); else {
                    const t = n.data;
                    console.log("收到数据", t), c(this, a, i).call(this, t);
                }
            })), this.hasListenMsg = !0);
            const o = () => {
                if (r >= 8) return clearInterval(s), console.log("超过最大重试次数,停止尝试"), void show.error("同步失败, 目标网站已中断, 请检查是否登录后再试!", {
                    close: !0,
                    duration: -1
                });
                console.log(`第 ${r + 1} 次ping...`), t.postMessage("ping", e), r++;
            };
            s = setInterval(o, 1e3), o();
        }));
    }
}

a = new WeakSet, i = async function(t) {
    try {
        const e = t.carList || [], n = t.titleFilterKeyword || [], a = t.reviewFilterKeyword || [], i = t.setting || {}, s = await storageManager.getCarList() || [], r = await storageManager.getTitleFilterKeyword() || [], o = await storageManager.getReviewFilterKeywordList() || [], l = await storageManager.getSetting() || {}, c = [ ...s ];
        e.forEach((t => {
            s.some((e => e.carNum === t.carNum)) || c.push(t);
        }));
        const d = [ ...new Set([ ...r, ...n ]) ], g = [ ...new Set([ ...o, ...a ]) ], p = {
            ...l
        };
        Object.keys(i).forEach((t => {
            t in p && p[t] || (p[t] = i[t]);
        })), await storageManager.overrideCarList(c), await storageManager.saveTitleFilterKeyword(d), 
        await storageManager.saveReviewFilterKeyword(g), await storageManager.saveSetting(p);
        const h = await storageManager.getActressFilterCarMap(), u = await storageManager.getActorFilterCarMap(), m = {
            ...h,
            ...u
        };
        for (const f of Object.keys(t)) if (f.startsWith("car_list_")) {
            let e = [];
            m[f] && m[f].length > 0 ? (e = [ ...m[f] ], t[f].forEach((t => {
                s.some((e => e.carNum === t.carNum)) || e.push(t);
            }))) : e = t[f], await storageManager.setItem(f, e);
        }
        show.ok("同步完成, 关闭提示后, 将重载数据", {
            close: !0,
            duration: -1,
            callback: () => {
                window.location.reload();
            }
        });
    } catch (e) {
        console.error(e), show.error("同步数据时出错:", e);
    }
};

const V = "x7k9p3";

function K(t) {
    return (V + t + V).split("").map((t => {
        const e = t.codePointAt(0);
        return String.fromCodePoint(e + 5);
    })).join("");
}

function J(t) {
    return t.split("").map((t => {
        const e = t.codePointAt(0);
        return String.fromCodePoint(e - 5);
    })).join("").slice(V.length, -V.length);
}

class SyncDataPlugin extends BasePlugin {
    async handle() {
        if (!window.location.href.includes("syncData=1")) return;
        p && $("h4").html("临时页面, 用于同步数据");
        let t = null;
        const e = this.getBean("OtherSitePlugin");
        g && (t = await e.getJavBusUrl()), p && (t = await e.getJavDbUrl()), console.log("等待发送方:", t), 
        window.addEventListener("message", (async e => {
            if (e.origin === t) if ("ping" === e.data) console.log("收到 ping,发送确认"), e.source.postMessage("ok", e.origin); else if ("syncData" === e.data) {
                console.log("开始发送数据...");
                const t = await storageManager.getCarList(), n = await storageManager.getTitleFilterKeyword(), a = await storageManager.getReviewFilterKeywordList(), i = await storageManager.getSetting(), s = await storageManager.getActressFilterCarMap(), r = await storageManager.getActorFilterCarMap();
                e.source.postMessage({
                    carList: t,
                    titleFilterKeyword: n,
                    reviewFilterKeyword: a,
                    setting: i,
                    ...s,
                    ...r
                }, e.origin), show.ok("数据已传输, 即将关闭页面...", {
                    callback: () => {
                        window.close();
                    }
                });
            }
        }));
    }
}

class BusPreviewVideoPlugin extends BasePlugin {
    async initCss() {
        return "\n            .video-control-btn {\n                min-width:100px;\n                padding: 8px 16px;\n                background: rgba(0,0,0,0.7);\n                color: white;\n                border: none;\n                border-radius: 4px;\n                cursor: pointer;\n            }\n            .video-control-btn.active {\n                background-color: #1890ff; /* 选中按钮的背景色 */\n                color: white;             /* 选中按钮的文字颜色 */\n                font-weight: bold;        /* 加粗显示 */\n                border: 2px solid #096dd9; /* 边框样式 */\n            }\n        ";
    }
    handle() {
        if (!isDetailPage) return;
        const t = $("#sample-waterfall a:first").attr("href"), e = $(`\n            <a class="preview-video-container sample-box" style="cursor: pointer">\n                <div class="photo-frame" style="position:relative;">\n                    <img src="${t}" class="video-cover" alt="">\n                    <div class="play-icon" style="position:absolute; top:50%; left:50%; transform:translate(-50%,-50%); \n                            color:white; font-size:40px; text-shadow:0 0 10px rgba(0,0,0,0.5);">\n                        ▶\n                    </div>\n                </div>\n            </a>`);
        $("#sample-waterfall").prepend(e);
        let n = !1, a = $(".preview-video-container");
        a.on("click", (async t => {
            if (t.preventDefault(), t.stopPropagation(), n) show.info("正在加载中, 勿重复点击"); else {
                n = !0;
                try {
                    await this.handleVideo();
                } finally {
                    n = !1;
                }
            }
        })), window.location.href.includes("autoPlay=1") && a[0].click();
    }
    async handleVideo() {
        const t = $("#preview-video");
        if (t.length > 0) return void (t.is(":visible") ? ($("#videoBox").hide(), t[0].pause()) : ($("#videoBox").show(), 
        t[0].play().catch((t => console.error("切换播放失败:", t)))));
        let e = this.getPageInfo().carNum;
        const n = await N(e);
        await this.createQualityBtn(n);
        const a = document.getElementById("preview-video");
        if (a) {
            const t = a.getBoundingClientRect();
            window.scrollTo({
                top: window.scrollY + t.top - 100,
                behavior: "smooth"
            });
        }
    }
    async createQualityBtn(t) {
        let e = await storageManager.getSetting("videoQuality") || "hhb";
        t[e] || (e = Object.keys(t)[0]);
        let n = t[e];
        $("#magneturlpost").next().after(`<div id="videoBox"><video id="preview-video" controls style="width: 100%;margin-top: 5px;"><source src="${n}" /></video></div>`);
        const a = $("#preview-video"), i = a.find("source"), s = a.parent();
        if (!a.length || !i.length) return;
        const r = a[0];
        r.muted = !1, r.play();
        let o = "";
        [ {
            id: "video-mmb",
            quality: "mmb",
            text: "中画质 (432p)"
        }, {
            id: "video-mhb",
            quality: "mhb",
            text: "高画质 (576p)"
        }, {
            id: "video-hmb",
            quality: "hmb",
            text: "HD (720p)"
        }, {
            id: "video-hhb",
            quality: "hhb",
            text: "FullHD (1080p)"
        } ].forEach(((n, a) => {
            let i = t[n.quality];
            if (i) {
                const t = e === n.quality;
                o += `\n                    <button class="video-control-btn${t ? " active" : ""}" \n                            id="${n.id}" \n                            data-quality="${n.quality}"\n                            data-video-src = "${i}"\n                            style="bottom: ${40 * a}px; right: -105px;">\n                        ${n.text}\n                    </button>\n                `;
            }
        })), s.append(o);
        const l = s.find(".video-control-btn");
        s.on("click", ".video-control-btn", (async t => {
            try {
                const e = $(t.currentTarget);
                if (e.hasClass("active")) return;
                let n = e.attr("data-video-src");
                i.attr("src", n), r.load(), r.muted = !1, await r.play(), l.removeClass("active"), 
                e.addClass("active");
            } catch (e) {
                show.error("切换画质失败"), console.error("切换画质失败:", e);
            }
        }));
    }
}

class SearchByImagePlugin extends BasePlugin {
    constructor() {
        super(...arguments), o(this, "siteList", [ {
            name: "TinEye",
            url: "https://tineye.com/search?url={占位符}",
            ico: "https://www.google.com/s2/favicons?sz=64&domain=tineye.com"
        }, {
            name: "Bing",
            url: "https://www.bing.com/images/search?q=imgurl:{占位符}&view=detailv2&iss=sbi",
            ico: "https://www.bing.com/favicon.ico"
        }, {
            name: "Google旧版",
            url: "https://www.google.com/searchbyimage?image_url={占位符}&client=firefox-b-d",
            ico: "https://www.google.com/favicon.ico"
        }, {
            name: "Google",
            url: "https://lens.google.com/uploadbyurl?url={占位符}",
            ico: "https://www.google.com/favicon.ico"
        }, {
            name: "Yandex",
            url: "https://yandex.ru/images/search?rpt=imageview&url={占位符}",
            ico: "https://yandex.ru/favicon.ico"
        } ]), o(this, "isUploading", !1);
    }
    async initCss() {
        return "\n            <style>\n                #upload-area {\n                    border: 2px dashed #85af68;\n                    border-radius: 8px;\n                    padding: 40px;\n                    text-align: center;\n                    margin-bottom: 20px;\n                    transition: all 0.3s;\n                    background-color: #f9f9f9;\n                }\n                #upload-area:hover {\n                    border-color: #76b947;\n                    background-color: #f0f0f0;\n                }\n                /* 拖拽进入 */\n                #upload-area.highlight {\n                    border-color: #2196F3;\n                    background-color: #e3f2fd;\n                }\n                \n                \n                #select-image-btn {\n                    background-color: #4CAF50;\n                    color: white;\n                    border: none;\n                    padding: 10px 20px;\n                    border-radius: 4px;\n                    cursor: pointer;\n                    font-size: 16px;\n                    transition: background-color 0.3s;\n                }\n                #select-image-btn:hover {\n                    background-color: #45a049;\n                }\n                \n                \n                #handle-btn, #cancel-btn {\n                    padding: 8px 16px;\n                    border-radius: 4px;\n                    cursor: pointer;\n                    font-size: 14px;\n                    border: none;\n                    transition: opacity 0.3s;\n                }\n                #handle-btn {\n                    background-color: #2196F3;\n                    color: white;\n                }\n                #handle-btn:hover {\n                    opacity: 0.9;\n                }\n                #cancel-btn {\n                    background-color: #f44336;\n                    color: white;\n                }\n                #cancel-btn:hover {\n                    opacity: 0.9;\n                }\n                \n                .search-img-site-btns-container {\n                    display: flex;\n                    flex-wrap: wrap;\n                    gap: 10px;\n                    margin-top: 15px;\n                }\n                .search-img-site-btn {\n                    display: flex;\n                    align-items: center;\n                    padding: 8px 12px;\n                    background-color: #f5f5f5;\n                    border-radius: 4px;\n                    text-decoration: none;\n                    color: #333;\n                    transition: all 0.2s;\n                    font-size: 14px;\n                    border: 1px solid #ddd;\n                }\n                .search-img-site-btn:hover {\n                    background-color: #e0e0e0;\n                    transform: translateY(-2px);\n                    box-shadow: 0 2px 5px rgba(0,0,0,0.1);\n                }\n                .search-img-site-btn img {\n                    width: 16px;\n                    height: 16px;\n                    margin-right: 6px;\n                }\n                .search-img-site-btn span {\n                    white-space: nowrap;\n                }\n            </style>\n        ";
    }
    open() {
        layer.open({
            type: 1,
            title: "以图识图",
            content: '\n            <div style="padding: 20px">\n                <div id="upload-area">\n                    <div style="color: #555;margin-bottom: 15px;">\n                        <p>拖拽图片到此处 或 点击按钮选择图片</p>\n                        <p>也可以直接 Ctrl+V 粘贴图片或 图片URL</p>\n                    </div>\n                    <button id="select-image-btn">选择图片</button>\n                    <input type="file" style="display: none" id="image-file" accept="image/*">\n                </div>\n                \n                <div id="url-input-container" style="margin-top: 15px;display: none;">\n                    <input type="text" id="image-url" placeholder="粘贴图片URL地址..." style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; box-sizing: border-box;">\n                </div>\n                \n                <div id="preview-area" style="margin-bottom: 20px; text-align: center; display: none;">\n                    <img id="preview-image" alt="" src="" style="max-width: 100%; max-height: 300px; border-radius: 4px; box-shadow: 0 2px 5px rgba(0,0,0,0.1);">\n                    <div style="margin-top: 15px; display: flex; justify-content: center; gap: 10px;" id="action-btns">\n                        <button id="handle-btn">搜索图片</button>\n                        <button id="cancel-btn">取消</button>\n                    </div>\n                    \n                    <div id="search-results" style="display: none;">\n                        <p style="margin: 20px auto">请选择识图网站:<a id="openAll" style="cursor: pointer">全部打开</a></p>\n                        <div class="search-img-site-btns-container" id="search-img-site-btns-container"></div>\n                    </div>\n                </div>\n                \n            </div>\n        ',
            area: utils.isMobile() ? utils.getResponsiveArea() : [ "40%", "80%" ],
            success: async t => {
                this.initEventListeners();
            }
        });
    }
    initEventListeners() {
        const t = $("#upload-area"), e = $("#image-file"), n = $("#select-image-btn"), a = $("#preview-area"), i = $("#preview-image"), s = $("#action-btns"), r = $("#handle-btn"), o = $("#cancel-btn"), l = $("#url-input-container"), c = $("#image-url"), d = $("#search-results"), g = $("#search-img-site-btns-container");
        t.on("dragover", (e => {
            e.preventDefault(), t.addClass("highlight");
        })).on("dragleave", (() => {
            t.removeClass("highlight");
        })).on("drop", (e => {
            e.preventDefault(), t.removeClass("highlight"), e.originalEvent.dataTransfer.files && e.originalEvent.dataTransfer.files[0] && (this.handleImageFile(e.originalEvent.dataTransfer.files[0]), 
            this.resetSearchUI());
        })), n.on("click", (() => {
            e.trigger("click");
        })), e.on("change", (t => {
            t.target.files && t.target.files[0] && (this.handleImageFile(t.target.files[0]), 
            this.resetSearchUI());
        })), $(document).on("paste", (async t => {
            const e = t.originalEvent.clipboardData.items;
            for (let a = 0; a < e.length; a++) if (-1 !== e[a].type.indexOf("image")) {
                const t = e[a].getAsFile();
                return this.handleImageFile(t), void this.resetSearchUI();
            }
            const n = t.originalEvent.clipboardData.getData("text");
            n && utils.isUrl(n) && (l.show(), c.val(n), i.attr("src", n), a.show(), this.resetSearchUI());
        })), r.on("click", (async () => {
            const t = i.attr("src");
            if (t) {
                if (!this.isUploading) {
                    this.isUploading = !0;
                    try {
                        const e = await this.searchByImage(t);
                        s.hide(), d.show(), g.empty();
                        const n = JSON.parse(localStorage.getItem("selectedSites") || "{}");
                        this.siteList.forEach((t => {
                            const a = t.url.replace("{占位符}", encodeURIComponent(e)), i = !1 !== n[t.name];
                            g.append(`\n                        <a href="${a}" class="search-img-site-btn" target="_blank" title="${t.name}">\n                        <input type="checkbox" \n                               class="site-checkbox" \n                               data-site-name="${t.name}" \n                               style="margin-right: 5px"\n                               ${i ? "checked" : ""}>\n                            <img src="${t.ico}" alt="${t.name}">\n                            <span>${t.name}</span>\n                        </a>\n                    `);
                        })), g.on("change", ".site-checkbox", (function() {
                            const t = $(this).data("site-name");
                            n[t] = $(this).is(":checked"), localStorage.setItem("selectedSites", JSON.stringify(n));
                        })), g.show();
                    } finally {
                        this.isUploading = !1;
                    }
                }
            } else show.info("请粘贴或上传图片");
        })), o.on("click", (() => {
            a.hide(), l.hide(), e.val(""), c.val("");
        })), c.on("change", (() => {
            utils.isUrl(c.val()) && (i.attr("src", c.val()), a.show());
        })), $("#openAll").on("click", (() => {
            $(".search-img-site-btn").each((function() {
                $(this).find(".site-checkbox").is(":checked") && window.open($(this).attr("href"));
            }));
        }));
    }
    resetSearchUI() {
        $("#action-btns").show(), $("#search-results").hide(), $("#search-img-site-btns-container").hide().empty();
    }
    handleImageFile(t) {
        const e = document.getElementById("preview-image"), n = document.getElementById("preview-area"), a = document.getElementById("url-input-container");
        if (!t.type.match("image.*")) return void show.info("请选择图片文件");
        const i = new FileReader;
        i.onload = t => {
            e.src = t.target.result, n.style.display = "block", a.style.display = "none", $("#handle-btn")[0].click();
        }, i.readAsDataURL(t);
    }
    async searchByImage(t) {
        let e = loading();
        try {
            let e = t;
            if (t.startsWith("data:")) {
                show.info("开始上传图片...");
                const n = await async function(t) {
                    var e;
                    const n = t.match(/^data:(.+);base64,(.+)$/);
                    if (!n || n.length < 3) throw new Error("无效的Base64图片数据");
                    const a = n[1], i = n[2], s = atob(i), r = new Array(s.length);
                    for (let p = 0; p < s.length; p++) r[p] = s.charCodeAt(p);
                    const o = new Uint8Array(r), l = new Blob([ o ], {
                        type: a
                    }), c = new FormData;
                    c.append("image", l);
                    const d = await fetch("https://api.imgur.com/3/image", {
                        method: "POST",
                        headers: {
                            Authorization: "Client-ID d70305e7c3ac5c6"
                        },
                        body: c
                    }), g = await d.json();
                    if (g.success && g.data && g.data.link) return g.data.link;
                    throw new Error((null == (e = g.data) ? void 0 : e.error) || "上传到Imgur失败");
                }(t);
                if (!n) return void show.error("上传到失败");
                e = n;
            }
            return e;
        } catch (n) {
            show.error(`搜索失败: ${n.message}`), console.error("搜索失败:", n);
        } finally {
            e.close();
        }
    }
}

class BusNavBarPlugin extends BasePlugin {
    handle() {
        $("#navbar > div > div > span").append('\n            <button class="btn btn-default" style="color: #0d9488" id="search-img-btn">识图</button>\n       '), 
        $("#search-img-btn").on("click", (() => {
            this.getBean("SearchByImagePlugin").open();
        }));
    }
}

class RelatedPlugin extends BasePlugin {
    constructor() {
        super(...arguments), o(this, "floorIndex", 1), o(this, "isInit", !1);
    }
    async showRelated(t) {
        const e = t || $("#magnets-content");
        let n = this.parseMovieId(window.location.href);
        e.append('\n            <div style="display: flex; align-items: center; margin: 16px 0; color: #666; font-size: 14px;">\n                <span style="flex: 1; height: 1px; background: linear-gradient(to right, transparent, #999, transparent);"></span>\n                <span style="padding: 0 10px;">相关清单</span>\n                <a id="relatedFold" style="margin-left: 8px; color: #1890ff; text-decoration: none; display: flex; align-items: center;">\n                    <span class="toggle-text">展开</span>\n                    <span class="toggle-icon" style="margin-left: 4px;">▼</span>\n                </a>\n                <span style="flex: 1; height: 1px; background: linear-gradient(to right, transparent, #999, transparent);"></span>\n            </div>\n        '), 
        $("#relatedFold").on("click", (t => {
            t.preventDefault(), t.stopPropagation();
            const e = $("#relatedFold .toggle-text"), a = $("#relatedFold .toggle-icon"), i = "展开" === e.text();
            e.text(i ? "折叠" : "展开"), a.text(i ? "▲" : "▼"), i ? ($("#relatedContainer").show(), 
            $("#relatedFooter").show(), this.isInit || (this.fetchAndDisplayRelateds(n), this.isInit = !0)) : ($("#relatedContainer").hide(), 
            $("#relatedFooter").hide());
        })), e.append('<div id="relatedContainer"></div>'), e.append('<div id="relatedFooter"></div>');
    }
    async fetchAndDisplayRelateds(t) {
        const e = $("#relatedContainer"), n = $("#relatedFooter");
        e.append('<div id="relatedLoading" style="margin-top:15px;background-color:#ffffff;padding:10px;margin-left: -10px;">获取清单中...</div>');
        let a = null;
        try {
            a = await q(t, 1, 20);
        } catch (i) {
            console.error("获取清单失败:", i);
        } finally {
            $("#relatedLoading").remove();
        }
        if (!a) return e.append('\n                <div style="margin-top:15px;background-color:#ffffff;padding:10px;margin-left: -10px;">\n                    获取清单失败\n                    <a id="retryFetchRelateds" href="javascript:;" style="margin-left: 10px; color: #1890ff; text-decoration: none;">重试</a>\n                </div>\n            '), 
        void $("#retryFetchRelateds").on("click", (async () => {
            $("#retryFetchRelateds").parent().remove(), await this.fetchAndDisplayRelateds(t);
        }));
        if (0 !== a.length) if (this.displayRelateds(a, e), 20 === a.length) {
            n.html('\n                <button id="loadMoreRelateds" style="width:100%; background-color: #e1f5fe; border:none; padding:10px; margin-top:10px; cursor:pointer; color:#0277bd; font-weight:bold; border-radius:4px;">\n                    加载更多清单\n                </button>\n                <div id="relatedEnd" style="display:none; text-align:center; padding:10px; color:#666; margin-top:10px;">已加载全部清单</div>\n            ');
            let a = 1, s = $("#loadMoreRelateds");
            s.on("click", (async () => {
                let n;
                s.text("加载中...").prop("disabled", !0), a++;
                try {
                    n = await q(t, a, 20);
                } catch (i) {
                    console.error("加载更多清单失败:", i);
                } finally {
                    s.text("加载失败, 请点击重试").prop("disabled", !1);
                }
                n && (this.displayRelateds(n, e), n.length < 20 ? (s.remove(), $("#relatedEnd").show()) : s.text("加载更多清单").prop("disabled", !1));
            }));
        } else n.html('<div style="text-align:center; padding:10px; color:#666; margin-top:10px;">已加载全部清单</div>'); else e.append('<div style="margin-top:15px;background-color:#ffffff;padding:10px;margin-left: -10px;">无清单</div>');
    }
    displayRelateds(t, e) {
        t.length && t.forEach((t => {
            let n = `\n                <div class="item columns is-desktop" style="display:block;margin-top:6px;background-color:#ffffff;padding:10px;margin-left: -10px;word-break: break-word;position:relative;">\n                   <span style="position:absolute;top:5px;right:10px;color:#999;font-size:12px;">#${this.floorIndex++}</span>\n                   <span style="position:absolute;bottom:5px;right:10px;color:#999;font-size:12px;">创建时间: ${t.createTime}</span>\n                   <p><a href="/lists/${t.relatedId}" target="_blank" style="color:#2e8abb">${t.name}</a></p>\n                   <p style="margin-top: 5px;">视频个数: ${t.movieCount}</p>\n                   <p style="margin-top: 5px;">收藏次数: ${t.collectionCount} 被查看次数: ${t.viewCount}</p>\n                </div>\n            `;
            e.append(n);
        }));
    }
}

class WantAndWatchedVideosPlugin extends BasePlugin {
    constructor() {
        super(...arguments), o(this, "type", null);
    }
    async handle() {
        window.location.href.includes("/want_watch_videos") && ($("h3").append('<a class="a-primary" id="wantWatchBtn" style="padding:10px;">导入至 JHS</a>'), 
        $("#wantWatchBtn").on("click", (t => {
            this.type = m, this.importWantWatchVideos(t, "是否将 想看的影片 导入到 JHS-收藏?");
        }))), window.location.href.includes("/watched_videos") && ($("h3").append('<a class="a-success" id="wantWatchBtn" style="padding:10px;">导入至 JHS</a>'), 
        $("#wantWatchBtn").on("click", (t => {
            this.type = f, this.importWantWatchVideos(t, "是否将 看过的影片 导入到 JHS-已下载?");
        })));
    }
    importWantWatchVideos(t, e) {
        utils.q(null, `${e} <br/> <span style='color: #f40'>执行此功能前请记得备份数据</span>`, (async () => {
            let t = loading();
            try {
                await this.parseMovieList();
            } catch (e) {
                console.error(e);
            } finally {
                t.close();
            }
        }));
    }
    async parseMovieList(t) {
        let e, n;
        t ? (e = t.find(this.getSelector().itemSelector), n = t.find(".pagination-next").attr("href")) : (e = $(this.getSelector().itemSelector), 
        n = $(".pagination-next").attr("href"));
        for (const i of e) {
            const t = $(i), e = t.find("a").attr("href"), n = t.find(".video-title strong").text().trim();
            if (e && n) try {
                if (await storageManager.getCar(n)) {
                    show.info(`${n} 已存在, 跳过`);
                    continue;
                }
                await storageManager.saveCar(n, e, "", this.type);
            } catch (a) {
                console.error(`保存失败 [${n}]:`, a);
            }
        }
        n ? (show.info("发现下一页,正在解析:", n), await new Promise((t => setTimeout(t, 1e3))), 
        $.ajax({
            url: n,
            method: "GET",
            success: t => {
                const e = new DOMParser, n = $(e.parseFromString(t, "text/html"));
                this.parseMovieList(n);
            },
            error: function(t) {
                console.error(t), show.error("加载下一页失败:" + t.message);
            }
        })) : (show.ok("导入结束!"), window.refresh());
    }
}

class SeHuaTangPlugin extends BasePlugin {
    constructor() {
        super(...arguments), o(this, "currentImageIndex", 0), o(this, "currentImageGroup", []), 
        o(this, "processedArticles", new Set);
    }
    async initCss() {
        return "\n            <style>\n                /*.icn{\n                    width: 85px !important;\n                }*/\n                .xst{\n                    font-size: 15px;\n                    color: #090909;\n                }\n                #threadlisttableid em{\n                    font-size: 15px;\n                }\n            </style>\n        ";
    }
    async handle() {
        let t = $(".enter-btn");
        t.length > 0 && t[0].click(), window.location.href.includes("viewthread") || (this.parseArticleImg().then(), 
        this.checkDom(), this.bindClick(), this.handleImg());
    }
    getInfo(t) {
        let e, n = t.find("a.xst"), a = n.text().trim(), i = n.attr("href");
        return e = i.includes("tid=") ? i.match(/tid=(\d+)/)[1] : i.split("-")[1], {
            articleId: e,
            url: i,
            title: a
        };
    }
    bindClick() {
        $("#threadlisttableid").on("click", ".block-btn", (async () => {
            let t = $(event.target).closest("tr");
            const {articleId: e, url: n, title: a} = this.getInfo(t);
            await seHuaTangStorageManager.saveArticle(e, n, a, u), show.error("屏蔽成功!"), this.doFilter().then();
        })).on("click", ".fav-btn", (async t => {
            let e = $(t.target).closest("tr");
            const {articleId: n, url: a, title: i} = this.getInfo(e);
            await seHuaTangStorageManager.saveArticle(n, a, i, m), show.ok("收藏成功!"), this.doFilter().then();
        }));
    }
    async doFilter() {
        const t = await seHuaTangStorageManager.getArticleList();
        $('.icn a[title="新窗口打开"], .icn a[title="有新回复 - 新窗口打开"]').toArray().forEach((e => {
            $(e).hide();
            let n = $(e).parent();
            n.find(".fav-btn").length || n.prepend('\n                    <a class="block-btn" style="color: #d99c1c; cursor: pointer; display: inline-block;min-width: 37px;">屏蔽</a>\n                    <a class="fav-btn" style="color: #1cd925; cursor: pointer; display: inline-block;min-width: 37px;">收藏</a>\n                ');
            let a = n.parent();
            const {articleId: i, url: s, title: r} = this.getInfo(a), o = t.find((t => t.articleId === i));
            o && o.status === m && a.find(".common em a").text("已收藏").css("color", "#14e097"), 
            o && o.status === u && (a.find(".common em a").text("已屏蔽").css("color", "#c72222"), 
            a.parent().hide());
        }));
    }
    checkDom() {
        const t = document.querySelector("#threadlisttableid"), e = new MutationObserver((async a => {
            e.disconnect();
            try {
                await this.doFilter(), this.parseArticleImg().then();
            } finally {
                e.observe(t, n);
            }
        })), n = {
            childList: !0,
            subtree: !1
        };
        e.observe(t, n);
    }
    async parseArticleImg() {
        $(".s.xst").each((async (t, e) => {
            const n = $(e).attr("href");
            if (!this.processedArticles.has(n)) {
                this.processedArticles.add(n);
                try {
                    const t = $(e).closest("tbody");
                    if (t.find(".imageBox").length) return;
                    if (!t.is(":visible")) return;
                    const a = await fetch(n);
                    if (!a.ok) return;
                    const i = $($.parseHTML(await a.text())).find("img.zoom[file]:not([file*='static'], [file*='hrline'])").slice(0, 5);
                    if (!i.length) return;
                    const s = i.map(((t, e) => `<img src="${$(e).attr("file")}" style="width:300px;height:auto;max-width:300px;max-height:300px;object-fit:contain" onclick="zoom(this,this.src,0,0,0)" alt="">`)).get().join("");
                    t.append(`\n                    <tr class="imageBox">\n                        <td colspan="5">\n                            <div style="display:flex;gap:10px;overflow-x:auto;padding:5px 0">${s}</div>\n                        </td>\n                    </tr>\n            `);
                } catch (a) {
                    console.error("Error:", n, a);
                }
            }
        }));
    }
    handleImg() {
        document.addEventListener("click", (t => {
            if ("IMG" === t.target.tagName && t.target.closest(".imageBox")) {
                const e = t.target.closest(".imageBox");
                this.currentImageGroup = Array.from(e.querySelectorAll("img")), this.currentImageIndex = this.currentImageGroup.indexOf(t.target), 
                this.createNavigateBtn();
            }
        }));
    }
    navigateImage(t) {
        this.currentImageIndex = (this.currentImageIndex + t + this.currentImageGroup.length) % this.currentImageGroup.length;
        const e = this.currentImageGroup[this.currentImageIndex];
        zoom(e, e.src, 0, 0, 0), this.createNavigateBtn();
    }
    createNavigateBtn() {
        utils.loopDetector((() => $("#imgzoom_picpage").length > 0), (() => {
            if (0 === $("#imgzoom_picpage").length) return;
            const t = document.getElementById("imgzoom_picpage");
            if (console.log("zoomContainer", t), !t) return;
            t.querySelectorAll("#zimg_prev, #zimg_next").forEach((t => t.remove()));
            const e = document.createElement("div");
            e.id = "zimg_prev", e.className = "zimg_prev", e.onclick = () => this.navigateImage(-1);
            const n = document.createElement("div");
            n.id = "zimg_next", n.className = "zimg_next", n.onclick = () => this.navigateImage(1), 
            t.append(e, n);
        }));
    }
}

class CopyTitleOrDownImgPlugin extends BasePlugin {
    constructor() {
        super(...arguments), o(this, "moreSvg", '<svg t="1749017229420" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9184" width="200" height="200"><path d="M512 74.666667C270.933333 74.666667 74.666667 270.933333 74.666667 512S270.933333 949.333333 512 949.333333 949.333333 753.066667 949.333333 512 753.066667 74.666667 512 74.666667z m0 810.666666c-204.8 0-373.333333-168.533333-373.333333-373.333333S307.2 138.666667 512 138.666667 885.333333 307.2 885.333333 512 716.8 885.333333 512 885.333333z" fill="#666666" p-id="9185"></path><path d="M512 512m-42.666667 0a42.666667 42.666667 0 1 0 85.333334 0 42.666667 42.666667 0 1 0-85.333334 0Z" fill="#666666" p-id="9186"></path><path d="M341.333333 512m-42.666666 0a42.666667 42.666667 0 1 0 85.333333 0 42.666667 42.666667 0 1 0-85.333333 0Z" fill="#666666" p-id="9187"></path><path d="M682.666667 512m-42.666667 0a42.666667 42.666667 0 1 0 85.333333 0 42.666667 42.666667 0 1 0-85.333333 0Z" fill="#666666" p-id="9188"></path></svg>'), 
        o(this, "titleSvg", '<svg t="1747553289744" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7507" width="200" height="200"><path d="M959.8 150.8c0-2.3-1.9-4.2-4.2-4.2H253.3c-2.3 0-4.2 1.9-4.2 4.2v115.9c0 2.3 1.9 4.2 4.2 4.2h702.3c2.3 0 4.2-1.9 4.2-4.2V150.8z" fill="" p-id="7508"></path><path d="M126.4 208.8m-62.2 0a62.2 62.2 0 1 0 124.4 0 62.2 62.2 0 1 0-124.4 0Z" fill="" p-id="7509"></path><path d="M851.5 453.7c0-2.1-1.8-3.9-3.9-3.9H252.9c-2.1 0-3.9 1.7-3.9 3.9v116.6c0 2.1 1.7 3.9 3.9 3.9h594.7c2.1 0 3.9-1.7 3.9-3.9V453.7z" fill="" p-id="7510"></path><path d="M126.4 512m-62.2 0a62.2 62.2 0 1 0 124.4 0 62.2 62.2 0 1 0-124.4 0Z" fill="" p-id="7511"></path><path d="M851.5 756.9c0-2.1-1.8-3.9-3.9-3.9H252.9c-2.1 0-3.9 1.8-3.9 3.9v116.6c0 2.1 1.7 3.9 3.9 3.9h594.7c2.1 0 3.9-1.7 3.9-3.9V756.9z" fill="" p-id="7512"></path><path d="M126.4 815.2m-62.2 0a62.2 62.2 0 1 0 124.4 0 62.2 62.2 0 1 0-124.4 0Z" fill="" p-id="7513"></path></svg>'), 
        o(this, "carNumSvg", '<svg t="1747552574854" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3539" width="200" height="200"><path d="M920.337035 447.804932c-6.067182-6.067182-10.918677-11.643178-16.985859-17.71036l48.536436-30.334889-42.469254-109.207238-121.340579 12.134365c-6.067182-6.067182-6.067182-12.134365-12.134365-18.201547-12.134365-12.134365-18.201547-24.267706-24.267706-30.334889-24.26873-36.402071-30.334889-42.469254-54.603619-42.469254H339.116511c-18.201547 0-24.267706 6.067182-54.603619 42.469254-6.067182 6.067182-12.134365 18.201547-24.267706 30.334889 0 0-6.067182 6.067182-12.134365 18.201547l-115.27442-12.134365-48.536436 109.207238 51.090608 24.378223c-6.067182 6.067182-30.334889 34.660404-30.334889 34.660405l-15.542998 22.280446-12.282744 17.018605c-6.067182 12.134365-5.064342 10.868535-5.064342 29.070082v224.480635c0 36.402071 18.201547 60.670801 54.603618 60.670801h115.273397c36.402071 0 54.603619-24.267706 54.603619-54.603619v-18.201547h424.693562v18.201547c0 30.334889 18.201547 54.603619 54.603618 54.603619h115.273397c36.402071 0 60.670801-24.267706 60.670801-60.670801V539.300786c0-42.469254 0.685615-46.662763-11.44875-64.863287-4.731768-6.744611-11.94403-16.196891-20.101827-26.632567z m-35.186383-78.381161l-30.334889 18.201547-12.134365-12.134365c-6.067182-8.899694-12.134365-12.134365-12.134365-18.201547l42.469254-6.067183 12.134365 18.201548z m-533.899776-97.072873h339.755054l78.871325 103.140055H272.378527l78.872349-103.140055zM175.305655 357.290429h36.402071c-6.067182 6.067182-6.067182 12.134365-12.134365 18.201547l-18.201547 6.067183-18.201547-12.134365 12.135388-12.134365z m667.375743 394.35765h-54.603619V678.843936H242.043638v72.804143H132.837424V527.167444c0-12.134365-0.041956-20.662599 1.216711-23.556508 1.258667-2.89391 9.955746-16.924461 21.193695-29.173437l35.722596-38.276768h639.576607l21.917172 20.938891c6.067182 6.067182 21.847587 21.366633 25.712615 28.732392 7.621585 9.996678 6.973832 10.999518 13.041014 23.133883v242.682182h-48.536436zM242.043638 533.234627h133.474944v60.670801H242.043638v-60.670801z m412.559197 0h133.474944v60.670801H654.602835v-60.670801z" p-id="3540"></path></svg>'), 
        o(this, "downSvg", '<svg t="1747552626242" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4551" width="200" height="200"><path d="M641.6 660l-8.64-64 32-4.32a211.2 211.2 0 0 0-26.72-420.32 215.36 215.36 0 0 0-213.12 192 94.56 94.56 0 0 0 0 11.52v41.28h-64V384v-7.04a153.12 153.12 0 0 1 0-19.52A279.84 279.84 0 0 1 636.16 108H640A275.2 275.2 0 0 1 673.28 656z" fill="#333333" p-id="4552"></path><path d="M490.4 446.24l-7.52-39.84a182.4 182.4 0 0 1 107.52-162.88l29.12-13.28L646.08 288l-29.12 13.28a117.92 117.92 0 0 0-70.08 101.28l6.24 30.4zM392.96 652.32h-78.72A202.24 202.24 0 0 1 256 256l30.72-9.12 18.24 61.28-30.72 9.12a138.24 138.24 0 0 0 39.68 270.72h78.72zM479.2 512h64v320h-64z" fill="#333333" p-id="4553"></path><path d="M510.4 908l-156.32-147.68 43.84-46.4 112.48 106.08 112.8-106.08 43.84 46.56-156.64 147.52z" fill="#333333" p-id="4554"></path></svg>'), 
        o(this, "handleSvg", '<svg t="1749106236917" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2628" width="200" height="200"><path d="M838 989.48a32 32 0 0 1-22.5-9.22L519.3 687.6 207.48 980.8a32 32 0 0 1-54-23.32V136.52A98.54 98.54 0 0 1 252 38.1h519.6A98.52 98.52 0 0 1 870 136.52v820.96a32 32 0 0 1-32 32zM252 102.1a34.46 34.46 0 0 0-34.42 34.42v746.96L498 619.84a32 32 0 0 1 44.42 0.56L806 880.88V136.52a34.46 34.46 0 0 0-34.4-34.42z" p-id="2629"></path><path d="M648 604.92a28 28 0 0 1-16.46-5.34l-112.84-82-112.84 82a28 28 0 0 1-43.08-31.32l43.1-132.64-112.84-82a28 28 0 0 1 16.46-50.66h139.48L492 170.34a28 28 0 0 1 53.26 0l43.1 132.64h139.48a28 28 0 0 1 16.46 50.66l-112.84 82 43.1 132.64A28 28 0 0 1 648 604.92z m-129.3-150a27.86 27.86 0 0 1 16.46 5.36l59.58 43.28-22.76-70a28 28 0 0 1 10.02-31.28l59.58-43.3H568a28 28 0 0 1-26.64-19.34l-22.76-70-22.76 70a28 28 0 0 1-26.62 19.34h-73.64l59.58 43.3a28 28 0 0 1 10.16 31.3l-22.76 70 59.58-43.28a28 28 0 0 1 16.46-5.32z" p-id="2630"></path></svg>'), 
        o(this, "siteSvg", '<svg t="1749107903569" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="12439" width="200" height="200"><path d="M882.758621 133.674884C882.758621 59.84828 822.91034 0 749.083736 0 675.25715 0 615.40887 59.84828 615.40887 133.674884 615.40887 163.358402 625.152318 191.656395 642.813352 214.773283L670.872117 193.336726 648.314739 166.170836 253.911693 493.666092 276.469054 520.831982 302.371681 496.834595C277.256669 469.725608 241.995388 453.990153 204.295574 453.990153 130.46897 453.990153 70.62069 513.838433 70.62069 587.66502 70.62069 661.491624 130.46897 721.339904 204.295574 721.339904 255.555319 721.339904 301.619094 692.208675 324.036714 647.136344L276.646223 663.002394 706.082022 877.440106 721.856794 845.849335 690.37312 829.861888C680.932829 848.452414 675.940882 869.068818 675.940882 890.325116 675.940882 964.15172 735.789162 1024 809.615766 1024 883.442353 1024 943.290633 964.15172 943.290633 890.325116 943.290633 874.050807 940.36533 858.125365 934.723584 843.16446L868.645076 868.0826C871.294817 875.109252 872.669943 882.595452 872.669943 890.325116 872.669943 925.14899 844.439623 953.37931 809.615766 953.37931 774.791892 953.37931 746.561571 925.14899 746.561571 890.325116 746.561571 880.245089 748.902894 870.575616 753.340487 861.836782L769.436089 830.140063 737.631567 814.258564 308.195769 599.820853 276.554929 584.02108 260.805279 615.686903C250.212352 636.984797 228.494795 650.719214 204.295574 650.719214 169.4717 650.719214 141.241379 622.488894 141.241379 587.66502 141.241379 552.841163 169.4717 524.610842 204.295574 524.610842 222.12269 524.610842 238.680594 531.99985 250.566444 544.829369L273.29589 569.363385 299.026432 547.997855 693.429478 220.502616 719.514606 198.84265 698.930882 171.900169C690.596687 160.991373 686.029559 147.727007 686.029559 133.674884 686.029559 98.85101 714.25988 70.62069 749.083736 70.62069 783.90761 70.62069 812.137931 98.85101 812.137931 133.674884 812.137931 148.208022 807.249885 161.899255 798.379608 172.996785L853.543883 217.089695C872.331935 193.584128 882.758621 164.379366 882.758621 133.674884ZM749.083736 196.729062C729.149334 196.729062 710.818745 187.460449 698.930882 171.900169L642.813352 214.773283C667.922573 247.639305 706.904064 267.349751 749.083736 267.349751 790.225902 267.349751 828.357809 248.599782 853.543883 217.089695L798.379608 172.996785C786.455411 187.915034 768.530291 196.729062 749.083736 196.729062ZM337.970441 587.66502C337.970441 553.551854 325.093782 521.360666 302.371681 496.834595L250.566444 544.829369C261.309069 556.424898 267.349751 571.526356 267.349751 587.66502 267.349751 597.565263 265.091478 607.069184 260.805279 615.686903L324.036714 647.136344C333.156105 628.801148 337.970441 608.540036 337.970441 587.66502ZM809.615766 756.650249C758.753986 756.650249 712.986006 785.330865 690.37312 829.861888L753.340487 861.836782C764.027215 840.791658 785.603302 827.270938 809.615766 827.270938 836.08553 827.270938 859.461862 843.730308 868.645076 868.0826L934.723584 843.16446C915.252259 791.529949 865.714547 756.650249 809.615766 756.650249Z" fill="#389BFF" p-id="12440"></path></svg>'), 
        o(this, "videoSvg", '<svg t="1749003664455" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1952" width="200" height="200"><path d="M825.6 153.6H198.4C124.5 153.6 64 214.1 64 288v448c0 73.9 60.5 134.4 134.4 134.4h627.2c73.9 0 134.4-60.5 134.4-134.4V288c0-73.9-60.5-134.4-134.4-134.4z m-138.2 44.8l112 112H706l-112-112h93.4z m-156.8 0l112 112H526.7l-112-112h115.9z m-179.2 0l112 112H347.5l-112-112h115.9zM108.8 288c0-41.4 28.4-76.1 66.7-86.3l108.7 108.7H108.8V288z m806.4 448c0 49.4-40.2 89.6-89.6 89.6H198.4c-49.4 0-89.6-40.2-89.6-89.6V355.2h806.4V736z m0-425.6h-52.5l-112-112h74.9c49.4 0 89.6 40.2 89.6 89.6v22.4z" p-id="1953"></path><path d="M454 687.2l149.3-77.6c27.5-13.8 27.5-53 0-66.8L468 472.2c-31.2-15.6-68 7.1-68 42v139.6c0 27.8 29.2 45.8 54 33.4zM444.8 512l134.4 67.2-134.4 67.2V512z" p-id="1954"></path></svg>'), 
        o(this, "recoveryVideoSvg", '<svg t="1749003779161" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8204" width="200" height="200"><path d="M938.666667 553.92V768c0 64.8-52.533333 117.333333-117.333334 117.333333H202.666667c-64.8 0-117.333333-52.533333-117.333334-117.333333V256c0-64.8 52.533333-117.333333 117.333334-117.333333h618.666666c64.8 0 117.333333 52.533333 117.333334 117.333333v297.92z m-64-74.624V256a53.333333 53.333333 0 0 0-53.333334-53.333333H202.666667a53.333333 53.333333 0 0 0-53.333334 53.333333v344.48A290.090667 290.090667 0 0 1 192 597.333333a286.88 286.88 0 0 1 183.296 65.845334C427.029333 528.384 556.906667 437.333333 704 437.333333c65.706667 0 126.997333 16.778667 170.666667 41.962667z m0 82.24c-5.333333-8.32-21.130667-21.653333-43.648-32.917333C796.768 511.488 753.045333 501.333333 704 501.333333c-121.770667 0-229.130667 76.266667-270.432 188.693334-2.730667 7.445333-7.402667 20.32-13.994667 38.581333-7.68 21.301333-34.453333 28.106667-51.370666 13.056-16.437333-14.634667-28.554667-25.066667-36.138667-31.146667A222.890667 222.890667 0 0 0 192 661.333333c-14.464 0-28.725333 1.365333-42.666667 4.053334V768a53.333333 53.333333 0 0 0 53.333334 53.333333h618.666666a53.333333 53.333333 0 0 0 53.333334-53.333333V561.525333zM320 480a96 96 0 1 1 0-192 96 96 0 0 1 0 192z m0-64a32 32 0 1 0 0-64 32 32 0 0 0 0 64z" fill="#000000" p-id="8205"></path></svg>');
    }
    async initCss() {
        return `\n            <style>\n                .box .tags {\n                    justify-content: space-between;\n                }\n                .tool-box span{\n                    opacity:.3\n                }\n                .tool-box span:hover{\n                    opacity:1\n                }\n                ${p ? ".tool-box .icon{ height: 24px; width: 24px; }" : ""}\n                .tool-box svg path {\n                  fill: blue;\n                }\n                [data-theme="dark"] .tool-box svg path {\n                  fill: white;\n                }\n                \n                \n                /* 鼠标移入时的弹性动画 */\n                .elastic-in {\n                    animation: elasticIn 0.2s ease-out forwards;  /* 动画名称 | 时长 | 缓动函数 | 保持最终状态 */\n                }\n                \n                /* 鼠标移出时的弹性动画 */\n                .elastic-out {\n                    animation: elasticOut 0.2s ease-in forwards;\n                }\n                /* 弹性进入动画(像果冻弹入) */\n                @keyframes elasticIn {\n                    0% {\n                        opacity: 0;\n                        transform: scale(0.8);  /* 起始状态:80% 大小 */\n                    }\n                    50% {\n                        opacity: 1;\n                        transform: scale(1.1);  /* 弹到 110%(超调一点) */\n                    }\n                    70% {\n                        transform: scale(0.95); /* 回弹到 95%(模拟弹性阻尼) */\n                    }\n                    100% {\n                        opacity: 1;\n                        transform: scale(1);    /* 最终恢复正常大小 */\n                    }\n                }\n                /* 弹性离开动画(像果冻弹出) */\n                @keyframes elasticOut {\n                    0% {\n                        opacity: 1;\n                        transform: scale(1);    /* 起始状态:正常大小 */\n                    }\n                    30% {\n                        transform: scale(1.05); /* 先弹大一点(105%) */\n                    }\n                    100% {\n                        opacity: 0;\n                        transform: scale(0.8);  /* 最终缩小并消失 */\n                    }\n                }\n                \n                \n                .loading {\n                    opacity: 0.7;\n                    filter: blur(1px);\n                }\n                .loading-spinner {\n                    position: absolute;\n                    top: 50%;\n                    left: 50%;\n                    transform: translate(-50%, -50%);\n                    width: 40px;\n                    height: 40px;\n                    border: 3px solid rgba(255,255,255,.3);\n                    border-radius: 50%;\n                    border-top-color: #fff;\n                    animation: spin 1s ease-in-out infinite;\n                    z-index: 20;\n                }\n                @keyframes spin {\n                    to { transform: translate(-50%, -50%) rotate(360deg); }\n                }\n            </style>\n        `;
    }
    handle() {
        window.isListPage && (this.addSvgBtn(), this.bindClick().then());
    }
    addSvgBtn() {
        $(this.getSelector().itemSelector).toArray().forEach((t => {
            let e = $(t);
            if (!(e.find(".tool-box").length > 0) && (g && e.find(".tags").append(`\n                    <div class="tool-box" style="margin-left: auto; display: flex; align-items: center">\n                        <span class="videoSvg" title="播放视频" style="margin-right: 15px;">${this.videoSvg}</span>\n                        \n                        <div class="more-tools-container" style="position: relative; margin-right: 15px;">\n                            <div title="鉴定处理" style="padding: 5px; margin: -5px;opacity:.3">${this.handleSvg}</div>\n                            \n                            <div class="more-tools" style=" position: absolute; bottom: 33px; right: -30px; display: none;\n                                background-color: rgba(255, 255, 255, 0);z-index: 10;">\n                                <a class="menu-btn hasWatchBtn" style="background-color:${B};color:white;margin-bottom: 5px"><span style="opacity: 1;">${P}</span></a>\n                                <a class="menu-btn hasDownBtn" style="background-color:${S}; color:white;margin-bottom: 5px"><span style="opacity: 1;">${C}</span></a>\n                                <a class="menu-btn favoriteBtn" style="background-color:${_}; color:white;margin-bottom: 5px"><span style="opacity: 1;">${x}</span></a>\n                                <a class="menu-btn filterBtn" style="background-color:${y};   color:white;margin-bottom: 5px"><span style="opacity: 1;">${w}</span></a>\n                            </div>\n                        </div>\n                        \n                        <div class="more-tools-container" style="position: relative; margin-right: 15px;">\n                            <div title="第三方网站" style="padding: 5px; margin: -5px;opacity:.3">${this.siteSvg}</div>\n                            \n                             <div class="more-tools" style=" position: absolute; bottom: 33px; right: -30px; display: none;\n                                background-color: rgba(255, 255, 255, 0);z-index: 10;">\n                                <a class="site-btn site-jable" style="color:white;margin-bottom: 5px;background-color:#71bb59;">\n                                    <span style="opacity: 1;">Jable</span>\n                                </a>\n                                <a class="site-btn site-avgle" style="margin-bottom: 5px;background-color:#71bb59;">\n                                    <span style="opacity: 1;">Avgle</span>\n                                </a>\n                                <a class="site-btn site-miss-av" style="color:white;margin-bottom: 5px;background-color:#71bb59;">\n                                    <span style="opacity: 1;">MissAv</span>\n                                </a>\n                            </div>\n                        </div>\n                        \n                        <div class="more-tools-container" style="position: relative; margin-right: 15px;">\n                            <div title="复制按钮" style="padding: 5px; margin: -5px;opacity:.3">${this.moreSvg}</div>\n                            \n                            <div class="more-tools" style="\n                                position: absolute;\n                                bottom: 20px;\n                                right: -10px;\n                                display: none;\n                                background: white;\n                                box-shadow: 0 2px 8px rgba(0,0,0,0.15);\n                                border-radius: 20px;\n                                padding: 10px 0;\n                                margin-bottom: 15px;\n                                z-index: 10;\n                            ">\n                                <span class="carNumSvg" title="复制番号" style="padding: 5px 10px; white-space: nowrap;">${this.carNumSvg}</span>\n                                <span class="titleSvg" title="复制标题" style="padding: 5px 10px; white-space: nowrap;">${this.titleSvg}</span>\n                                <span class="downSvg" title="下载封面" style="padding: 5px 10px; white-space: nowrap;">${this.downSvg}</span>\n                            </div>\n                        </div>\n                    </div>\n                `), 
            p)) {
                if (e.find(".avatar-box").length > 0) return;
                e.find(".photo-info").append(`\n                    <div class="tool-box" style="display: flex; align-items: center;justify-content: flex-end">\n                        <span class="videoSvg" title="播放视频" style="margin-right: 15px;">${this.videoSvg}</span>\n                        \n                        <div class="more-tools-container" style="position: relative; margin-right: 15px;">\n                            <div title="鉴定处理" style="padding: 5px; margin: -5px;opacity:.3">${this.handleSvg}</div>\n                            \n                            <div class="more-tools" style=" position: absolute; bottom: 33px; right: -30px; display: none;\n                                background-color: rgba(255, 255, 255, 0);z-index: 10;">\n                                <a class="menu-btn hasWatchBtn" style="background-color:${B};color:white;margin-bottom: 5px"><span style="opacity: 1;display: inline">${P}</span></a>\n                                <a class="menu-btn hasDownBtn" style="background-color:${S}; color:white;margin-bottom: 5px"><span style="opacity: 1;display: inline">${C}</span></a>\n                                <a class="menu-btn favoriteBtn" style="background-color:${_}; color:white;margin-bottom: 5px"><span style="opacity: 1;display: inline">${x}</span></a>\n                                <a class="menu-btn filterBtn" style="background-color:${y};   color:white;margin-bottom: 5px"><span style="opacity: 1;display: inline">${w}</span></a>\n                            </div>\n                        </div>\n                        \n                        <div class="more-tools-container" style="position: relative; margin-right: 15px;">\n                            <div title="第三方网站" style="padding: 5px; margin: -5px;opacity:.3">${this.siteSvg}</div>\n                            \n                             <div class="more-tools" style=" position: absolute; bottom: 33px; right: -30px; display: none;\n                                background-color: rgba(255, 255, 255, 0);z-index: 10;">\n                                <a class="site-btn site-jable" style="color:white;margin-bottom: 5px;background-color:#71bb59;">\n                                    <span style="opacity: 1;display: inline">Jable</span>\n                                </a>\n                                <a class="site-btn site-avgle" style="margin-bottom: 5px;background-color:#71bb59;">\n                                    <span style="opacity: 1;display: inline">Avgle</span>\n                                </a>\n                                <a class="site-btn site-miss-av" style="color:white;margin-bottom: 5px;background-color:#71bb59;">\n                                    <span style="opacity: 1;display: inline">MissAv</span>\n                                </a>\n                            </div>\n                        </div>\n                      \n                        <div class="more-tools-container" style="position: relative;">\n                            <div title="复制按钮" style="padding: 5px; margin: -5px;opacity:.3">${this.moreSvg}</div>\n                            \n                            <div class="more-tools" style="\n                                max-width: 44px;\n                                position: absolute;\n                                bottom: 20px;\n                                right: -10px;\n                                display: none;\n                                background: white;\n                                box-shadow: 0 2px 8px rgba(0,0,0,0.15);\n                                border-radius: 20px;\n                                padding: 10px 0;\n                                margin-bottom: 15px;\n                                z-index: 10;\n                                text-align: center;\n                            ">\n                                <span class="carNumSvg" title="复制番号" style="padding: 5px 10px; white-space: nowrap;display: inline">${this.carNumSvg}</span>\n                                <span class="titleSvg" title="复制标题"  style="padding: 5px 10px; white-space: nowrap;display: inline">${this.titleSvg}</span>\n                                <span class="downSvg" title="下载封面"   style="padding: 5px 10px; white-space: nowrap;display: inline">${this.downSvg}</span>\n                            </div>\n                        </div>\n                    </div>\n                `);
            }
        }));
    }
    async bindClick() {
        const t = this.getSelector(), e = this.getBean("ListPagePlugin");
        $(document).on("click", ".more-tools-container", (t => {
            t.preventDefault();
            var e = $(t.target).closest(".more-tools-container").find(".more-tools");
            $(".more-tools").not(e).stop(!0, !0).removeClass("elastic-in").addClass("elastic-out").hide(), 
            e.is(":visible") ? e.stop(!0, !0).removeClass("elastic-in").addClass("elastic-out").hide() : e.stop(!0, !0).removeClass("elastic-out").addClass("elastic-in").show();
        })), $(document).on("click", (function(t) {
            $(t.target).closest(".more-tools-container").length || $(".more-tools").stop(!0, !0).removeClass("elastic-in").addClass("elastic-out").hide();
        })), $(document).on("click", ".videoSvg", (n => {
            n.preventDefault(), $('.videoSvg[title!="播放视频"]').each(((n, a) => {
                const i = $(a);
                let s = i.closest(".item"), r = s.find(t.coverImgSelector), {carNum: o} = e.findCarNumAndHref(s);
                this.showImg(i, r, o), i.html(this.videoSvg).attr("title", "播放视频");
            }));
            const a = $(n.target).closest(".item"), i = a.find(".videoSvg");
            if ("播放视频" === i.attr("title")) {
                i.html(this.recoveryVideoSvg).attr("title", "切回封面");
                const {carNum: n} = e.findCarNumAndHref(a);
                let s = a.find(t.coverImgSelector);
                s.length || show.error("没有找到图片"), this.showVideo(i, s, n).then();
            }
        })), $(document).on("click", ".filterBtn, .favoriteBtn, .hasDownBtn, .hasWatchBtn", (t => {
            t.preventDefault(), t.stopPropagation();
            const n = $(t.target).closest(".menu-btn"), a = n.closest(".item"), {carNum: i, aHref: s, title: r} = e.findCarNumAndHref(a), o = async t => {
                await storageManager.saveCar(i, s, null, t), window.refresh();
            };
            n.hasClass("filterBtn") ? utils.q(t, `是否屏蔽${i}?`, (() => o(u))) : n.hasClass("favoriteBtn") ? o(m).then() : n.hasClass("hasDownBtn") ? o(f).then() : n.hasClass("hasWatchBtn") && o(v).then();
        }));
        const n = this.getBean("OtherSitePlugin"), a = await n.getMissAvUrl(), i = await n.getjableUrl(), s = await n.getAvgleUrl();
        $(document).on("click", ".site-jable, .site-avgle, .site-miss-av", (t => {
            t.preventDefault(), t.stopPropagation();
            const n = $(t.currentTarget), r = n.closest(".item"), {carNum: o, aHref: l, title: c} = e.findCarNumAndHref(r);
            n.hasClass("site-jable") ? window.open(`${i}/videos/${o}/`, "_blank") : n.hasClass("site-avgle") ? window.open(`${s}/vod/search.html?wd=${o}`, "_blank") : n.hasClass("site-miss-av") && window.open(`${a}/search/${o}`, "_blank");
        })), $(document).on("click", ".titleSvg, .carNumSvg, .downSvg", (t => {
            t.preventDefault(), t.stopPropagation();
            const n = $(t.currentTarget).closest(".item"), {carNum: a, aHref: i, title: s} = e.findCarNumAndHref(n), r = n.find(p ? ".photo-frame img" : ".cover img");
            $(t.currentTarget).hasClass("titleSvg") ? utils.copyToClipboard("标题", s) : $(t.currentTarget).hasClass("carNumSvg") ? utils.copyToClipboard("番号", a) : $(t.currentTarget).hasClass("downSvg") && fetch(r.attr("src")).then((t => t.blob())).then((t => {
                utils.download(t, s + ".jpg");
            }));
        }));
    }
    showImg(t, e, n) {
        t.html(this.videoSvg).attr("title", "播放视频");
        let a = $(`#${`${n}_preview_video`}`);
        a.length > 0 && (a[0].pause(), a.parent().hide()), e.show(), e.removeClass("loading"), 
        e.next(".loading-spinner").remove();
    }
    async showVideo(t, e, n) {
        const a = `${n}_preview_video`;
        let i = $(`#${a}`);
        if (i.length > 0) return i.parent().show(), i[0].play(), void e.hide();
        e.addClass("loading"), e.after('<div class="loading-spinner"></div>');
        const s = e.attr("src"), r = await N(n);
        if (!r) return show.error("获取预览视频地址失败"), void this.showImg(t, e, n);
        let o = await storageManager.getSetting("videoQuality") || "hhb";
        r[o] || (o = Object.keys(r)[0]);
        const l = `\n            <div style="display: flex; justify-content: center; align-items: center; position: absolute; top:0; left:0; height: 100%; width: 100%; z-index: 10; overflow: hidden">\n                <video \n                    src="${r[o]}" \n                    poster="${s}" \n                    id="${a}" \n                    controls \n                    loop \n                    muted \n                    playsinline\n                    style="max-height: 100%; max-width: 100%; object-fit: contain"\n                ></video>\n            </div>\n        `;
        e.parent().append(l), e.hide(), e.removeClass("loading"), e.next(".loading-spinner").remove(), 
        i = $(`#${a}`);
        let c = i[0];
        c.load(), c.muted = !1, c.play(), i.trigger("focus");
    }
}

class Fc2By123AvPlugin extends BasePlugin {
    constructor() {
        super(...arguments), o(this, "$contentBox", $(".section .container")), o(this, "urlParams", new URLSearchParams(window.location.search)), 
        o(this, "sortVal", this.urlParams.get("sort") || "release_date"), o(this, "currentPage", this.urlParams.get("page") ? parseInt(this.urlParams.get("page")) : 1), 
        o(this, "maxPage", null), o(this, "keyword", this.urlParams.get("keyword") || null);
    }
    async getBaseUrl() {
        const t = this.getBean("OtherSitePlugin");
        return await t.getAv123Url() + "/ja";
    }
    handle() {
        $("#navbar-menu-hero > div > div:nth-child(1) > div > a:nth-child(4)").after('<a class="navbar-item" href="/advanced_search?type=100&released_start=2099-09">123Av-Fc2</a>'), 
        $('.tabs li:contains("FC2")').after('<li><a href="/advanced_search?type=100&released_start=2099-09"><span>123Av-Fc2</span></a></li>'), 
        d.includes("/advanced_search?type=100") && (this.hookPage(), this.handleQuery().then());
    }
    hookPage() {
        let t = $("h2.section-title");
        t.contents().first().replaceWith("123Av"), t.css("marginBottom", "0"), t.append('\n            <div style="margin-left: 100px; width: 400px;">\n                <input id="search-123av-keyword" type="text" placeholder="搜索123Av Fc2ppv内容" style="padding: 4px 5px;margin-right: 0">\n                <a id="search-123av-btn" class="a-primary" style="margin-left: 0">搜索</a>\n                <a id="clear-123av-btn" class="a-dark" style="margin-left: 0">重置</a>\n            </div>\n        '), 
        $("#search-123av-keyword").val(this.keyword), $("#search-123av-btn").on("click", (async () => {
            let t = $("#search-123av-keyword").val().trim();
            t && (this.keyword = t, utils.setHrefParam("keyword", t), await this.handleQuery());
        })), $("#clear-123av-btn").on("click", (async () => {
            $("#search-123av-keyword").val(""), this.keyword = "", utils.setHrefParam("keyword", ""), 
            $(".page-box").show(), $(".tool-box").show(), await this.handleQuery();
        })), $(".empty-message").remove(), $("#foldCategoryBtn").remove(), $(".section .container .box").remove(), 
        $("#sort-toggle-btn").remove(), this.$contentBox.append('<div class="tool-box" style="margin-top: 10px"></div>'), 
        this.$contentBox.append('<div class="movie-list h cols-4 vcols-8" style="margin-top: 10px"></div>'), 
        this.$contentBox.append('<div class="page-box"></div>');
        $(".tool-box").append('\n            <div class="button-group">\n                <div class="buttons has-addons" id="conditionBox">\n                    <a style="padding:18px 18px !important;" class="button is-small" data-sort="release_date">发布日期</a>\n                    <a style="padding:18px 18px !important;" class="button is-small" data-sort="recent_update">最近更新</a>\n                    <a style="padding:18px 18px !important;" class="button is-small" data-sort="trending">热门</a>\n                    <a style="padding:18px 18px !important;" class="button is-small" data-sort="most_viewed_today">今天最多观看</a>\n                    <a style="padding:18px 18px !important;" class="button is-small" data-sort="most_viewed_week">本周最多观看</a>\n                    <a style="padding:18px 18px !important;" class="button is-small" data-sort="most_viewed_month">本月最多观看</a>\n                    <a style="padding:18px 18px !important;" class="button is-small" data-sort="most_viewed">最多观看</a>\n                    <a style="padding:18px 18px !important;" class="button is-small" data-sort="most_favourited">最受欢迎</a>\n                </div>\n            </div>\n        '), 
        $(`#conditionBox a[data-sort="${this.sortVal}"]`).addClass("is-info"), utils.setHrefParam("sort", this.sortVal), 
        utils.setHrefParam("page", this.currentPage), $("#conditionBox").on("click", "a.button", (t => {
            let e = $(t.target);
            this.sortVal = e.data("sort"), utils.setHrefParam("sort", this.sortVal), e.siblings().removeClass("is-info"), 
            e.addClass("is-info"), this.handleQuery();
        }));
        $(".page-box").append('\n            <nav class="pagination">\n                <a class="pagination-previous">上一页</a>\n                <ul class="pagination-list"></ul>\n                <a class="pagination-next">下一页</a>\n            </nav>\n        '), 
        $(document).on("click", ".pagination-link", (t => {
            t.preventDefault(), this.currentPage = parseInt($(t.target).data("page")), utils.setHrefParam("page", this.currentPage), 
            this.renderPagination(), this.handleQuery();
        })), $(".pagination-previous").on("click", (t => {
            t.preventDefault(), this.currentPage > 1 && (this.currentPage--, utils.setHrefParam("page", this.currentPage), 
            this.renderPagination(), this.handleQuery());
        })), $(".pagination-next").on("click", (t => {
            t.preventDefault(), this.currentPage < this.maxPage && (this.currentPage++, utils.setHrefParam("page", this.currentPage), 
            this.renderPagination(), this.handleQuery());
        }));
    }
    renderPagination() {
        const t = $(".pagination-list");
        t.empty();
        let e = Math.max(1, this.currentPage - 2), n = Math.min(this.maxPage, this.currentPage + 2);
        this.currentPage <= 3 ? n = Math.min(6, this.maxPage) : this.currentPage >= this.maxPage - 2 && (e = Math.max(this.maxPage - 5, 1)), 
        e > 1 && (t.append('<li><a class="pagination-link" data-page="1">1</a></li>'), e > 2 && t.append('<li><span class="pagination-ellipsis">…</span></li>'));
        for (let a = e; a <= n; a++) {
            const e = a === this.currentPage ? " is-current" : "";
            t.append(`<li><a class="pagination-link${e}" data-page="${a}">${a}</a></li>`);
        }
        n < this.maxPage && (n < this.maxPage - 1 && t.append('<li><span class="pagination-ellipsis">…</span></li>'), 
        t.append(`<li><a class="pagination-link" data-page="${this.maxPage}">${this.maxPage}</a></li>`));
    }
    async handleQuery() {
        let t = loading();
        try {
            let t = [];
            t = 1 === this.currentPage ? [ 1, 2 ] : [ 2 * this.currentPage - 1, 2 * this.currentPage ], 
            this.keyword && (t = [ 1 ], $(".page-box").hide(), $(".tool-box").hide());
            const e = await this.getBaseUrl(), n = t.map((t => {
                let n = `${e}/dm4/tags/fc2?sort=${this.sortVal}&page=${t}`;
                return this.keyword && (n = `${e}/search?keyword=${this.keyword}`), gmHttp.get(n);
            })), a = await Promise.all(n);
            let i = [];
            for (const r of a) {
                let t = $(r);
                if (t.find(".box-item").each(((t, n) => {
                    const a = $(n), s = a.find("img").attr("data-src");
                    let r = a.find("img").attr("title");
                    const o = a.find(".detail a"), l = o.attr("href"), c = e + (l.startsWith("/") ? l : "/" + l), d = o.text().trim().replace(r + " - ", "");
                    r = r.replace("FC2-PPV", "FC2"), i.push({
                        imgSrc: s,
                        carNum: r,
                        href: c,
                        title: d
                    });
                })), !this.maxPage) {
                    let e, n = t.find(".page-item:not(.disabled)").last();
                    if (n.find("a.page-link").length) {
                        let t = n.find("a.page-link").attr("href");
                        e = parseInt(t.split("page=")[1]);
                    } else e = parseInt(n.find("span.page-link").text());
                    this.maxPage = Math.ceil(e / 2), this.renderPagination();
                }
            }
            if (0 === i.length) {
                show.error("无结果");
                let t = `${e}/dm4/tags/fc2?sort=${this.sortVal}`;
                this.keyword && (t = `${e}/search?keyword=${this.keyword}`), console.error("获取数据失败!", t);
            }
            let s = this.markDataListHtml(i);
            $(".movie-list").html(s), await utils.smoothScrollToTop();
        } catch (e) {
            console.error(e);
        } finally {
            t.close();
        }
    }
    open123AvFc2Dialog(t, e) {
        let n = `\n            <div class="movie-detail-container">\n                <div class="movie-poster-container">\n                    <iframe class="movie-trailer" frameborder="0" allowfullscreen scrolling="no"></iframe>\n                </div>\n                <div class="right-box">\n                    <div class="movie-info-container">\n                        <div class="search-loading">加载中...</div>\n                    </div>\n                    <div style="margin: 10px 0">\n                        <a id="filterBtn" class="menu-btn" style="background-color:${y}"><span>${w}</span></a>\n                        <a id="favoriteBtn" class="menu-btn" style="background-color:${_}"><span>${x}</span></a>\n                        <a id="hasDownBtn" class="menu-btn" style="background-color:${S}"><span>${C}</span></a>\n                        <a id="hasWatchBtn" class="menu-btn" style="background-color:${B};"><span>${P}</span></a>\n                        \n                        <a id="search-subtitle-btn" class="menu-btn fr-btn" style="background:linear-gradient(to bottom, #8d5656, rgb(196,159,91))">\n                            <span>字幕 (SubTitleCat)</span>\n                        </a>\n                        <a id="xunLeiSubtitleBtn" class="menu-btn fr-btn" style="background:linear-gradient(to left, #375f7c, #2196F3)">\n                            <span>字幕 (迅雷)</span>\n                        </a>\n                    </div>\n                    <div class="message video-panel" style="margin-top:20px">\n                        <div id="magnets-content" class="magnet-links">\n                        </div>\n                    </div>\n                    <div id="reviews-content">\n                    </div>\n                    <div id="related-content">\n                    </div>\n                    <span id="data-actress" style="display: none"></span>\n                </div>\n            </div>\n        `;
        layer.open({
            type: 1,
            title: t,
            content: n,
            area: [ "80%", "90%" ],
            skin: "movie-detail-layer",
            scrollbar: !1,
            success: (n, a) => {
                this.loadData(t, e);
                let i = t.replace("FC2-", "");
                $("#magnets-content").append(this.getBean("MagnetHubPlugin").createMagnetHub(i)), 
                $("#favoriteBtn").on("click", (async n => {
                    const a = $("#data-actress").text();
                    await storageManager.saveCar(t, e, a, m), window.refresh(), layer.closeAll();
                })), $("#filterBtn").on("click", (n => {
                    utils.q(n, `是否屏蔽${t}?`, (async () => {
                        const n = $("#data-actress").text();
                        await storageManager.saveCar(t, e, n, u), window.refresh(), layer.closeAll(), window.location.href.includes("collection_codes?movieId") && utils.closePage();
                    }));
                })), $("#hasDownBtn").on("click", (async n => {
                    const a = $("#data-actress").text();
                    await storageManager.saveCar(t, e, a, f), window.refresh(), layer.closeAll();
                })), $("#hasWatchBtn").on("click", (async n => {
                    const a = $("#data-actress").text();
                    await storageManager.saveCar(t, e, a, v), window.refresh(), layer.closeAll();
                })), $("#search-subtitle-btn").on("click", (e => utils.openPage(`https://subtitlecat.com/index.php?search=${t}`, t, !1, e))), 
                $("#xunLeiSubtitleBtn").on("click", (() => this.getBean("DetailPageButtonPlugin").searchXunLeiSubtitle(t)));
            }
        });
    }
    async loadData(t, e) {
        let n = loading();
        try {
            const {id: n, publishDate: a, title: i, moviePoster: s} = await this.get123AvVideoInfo(e);
            $(".movie-info-container").html(`\n                    <h3 class="movie-title" style="margin-bottom: 10px">${i || "无标题"}</h3>\n                    <div class="movie-meta" style="margin-bottom: 10px">\n                        <span>番号: ${t || "未知"}</span>\n                        <span>年份: ${a || "未知"}</span>\n                        <span>\n                            站点: \n                            <a href="https://fc2ppvdb.com/articles/${t.replace("FC2-", "")}" target="_blank">fc2ppvdb</a>\n                            <a style="margin-left: 5px;" href="https://adult.contents.fc2.com/article/${t.replace("FC2-", "")}/" target="_blank">fc2电子市场</a>\n                        </span>\n                    </div>\n                    <div class="movie-actors" style="margin-bottom: 10px">\n                        <div class="actor-list">主演: </div>\n                    </div>\n                    <div class="movie-seller" style="margin-bottom: 10px">\n                        <span>販売者: </span>\n                    </div>\n                    <div class="movie-gallery" style="margin-bottom: 10px">\n                        <h4>剧照: </h4>\n                        <div class="image-list"></div>\n                    </div>\n                `), 
            this.getMovie(n, s).then((t => {
                $(".movie-trailer").attr("src", t[0].url);
                let e = '\n                    <div class="movie-gallery" style="margin-bottom: 10px"> \n                    <span>影片: </span> \n                    <div class="movie-parts-list">\n                ';
                t.forEach(((t, n) => {
                    e += `\n                        <a class="movie-part a-outline" data-url="${t.url}" style="margin-left: 0">\n                            部分 ${n + 1}\n                        </a>\n                    `;
                })), e += "</div> </div> ", $(".movie-gallery").after(e), $(".movie-parts-list").on("click", ".movie-part", (function() {
                    const t = $(this).data("url");
                    $(".movie-trailer").attr("src", t);
                }));
            })), this.getImgList(t).then(), this.getActressInfo(t).then();
        } catch (a) {
            console.error(a);
        } finally {
            n.close();
        }
    }
    async get123AvVideoInfo(t) {
        const e = await gmHttp.get(t), n = e.match(/v-scope="Movie\({id:\s*(\d+),/), a = n ? n[1] : null, i = $(e);
        return {
            id: a,
            publishDate: i.find('span:contains("リリース日:")').next("span").text(),
            title: i.find("h1").text().trim(),
            moviePoster: i.find("#player").attr("data-poster")
        };
    }
    async getActressInfo(t) {
        let e = `https://fc2ppvdb.com/articles/${t.replace("FC2-", "")}`;
        const n = await gmHttp.get(e), a = $(n), i = a.find("div").filter((function() {
            return 0 === $(this).text().trim().indexOf("女優:");
        }));
        if (0 === i.length || i.length > 1) return void show.error("解析女优信息失败");
        const s = $(i[0]).find("a");
        let r = "主演: ";
        if (s.length > 0) {
            let t = "";
            s.each(((e, n) => {
                let a = $(n), i = a.text(), s = a.attr("href");
                r += `<span class="actor-tag"><a href="https://fc2ppvdb.com${s}" target="_blank">${i}</a></span>`, 
                t += i + " ";
            })), $("#data-actress").text(t);
        } else r += "<span>暂无演员信息</span>";
        $(".actor-list").html(r);
        const o = a.find("div").filter((function() {
            return 0 === $(this).text().trim().indexOf("販売者:");
        }));
        if (o.length > 0) {
            const t = $(o[0]).find("a");
            if (t.length > 0) {
                const e = $(t[0]);
                let n = e.text(), a = e.attr("href");
                $(".movie-seller").html(`<span> 販売者: <a href="https://fc2ppvdb.com${a}" target="_blank">${n}</a></span>`);
            }
        }
    }
    async getImgList(t) {
        let e = `https://adult.contents.fc2.com/article/${t.replace("FC2-", "")}/`;
        const n = await gmHttp.get(e, null, {
            referer: e
        });
        let a = $(n).find(".items_article_SampleImagesArea img").map((function() {
            return $(this).attr("src");
        })).get(), i = "";
        Array.isArray(a) && a.length > 0 ? i = a.map(((t, e) => `\n                <a href="${t}" data-fancybox="movie-gallery" data-caption="剧照 ${e + 1}">\n                    <img src="${t}" class="movie-image-thumb"  alt=""/>\n                </a>\n            `)).join("") : $(".movie-gallery").html("<h4>剧照: 暂无剧照</h4>"), 
        $(".image-list").html(i);
    }
    async getMovie(t, e) {
        let n = `${await this.getBaseUrl()}/ajax/v/${t}/videos`, a = loading();
        try {
            let t = (await gmHttp.get(n)).result.watch;
            return t.length > 0 ? (t.forEach((t => {
                t.url = t.url + "?poster=" + e;
            })), t) : null;
        } catch (i) {
            console.error(i);
        } finally {
            a.close();
        }
    }
    markDataListHtml(t) {
        let e = "";
        return t.forEach((t => {
            e += `\n                <div class="item">\n                    <a href="${t.href}" class="box" title="${t.title}">\n                        <div class="cover ">\n                            <img loading="lazy" src="${t.imgSrc.replace("/s360", "")}" alt="">\n                        </div>\n                        <div class="video-title"><strong>${t.carNum}</strong> ${t.title}</div>\n                        <div class="score">\n                        </div>\n                        <div class="meta">\n                        </div>\n                        <div class="tags has-addons">\n                        </div>\n                    </a>\n                </div>\n            `;
        })), e;
    }
}

class video123AvPlugin extends BasePlugin {
    async handle() {
        if (!d.includes("5masterzzz")) return;
        localStorage.setItem("__pul", Date.now().toString()), setInterval((() => {
            localStorage.setItem("__pul", Date.now().toString());
        }), 5e3);
        document.querySelector("video").play().then();
    }
}

class MagnetHubPlugin extends BasePlugin {
    constructor() {
        super(...arguments), o(this, "currentEngine", null), o(this, "searchEngines", [ {
            name: "U3C3",
            id: "u3c3",
            url: "https://u3c3.com/?search2=eelj1a3lfe1a1&search={keyword}",
            parse: this.parseU3C3
        }, {
            name: "BTSOW",
            id: "BTSOW",
            url: "https://btsow.pics/search/{keyword}",
            parse: this.parseBTSOW
        }, {
            name: "Sukebei",
            id: "Sukebei",
            url: "https://sukebei.nyaa.si/?f=0&c=0_0&q={keyword}",
            parse: this.parseSukebei
        } ]);
    }
    async initCss() {
        return "\n            <style>\n                .magnet-container {\n                    margin: 20px auto;\n                    width: 100%;\n                    font-family: Arial, sans-serif;\n                }\n                .magnet-tabs {\n                    display: flex;\n                    border-bottom: 1px solid #ddd;\n                    margin-bottom: 15px;\n                }\n                .magnet-tab {\n                    padding: 5px 12px;\n                    cursor: pointer;\n                    border: 1px solid transparent;\n                    border-bottom: none;\n                    margin-right: 5px;\n                    background: #f5f5f5;\n                    border-radius: 5px 5px 0 0;\n                }\n                .magnet-tab.active {\n                    background: #fff;\n                    border-color: #ddd;\n                    border-bottom: 1px solid #fff;\n                    margin-bottom: -1px;\n                    font-weight: bold;\n                }\n                .magnet-tab:hover:not(.active) {\n                    background: #e9e9e9;\n                }\n                \n                .magnet-results {\n                    min-height: 200px;\n                }\n                .magnet-result {\n                    padding: 15px;\n                    border-bottom: 1px solid #eee;\n                    position: relative; \n                }\n                .magnet-result:hover {\n                    background-color: #f9f9f9;\n                }\n                .magnet-title {\n                    font-weight: bold;\n                    margin-bottom: 5px;\n                    white-space: nowrap;\n                    overflow: hidden; \n                    text-overflow: ellipsis;\n                    padding-right: 80px; \n                }\n                .magnet-info {\n                    display: flex;\n                    justify-content: space-between;\n                    font-size: 12px;\n                    color: #666;\n                    margin-bottom: 5px;\n                }\n                .magnet-loading {\n                    text-align: center;\n                    padding: 20px;\n                }\n                .magnet-error {\n                    color: #f44336;\n                    padding: 10px;\n                }\n                \n                .magnet-copy {\n                position: absolute;\n                right: 15px;\n                top: 12px;\n            }\n                .copy-btn {\n                    background-color: #f0f0f0;\n                    color: #555;\n                    border: 1px solid #ddd;\n                    padding: 3px 8px;\n                    border-radius: 3px;\n                    cursor: pointer;\n                    font-size: 12px;\n                    transition: all 0.2s;\n                }\n                .copy-btn:hover {\n                    background-color: #e0e0e0;\n                    border-color: #ccc;\n                }\n                .copy-btn.copied {\n                    background-color: #4CAF50;\n                    color: white;\n                    border-color: #4CAF50;\n                }\n            </style>\n        ";
    }
    createMagnetHub(t) {
        const e = $('<div class="magnet-container"></div>'), n = $('<div class="magnet-tabs"></div>'), a = localStorage.getItem("magnetHub_selectedEngine");
        let i = 0;
        this.searchEngines.forEach(((t, e) => {
            const s = $(`<div class="magnet-tab" data-engine="${t.id}">${t.name}</div>`);
            a && t.id === a ? (s.addClass("active"), this.currentEngine = t, i = e) : 0 !== e || a || (s.addClass("active"), 
            this.currentEngine = t), n.append(s);
        })), e.append(n);
        const s = $('<div class="magnet-results"></div>');
        return e.append(s), e.on("click", ".magnet-tab", (n => {
            const a = $(n.target).data("engine");
            this.currentEngine = this.searchEngines.find((t => t.id === a)), localStorage.setItem("magnetHub_selectedEngine", a), 
            e.find(".magnet-tab").removeClass("active"), $(n.target).addClass("active"), this.searchEngine(s, this.currentEngine, t);
        })), this.searchEngine(s, this.currentEngine || this.searchEngines[i], t), e;
    }
    searchEngine(t, e, n) {
        t.html(`<div class="magnet-loading">正在从 ${e.name} 搜索 "${n}"...</div>`);
        const a = `${e.name}_${n}`, i = sessionStorage.getItem(a);
        if (i) try {
            const n = JSON.parse(i);
            return void this.displayResults(t, n, e.name);
        } catch (r) {
            t.html(`<div class="magnet-error">解析 ${e.name} 缓存结果失败: ${r.message}</div>`);
        }
        const s = e.url.replace("{keyword}", encodeURIComponent(n));
        GM_xmlhttpRequest({
            method: "GET",
            url: s,
            onload: n => {
                try {
                    const i = e.parse.call(this, n.responseText);
                    i.length > 0 && sessionStorage.setItem(a, JSON.stringify(i)), this.displayResults(t, i, e.name);
                } catch (r) {
                    t.html(`<div class="magnet-error">解析 ${e.name} 结果失败: ${r.message}</div>`);
                }
            },
            onerror: n => {
                t.html(`<div class="magnet-error">从 ${e.name} 获取数据失败: ${n.statusText}</div>`);
            }
        });
    }
    displayResults(t, e, n) {
        function a(t) {
            const e = t.text();
            t.addClass("copied").text("已复制"), setTimeout((() => {
                t.removeClass("copied").text(e);
            }), 2e3);
        }
        function i(t, e) {
            const n = document.createElement("textarea");
            n.value = t, n.style.position = "fixed", document.body.appendChild(n), n.select();
            try {
                document.execCommand("copy"), a(e);
            } catch (i) {
                console.error("复制失败:", i), alert("复制失败,请手动复制链接");
            }
            document.body.removeChild(n);
        }
        t.empty(), 0 !== e.length ? (e.forEach((e => {
            const n = $(`\n                <div class="magnet-result">\n                    <div class="magnet-title"><a href="${e.magnet}">${e.title}</a></div>\n                    <div class="magnet-info">\n                        <span>大小: ${e.size || "未知"}</span>\n                        <span>日期: ${e.date || "未知"}</span>\n                    </div>\n                    <div class="magnet-copy">\n                        <button class="copy-btn" data-magnet="${e.magnet}">复制链接</button>\n                    </div>\n                </div>\n            `);
            t.append(n);
        })), t.on("click", ".copy-btn", (function() {
            const t = $(this), e = t.data("magnet");
            navigator.clipboard ? navigator.clipboard.writeText(e).then((() => {
                a(t);
            })).catch((n => {
                i(e, t);
            })) : i(e, t);
        }))) : t.append('<div class="magnet-error">没有找到相关结果</div>');
    }
    parseBTSOW(t) {
        const e = utils.htmlTo$dom(t), n = [];
        return e.find(".data-list .row").each(((t, e) => {
            const a = $(e);
            let i = a.find("a");
            if (0 === i.length) return;
            const s = i.attr("title"), r = "magnet:?xt=urn:btih:" + i.attr("href").split("/").pop(), o = a.find(".size").text(), l = a.find(".date").text();
            n.push({
                title: s,
                magnet: r,
                size: o,
                date: l
            });
        })), n;
    }
    parseU3C3(t) {
        const e = utils.htmlTo$dom(t), n = [];
        return e.find(".torrent-list tbody tr").each(((t, e) => {
            const a = $(e);
            if (a.text().includes("置顶")) return;
            const i = a.find("td:nth-child(2) a").attr("title") || a.find("td:nth-child(2) a").text().trim(), s = a.find("td:nth-child(3) a[href^='magnet:']").attr("href"), r = a.find("td:nth-child(4)").text().trim(), o = a.find("td:nth-child(5)").text().trim();
            s && n.push({
                title: i,
                magnet: s,
                size: r,
                date: o
            });
        })), n;
    }
    parseSukebei(t) {
        const e = utils.htmlTo$dom(t), n = [];
        return e.find(".torrent-list tbody tr").each(((t, e) => {
            const a = $(e);
            if (a.text().includes("置顶")) return;
            const i = a.find("td:nth-child(2) a").attr("title") || a.find("td:nth-child(2) a").text().trim(), s = a.find("td:nth-child(3) a[href^='magnet:']").attr("href"), r = a.find("td:nth-child(4)").text().trim(), o = a.find("td:nth-child(5)").text().trim();
            s && n.push({
                title: i,
                magnet: s,
                size: r,
                date: o
            });
        })), n;
    }
}

class ScreenShotPlugin extends BasePlugin {
    async handle() {
        if (!isDetailPage) return;
        let t = this.getPageInfo().carNum;
        const e = `ScreenShot_${t}`, n = sessionStorage.getItem(e);
        n ? this.addImg("缩略图", n) : Promise.any([ this.img_3xplanet(t), this.img_memojav(t) ]).then((t => {
            t && (sessionStorage.setItem(e, t), this.addImg("缩略图", t));
        })).catch((t => {
            console.error(t);
        }));
    }
    addImg(t, e) {
        if (!e) return;
        let n = utils.simpleId();
        g && $(".preview-images").append(`\n                <a class="tile-item" data-fancybox="gallery" data-caption="${t}" data-src="#${n}" style="overflow:hidden;max-height: 100px;">\n                  <img src="${e}" alt="${t}" loading="lazy" style="width: 100%;">\n                </a> \n                <div id="${n}" style="display: none;"><img src="${e}" alt="${t}" loading="lazy" style="width: 85vw;"></div>\n            `), 
        p && ($("#sample-waterfall .sample-box:last").after(`\n                <a class="sample-box" style="height: 110px; overflow:hidden;" id="${n}" href="${e}"><div class="photo-frame"><img src="${e}" style="height: inherit;width: 100%;" title="${t}" alt="${t}"></div></a>\n            `), 
        $(`#${n}`).on("click", (n => {
            n.preventDefault(), n.stopPropagation(), layer.photos({
                photos: {
                    title: t,
                    start: 0,
                    data: [ {
                        alt: t,
                        pid: 0,
                        src: e
                    } ]
                },
                footer: !1,
                success: function(t) {
                    t.find('[toolbar-event="zoom"][data-option="0.1"]').attr("data-option", .5);
                    t.find('[toolbar-event="zoom"][data-option="-0.1"]').attr("data-option", -.5);
                }
            });
        })));
    }
    async img_3xplanet(t) {
        let e = `https://3xplanet.com/?s=${t}`, n = await gmHttp.get(e);
        const a = $(n).find(".td-image-wrap").first().attr("href");
        if (!a) throw console.error("解析3xplanet搜索页失败:", e), new Error("解析3xplanet搜索页失败");
        const i = await gmHttp.get(a);
        let s = $(i).find('img[src*="s200"]').attr("src");
        if (!s) throw console.error("解析3xplanet缩略图失败:", e), new Error("解析3xplanet缩略图失败");
        return s = s.replace("s200", "s0"), s;
    }
    async img_memojav(t) {
        let e = `https://memojav.com/image/screenshot/${t}.jpg`;
        try {
            return await gmHttp.gmRequest("HEAD", e), e;
        } catch (n) {
            throw console.error("memojav缩略图不可用:", n), n;
        }
    }
}

class FilterActorVideoPlugin extends BasePlugin {
    async handle() {
        d.includes("/actors/") && $("h2").append('<a class="a-danger" id="filterActorVideo" style="padding:8px;">屏蔽该演员所有作品</a>'), 
        d.includes("/star/") && $("#waitDownBtn").after(' \n                <a id="filterActorVideo" class="menu-btn" style="background-color:#b91c1c !important;margin-left: 5px;border-bottom:none !important;border-radius:3px;">\n                    <span>屏蔽该演员所有作品</span>\n                </a>\n            '), 
        $("#filterActorVideo").on("click", (t => {
            let e = {
                clientX: t.clientX,
                clientY: t.clientY + 80
            };
            utils.q(e, "是否屏蔽该演员下的所有作品?", (async () => {
                this.loadObj = loading();
                try {
                    await storageManager.saveSettingItem("autoPage", "yes");
                    let t = g ? $(".actor-section-name") : $(".avatar-box .photo-info .pb10");
                    if (0 === t.length) return void show.error("获取演员名称失败");
                    let e, n = t.text().trim().split(",")[0], a = $(".section-meta:contains('男優')").length > 0;
                    e = a ? storageManager.filter_actor_car_list_key + n : storageManager.filter_actress_car_list_key + n;
                    const i = await storageManager.getSetting(storageManager.filter_actor_actress_info_list_key, []), s = this.getCurrentStarUrl();
                    i.some((t => t.name === n || t.url === s)) || (i.push({
                        name: n,
                        key: e,
                        url: s,
                        isActor: a,
                        checkTime: ""
                    }), await storageManager.saveSettingItem(storageManager.filter_actor_actress_info_list_key, i)), 
                    await this.filterActorVideo(e, n);
                } catch (t) {
                    console.error(t), this.loadObj.close();
                } finally {
                    this.loadObj.close();
                }
            }));
        })), this.checkNewActressActorFilterCar();
    }
    getCurrentStarUrl() {
        let t = d.replace(/\/\d+(\?|$)/, "$1");
        return t = t.replace(/([&?])page=\d+&?/, "$1"), t = t.replace(/[&?]$/, ""), t = t.replace(/\?&/, "?"), 
        t;
    }
    async filterActorVideo(t, e, n) {
        let a = await this.parseAndSaveFilterInfo(n, t, e);
        if (a) {
            show.info("请不要关闭窗口, 正在解析下一页:" + a), await new Promise((t => setTimeout(t, 500)));
            const n = await http.get(a), i = new DOMParser, s = $(i.parseFromString(n, "text/html"));
            await this.filterActorVideo(t, e, s);
        } else show.ok("执行结束!"), await storageManager.saveSettingItem("autoPage", "no"), 
        window.refresh();
    }
    async parseAndSaveFilterInfo(t, e, n) {
        let a, i;
        if (t ? (p && t.find(".avatar-box").length > 0 && t.find(".avatar-box").parent().remove(), 
        a = t.find(this.getSelector().requestDomItemSelector), i = t.find(this.getSelector().nextPageSelector).attr("href")) : (a = $(this.getSelector().itemSelector), 
        i = $(this.getSelector().nextPageSelector).attr("href")), i && 0 === a.length) throw show.error("解析列表失败"), 
        new Error("解析列表失败");
        for (const r of a) {
            const t = $(r), {carNum: a, aHref: i} = this.getBean("ListPagePlugin").findCarNumAndHref(t);
            if (i && a) try {
                if (await storageManager.getActorFilterCar(e, a)) continue;
                await storageManager.saveActorFilterCar(e, a, i, n), console.log("屏蔽女优番号", n, a);
            } catch (s) {
                console.error(`保存失败 [${a}]:`, s);
            }
        }
        return i;
    }
    async checkNewActressActorFilterCar() {
        const t = await storageManager.getSetting(storageManager.filter_actor_actress_info_list_key, []), e = {
            ...await storageManager.getActressFilterCarMap(),
            ...await storageManager.getActorFilterCarMap()
        }, n = Object.keys(e);
        for (const i of n) try {
            const e = i.split("_").pop(), n = t.find((t => t.name === e));
            if (!n) {
                console.log("演员详情页不存在, 需重新屏蔽: ", e);
                continue;
            }
            let a = n.url, s = n.checkTime;
            if (s && this.isToday(s)) {
                console.log("今日已检测,跳过", e);
                continue;
            }
            const r = await http.get(a), o = $(r);
            console.log("检测屏蔽演员最新番号:", e, a), await this.parseAndSaveFilterInfo(o, i, e), n.checkTime = utils.getNowStr();
        } catch (a) {
            console.error("检测屏蔽演员信息, 发生错误:", a);
        } finally {
            await storageManager.saveSettingItem(storageManager.filter_actor_actress_info_list_key, t);
        }
    }
    isToday(t) {
        return (new Date).toISOString().split("T")[0] === t.split(" ")[0];
    }
}

class WangPan115TaskPlugin extends BasePlugin {
    async handle() {
        $(".buttons button[data-clipboard-text*='magnet:']").each(((t, e) => {
            $(e).parent().append($("<button>").text("115离线下载").addClass("button is-info is-small").click((async () => {
                let t = loading();
                try {
                    await this.handleAddTask($(e).attr("data-clipboard-text"));
                } catch (n) {
                    console.error(n);
                } finally {
                    t.close();
                }
            })));
        }));
    }
    async handleAddTask(t) {
        const e = await (async () => {
            const t = await gmHttp.get("https://115.com/?ct=offline&ac=space&_=" + (new Date).getTime());
            return "object" == typeof t ? t : null;
        })();
        if (!e) return void show.error("未登录115网盘", {
            close: !0,
            duration: -1,
            callback: async () => {
                const t = await storageManager.getSetting("cookie115", "");
                window.open("https://115.com/?cookie=" + t);
            }
        });
        const n = e.sign, a = e.time, i = "115UserId";
        let s = sessionStorage.getItem(i);
        if (s || (s = await (async () => {
            const t = await gmHttp.get("https://webapi.115.com/offine/downpath");
            return "object" == typeof t ? t.data[0].id : null;
        })()), !s) return void show.error("获取115网盘UserId失败");
        sessionStorage.setItem(i, s);
        const r = await (async (t, e, n, a) => {
            const i = {
                url: encodeURIComponent(t),
                uid: e,
                sign: n,
                time: a
            };
            return await gmHttp.postForm("https://115.com/web/lixian/?ct=lixian&ac=add_task_url", i);
        })(t, s, n, a);
        console.log("离线下载返回值:", r), !1 === r.state ? show.error(r.error_msg || "添加任务失败") : show.ok("添加成功");
    }
}

class WangPan115Plugin extends BasePlugin {
    async handle() {
        const t = this.parseCookie(), e = [ "UID", "CID", "KID", "SEID" ];
        if (t) {
            let n = "";
            const a = new Set;
            t.split(";").forEach((t => {
                const i = t.trim();
                if (i) {
                    const [t, s] = i.split("=");
                    t && s && e.includes(t) && (n += i + ";", a.add(t));
                }
            }));
            const i = e.filter((t => !a.has(t)));
            if (i.length > 0) return void show.error(`缺少必需的 Cookie: ${i.join(", ")}`);
            if (!n) return void show.error("cookie为空,无法执行记忆登录");
            utils.addCookie(n, {
                domain: ".115.com"
            }), window.location.reload();
        } else console.error("未获取到有效的 Cookie 字符串");
    }
    parseCookie() {
        const t = new URLSearchParams(window.location.search).get("goto");
        if (t) {
            const e = decodeURIComponent(t), n = new URL(e), a = new URLSearchParams(n.search).get("cookie");
            if (a) return decodeURIComponent(a);
        }
        return null;
    }
}

utils.importResource("https://cdn.jsdelivr.net/npm/[email protected]/layer.min.css"), 
utils.importResource("https://cdn.jsdelivr.net/npm/[email protected]/src/toastify.min.css"), 
window.onload = async function() {
    window.isDetailPage = function() {
        let t = window.location.href;
        return g ? t.split("?")[0].includes("/v/") : !!p && $("#magnet-table").length > 0;
    }(), window.isListPage = function() {
        let t = window.location.href;
        return g ? $(".movie-list").length > 0 || t.includes("advanced_search") : !!p && $(".masonry > div .item").length > 0;
    }(), function() {
        const t = new PluginManager;
        let e = window.location.hostname;
        g && (t.register(ListPagePlugin), t.register(AutoPagePlugin), t.register(Fc2Plugin), 
        t.register(FoldCategoryPlugin), t.register(ListPageButtonPlugin), t.register(HistoryPlugin), 
        t.register(SettingPlugin), t.register(NavBarPlugin), t.register(HitShowPlugin), 
        t.register(TOP250Plugin), t.register(SyncDataPlugin), t.register(SearchByImagePlugin), 
        t.register(CopyTitleOrDownImgPlugin), t.register(Fc2By123AvPlugin), t.register(DetailPagePlugin), 
        t.register(ReviewPlugin), t.register(RelatedPlugin), t.register(DetailPageButtonPlugin), 
        t.register(HighlightMagnetPlugin), t.register(PreviewVideoPlugin), t.register(FilterTitleKeywordPlugin), 
        t.register(ActressInfoPlugin), t.register(OtherSitePlugin), t.register(WangPan115TaskPlugin), 
        t.register(WantAndWatchedVideosPlugin), t.register(MagnetHubPlugin), t.register(ScreenShotPlugin), 
        t.register(FilterActorVideoPlugin)), p && (t.register(ListPagePlugin), t.register(ListPageButtonPlugin), 
        t.register(SettingPlugin), t.register(HistoryPlugin), t.register(SyncDataPlugin), 
        t.register(AutoPagePlugin), t.register(SearchByImagePlugin), t.register(BusNavBarPlugin), 
        t.register(CopyTitleOrDownImgPlugin), t.register(BusDetailPagePlugin), t.register(DetailPageButtonPlugin), 
        t.register(ReviewPlugin), t.register(FilterTitleKeywordPlugin), t.register(HighlightMagnetPlugin), 
        t.register(BusPreviewVideoPlugin), t.register(MagnetHubPlugin), t.register(ScreenShotPlugin), 
        t.register(OtherSitePlugin), t.register(WangPan115TaskPlugin), t.register(FilterActorVideoPlugin)), 
        e.includes("sehuatang") && t.register(SeHuaTangPlugin), e.includes("javtrailers") && t.register(JavTrailersPlugin), 
        e.includes("subtitlecat") && t.register(SubTitleCatPlugin), e.includes("aliyundrive") && t.register(AliyunPanPlugin), 
        e.includes("5masterzzz") && t.register(video123AvPlugin), e.includes("115.com") && t.register(WangPan115Plugin), 
        t.process().then();
    }();
};