JAV-JHS

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

// ==UserScript==
// @name         JAV-JHS
// @namespace    https://sleazyfork.org/zh-CN/scripts/533695-jav-jhs
// @version      2.4.0
// @author       xie bro
// @description  Jav-鉴黄师 收藏、屏蔽、标记已下载; 免VIP查看热榜、Top250排行榜、Fc2ppv等数据; 可查看所有评论信息; 支持云盘备份; 以图识图; 字幕搜索; JavBus|JavDb
// @license      MIT
// @icon         https://www.google.com/s2/favicons?sz=64&domain=javdb.com
// @match        https://javdb.com/*
// @match        https://www.javbus.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://www.alipan.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://update.greasyfork.org/scripts/540597/1613170/parallel_GM_xmlhttpRequest.js
// @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
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/viewer.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      javstore.net
// @connect      3xplanet.com
// @connect      javbest.net
// @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, o = t => {
    throw TypeError(t);
}, r = (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) ? o("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) || o("Cannot " + n);
})(t, e, "access private method"), n);

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

let M = "";

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

const A = `\n<style>\n    \n    ${M}\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        height:auto !important;\n        margin: 0 !important;\n        position:relative; /* 方便预览视频定位*/\n    }\n    .masonry .movie-box img {\n        max-height: 500px;\n        height: 100% !important;\n        object-fit: contain;\n        object-position: top;\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    \n        \n    .video-title {\n        display: -webkit-box !important;\n        -webkit-box-orient: vertical;\n        -webkit-line-clamp: 2;  /* 限制显示2行 */\n        white-space: normal !important;\n        margin-bottom: 5px;\n    }\n    \n</style>\n`;

let T = "";

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

const L = `\n<style>\n    ${T}\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    .video-title {\n      display: -webkit-box;\n      -webkit-box-orient: vertical;\n      -webkit-line-clamp: 2;  /* 限制显示2行 */\n      white-space: normal !important;\n    }\n</style>\n`;

function H(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 && H(A), h && H(L), H("\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"), 
H("\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    \n    \n    /* 悬浮提示 */\n    [data-tip] {\n        position: relative;\n        overflow:visible;\n    }\n    [data-tip]::after {\n        content: attr(data-tip);\n        position: absolute;\n        bottom: 100%;\n        left: 50%;\n        padding: 8px 15px;\n        border-radius: 4px;\n        white-space: nowrap;\n        opacity: 0;\n        pointer-events: none;\n        transform: translateX(-50%) translateY(10px);\n        font-size: 14px;\n        z-index: 9999999999;\n        transition: all 0.3s cubic-bezier(0.68, -0.55, 0.27, 1.55);\n        background: #F0FDF4;\n        color: #166534;\n        border: 1px solid #BBF7D0; \n        box-shadow: 0 4px 6px rgba(0, 0, 0, 0.08);\n    }\n    [data-tip]:hover::after {\n        opacity: 1;\n        transform: translateX(-50%) translateY(0);\n    }\n</style>\n");

t = new WeakSet, e = async function() {
    window.location.hostname.includes("javdb") && ((await this.forage.keys()).forEach((t => t.startsWith("lastCleanupTime") && this.forage.removeItem(t))), 
    (await this.forage.keys()).forEach((t => t.startsWith("z_score_") && this.forage.removeItem(t))), 
    (await this.forage.keys()).forEach((t => t.startsWith("z_actress_") && this.forage.removeItem(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 E = class a {
    constructor() {
        if (l(this, t), r(this, "car_list_key", "car_list"), r(this, "title_filter_keyword_key", "title_filter_keyword"), 
        r(this, "review_filter_keyword_key", "review_filter_keyword"), r(this, "setting_key", "setting"), 
        r(this, "filter_actress_car_list_key", "car_list_actress_"), r(this, "filter_actor_car_list_key", "car_list_actor_"), 
        r(this, "filter_actor_actress_info_list_key", "filter_actor_actress_info_list"), 
        r(this, "fold_category_key", "foldCategory"), r(this, "forage", localforage.createInstance({
            driver: localforage.INDEXEDDB,
            name: "JAV-JHS",
            version: 1,
            storeName: "appData"
        })), r(this, "interceptedKeys", [ this.car_list_key, this.title_filter_keyword_key, this.review_filter_keyword_key, this.setting_key ]), 
        a.instance) throw new Error("LocalStorageManager已被实例化过了!");
        a.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, n && (s.actress = n), s.updateDate = utils.getNowStr()) : (s = {
            carNum: t,
            url: e,
            actress: n,
            status: "",
            updateDate: utils.getNowStr()
        }, i.push(s)), a) {
          case m:
            if (s.status === m) {
                const e = `${t} 已在屏蔽列表中`;
                throw show.error(e), new Error(e);
            }
            s.status = m;
            break;

          case u:
            if (s.status === u) {
                const e = `${t} 已在收藏列表中`;
                throw show.error(e), new Error(e);
            }
            s.status = u;
            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: m,
            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 : e;
    }
    async setItem(t, e) {
        if (this.interceptedKeys.includes(t)) {
            let e = `危险操作, 该key已有方法实现获取, 请用内部方法调用!  key: ${t}`;
            throw show.error(e), new Error(e);
        }
        return await this.forage.setItem(t, e);
    }
    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);
        const n = {
            ...await storageManager.getActressFilterCarMap(),
            ...await storageManager.getActorFilterCarMap()
        };
        for (const a of Object.keys(n)) console.log("移除key", a), await this.forage.removeItem(a);
        for (const a of Object.keys(t)) (a.startsWith(this.filter_actress_car_list_key) || a.startsWith(this.filter_actor_car_list_key)) && await this.forage.setItem(a, t[a]);
    }
    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 N {
    constructor() {
        return r(this, "intervalContainer", {}), r(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"
        }), r(this, "insertStyle", (t => {
            t && (-1 === t.indexOf("<style>") && (t = "<style>" + t + "</style>"), $("head").append(t));
        })), N.instance || (N.instance = this), N.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: this.getResponsiveArea([ "85%", "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 o = Math.random(), r = (new Date).getTime();
        this.intervalContainer[o] = setInterval((() => {
            (new Date).getTime() - r > a && (console.warn("loopDetector timeout!", t, e), s = i), 
            (t() || s) && (clearInterval(this.intervalContainer[o]), e && e(), delete this.intervalContainer[o]);
        }), 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 o = layer.confirm(e, {
            offset: [ s, i ],
            title: "提示",
            btn: [ "确定", "取消" ],
            shade: 0,
            zIndex: 999999991
        }, (function() {
            n(), layer.close(o);
        }), (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"), o = String(a.getDate()).padStart(2, "0"), r = String(a.getHours()).padStart(2, "0"), l = String(a.getMinutes()).padStart(2, "0"), c = String(a.getSeconds()).padStart(2, "0");
        return `${[ i, s, o ].join(t)} ${[ r, 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"), o = String(a.getDate()).padStart(2, "0"), r = String(a.getHours()).padStart(2, "0"), l = String(a.getMinutes()).padStart(2, "0"), c = String(a.getSeconds()).padStart(2, "0");
        return `${[ i, s, o ].join(e)} ${[ r, 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), o = document.createElement("a");
        o.href = s, o.download = e, document.body.appendChild(o), o.click(), setTimeout((() => {
            document.body.removeChild(o), URL.revokeObjectURL(s);
        }), 100);
    }
    smoothScrollToTop(t = 500) {
        return new Promise((e => {
            const n = performance.now(), a = window.pageYOffset;
            window.requestAnimationFrame((function i(s) {
                const o = s - n, r = Math.min(o / t, 1), l = r < .5 ? 4 * r * r * r : 1 - Math.pow(-2 * r + 2, 3) / 2;
                window.scrollTo(0, a * (1 - l)), r < 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 >= 1200 ? t || this.getDefaultArea() : e >= 768 ? [ "70%", "90%" ] : [ "95%", "95%" ];
    }
    getDefaultArea() {
        return [ "85%", "90%" ];
    }
    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: o = ""} = e;
        t.split(";").forEach((t => {
            const e = t.trim();
            if (e) {
                const [t, r] = e.split("=");
                if (t && r) {
                    let e = [ `${t}=${r}`, `max-age=${n}`, `path=${a}` ];
                    i && e.push(`domain=${i}`), s && e.push("Secure"), o && e.push(`SameSite=${o}`), 
                    document.cookie = e.join("; ");
                }
            }
        }));
    }
    isHidden(t) {
        const e = t.jquery ? t[0] : t;
        return !e || (e.offsetWidth <= 0 && e.offsetHeight <= 0 || "none" === window.getComputedStyle(e).display);
    }
}

class j {
    constructor() {
        if (r(this, "forage", localforage.createInstance({
            driver: localforage.INDEXEDDB,
            name: "JAV-JHS-SeHuaTang",
            version: 1,
            storeName: "appData"
        })), r(this, "article_list_key", "article_list"), j.instance) throw new Error("SeHuaTangStorageManager已被实例化过了!");
        j.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 m:
            if (s.status === m) {
                const e = `${t} 已在屏蔽列表中`;
                throw show.error(e), new Error(e);
            }
            s.status = m;
            break;

          case u:
            if (s.status === u) {
                const e = `${t} 已在收藏列表中`;
                throw show.error(e), new Error(e);
            }
            s.status = u;
            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 N, 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, o) => {
            $.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;
                    }
                    o(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, o) => {
            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 (n) {
                            a(t.responseText);
                        } else a(t.responseText || t); else if (console.error("请求失败,状态码:", t.status, e), 
                        t.responseText) try {
                            const e = JSON.parse(t.responseText);
                            o(e);
                        } catch {
                            o(new Error(t.responseText || `HTTP Error ${t.status}`));
                        } else o(new Error(`HTTP Error ${t.status}`));
                    } catch (n) {
                        o(n);
                    }
                },
                onerror: t => {
                    console.error("网络错误:", e), o(new Error(t.error || "Network Error"));
                },
                ontimeout: () => {
                    o(new Error("Request Timeout"));
                }
            });
        }));
    }
}, window.storageManager = new E, window.seHuaTangStorageManager = new j;

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

window.refresh = function() {
    F.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, console.log("当前页", this.config.pagination.currentPage), 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 o = "#60A5FA", r = "#93C5FD", l = "#10B981", c = "#6EE7B7", d = "#EF4444", h = "#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
        }, g = {
            text: t,
            duration: 2e3,
            close: !1,
            gravity: "top",
            position: "center",
            style: {
                info: {
                    ...p,
                    background: `linear-gradient(to right, ${o}, ${r})`
                },
                success: {
                    ...p,
                    background: `linear-gradient(to right, ${l}, ${c})`
                },
                error: {
                    ...p,
                    background: `linear-gradient(to right, ${d}, ${h})`
                }
            }[e],
            stopOnFocus: !0,
            oldestFirst: !1,
            ...s
        };
        -1 === g.duration && (g.close = !0);
        const m = Toastify(g);
        return m.showToast(), m.closeShow = () => {
            m.toastElement.remove();
        }, m;
    };
    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)
    };
}(), document.head.insertAdjacentHTML("beforeend", "\n        <style>\n            .viewer-canvas {\n                overflow: auto !important;\n            }\n            \n            .viewer-close {\n                background: rgba(255,0,0,0.6) !important;\n            }\n            .viewer-close:hover {\n                background: rgba(255,0,0,0.8) !important;\n            }\n        </style>\n    "), 
window.showImageViewer = function(t, e = "") {
    let n = null, a = !1;
    "string" == typeof t || t instanceof String ? (n = $('<div class="temporary-container" style="display:none;">').append(`<img src="${t}" alt="${e}">`).appendTo("body"), 
    a = !0) : n = $(t);
    const i = {
        zIndex: 2147483647,
        navbar: !1,
        zoomOnWheel: !1,
        zoomRatio: .1,
        toggleOnDblclick: !1,
        toolbar: {
            zoomIn: 1,
            zoomOut: 1,
            reset: 1,
            rotateLeft: 0,
            rotateRight: 0,
            flipHorizontal: 0,
            flipVertical: 0
        },
        title: !1,
        keyboard: !1,
        viewed() {
            s.zoomTo(1.5);
            const t = (s.viewerData.width - s.imageData.width) / 2;
            s.moveTo(t, 0);
        },
        shown() {
            a && n.remove(), document.documentElement.style.overflow = "hidden", document.body.style.overflow = "hidden", 
            s.handleKeydown = function(t) {
                "Escape" !== t.key && " " !== t.key || (t.preventDefault(), s.destroy(), document.removeEventListener("keydown", s.handleKeydown), 
                document.documentElement.style.overflow = "", document.body.style.overflow = "");
            }, document.addEventListener("keydown", s.handleKeydown);
        },
        hidden() {
            s && s.handleKeydown && document.removeEventListener("keydown", s.handleKeydown), 
            s.destroy(), document.documentElement.style.overflow = "", document.body.style.overflow = "";
        }
    }, s = new Viewer(n[0], i);
    s.show();
}, window.ImageHoverPreview = class {
    constructor(t = {}) {
        this.config = {
            selector: ".hover-preview",
            dataAttribute: "data-full",
            maxWidth: 1e3,
            maxHeight: 1e3,
            offsetX: 20,
            offsetY: 20,
            zIndex: 9999999999,
            transition: .2,
            autoAdjustPosition: !0,
            ...t
        }, this.preview = null, this.currentTarget = null, this.timer = null, this.imgElement = null, 
        this.boundElements = new WeakSet, this.init();
    }
    init() {
        this.injectStyles(), this.createPreviewElement(), this.bindEvents();
    }
    injectStyles() {
        const t = `\n                <style>\n                    .image-hover-preview {\n                        position: fixed;\n                        display: none;\n                        z-index: ${this.config.zIndex};\n                        border-radius: 4px;\n                        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n                        overflow: hidden;\n                        pointer-events: none;\n                        opacity: 0;\n                        transition: opacity ${this.config.transition}s ease;\n                        background-color: #fff;\n                    }\n                    \n                    .image-hover-preview.active {\n                        opacity: 1;\n                    }\n                    \n                    .image-hover-preview img {\n                        max-width: ${this.config.maxWidth}px;\n                        max-height: ${this.config.maxHeight}px;\n                        display: block;\n                        object-fit: contain;\n                    }\n                    \n                    .image-hover-preview::after {\n                        content: '';\n                        position: absolute;\n                        top: 0;\n                        left: 0;\n                        right: 0;\n                        bottom: 0;\n                        background: rgba(0, 0, 0, 0.03);\n                        pointer-events: none;\n                    }\n                    \n                    .image-hover-preview.loading::before {\n                        content: '加载中...';\n                        position: absolute;\n                        top: 50%;\n                        left: 50%;\n                        transform: translate(-50%, -50%);\n                        color: #666;\n                        font-size: 14px;\n                    }\n                </style>\n            `;
        document.head.insertAdjacentHTML("beforeend", t);
    }
    createPreviewElement() {
        this.preview = document.createElement("div"), this.preview.className = "image-hover-preview", 
        document.body.appendChild(this.preview);
    }
    bindEvents() {
        document.querySelectorAll(this.config.selector).forEach((t => {
            this.boundElements.has(t) || (t.addEventListener("mouseenter", (t => this.handleMouseEnter(t))), 
            t.addEventListener("mouseleave", (t => this.handleMouseLeave(t))), t.addEventListener("mousemove", (t => this.handleMouseMove(t))), 
            this.boundElements.add(t));
        }));
    }
    handleMouseEnter(t) {
        clearTimeout(this.timer), this.currentTarget = t.currentTarget;
        const e = this.currentTarget.getAttribute(this.config.dataAttribute) || this.currentTarget.src;
        if (!e) return;
        this.preview.innerHTML = "", this.preview.classList.add("loading"), this.preview.style.display = "block", 
        this.preview.classList.remove("active");
        const n = new Image;
        n.onload = () => {
            this.preview.classList.remove("loading"), this.preview.innerHTML = `<img src="${e}" alt="预览图">`, 
            this.imgElement = this.preview.querySelector("img");
            const {width: a, height: i} = this.calculateImageSize(n);
            this.preview.style.width = `${a}px`, this.preview.style.height = `${i}px`, this.preview.offsetHeight, 
            this.preview.classList.add("active"), this.handleMouseMove(t);
        }, n.onerror = () => {
            this.preview.classList.remove("loading"), this.preview.innerHTML = '<div style="padding:10px;color:#f00;">图片加载失败</div>';
        }, n.src = e;
    }
    calculateImageSize(t) {
        let e = t.naturalWidth, n = t.naturalHeight;
        if (e > this.config.maxWidth || n > this.config.maxHeight) {
            const t = Math.min(this.config.maxWidth / e, this.config.maxHeight / n);
            e *= t, n *= t;
        }
        return {
            width: e,
            height: n
        };
    }
    handleMouseMove(t) {
        if (!this.currentTarget || !this.preview.classList.contains("active")) return;
        let {offsetX: e, offsetY: n} = this.config, a = t.clientX + e, i = t.clientY + n;
        if (this.config.autoAdjustPosition) {
            const s = this.preview.offsetWidth, o = this.preview.offsetHeight;
            a + s > window.innerWidth && (a = t.clientX - s - e), i + o > window.innerHeight && (i = t.clientY - o - n), 
            a = Math.max(0, a), i = Math.max(0, i);
        }
        this.preview.style.left = `${a}px`, this.preview.style.top = `${i}px`;
    }
    handleMouseLeave() {
        this.preview.classList.remove("active"), this.preview.style.display = "none", this.currentTarget = null, 
        this.imgElement = null;
    }
    destroy() {
        document.querySelectorAll(this.config.selector).forEach((t => {
            this.boundElements.has(t) && (t.removeEventListener("mouseenter", this.handleMouseEnter), 
            t.removeEventListener("mouseleave", this.handleMouseLeave), t.removeEventListener("mousemove", this.handleMouseMove), 
            this.boundElements.delete(t));
        })), this.preview && this.preview.parentNode && this.preview.parentNode.removeChild(this.preview);
    }
};

class z {
    constructor() {
        this.plugins = new Map;
    }
    register(t) {
        if ("function" != typeof t) throw new Error("插件必须是一个类");
        const e = new t;
        e.pluginManager = this;
        const n = e.getName().toLowerCase();
        if (this.plugins.has(n)) throw new Error(`插件"${name}"已注册`);
        this.plugins.set(n, e);
    }
    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 U {
    constructor() {
        r(this, "pluginManager", null);
    }
    getName() {
        throw new Error(`${this.constructor.name} 未显示getName()`);
    }
    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 h && (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 (h) 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: ".movie-box .photo-frame img",
            requestDomItemSelector: "#waterfall .item",
            nextPageSelector: "#next"
        };
        throw new Error("类型错误");
    }
    parseMovieId(t) {
        return t.split("/").pop().split(/[?#]/)[0];
    }
}

const O = async (t, e = "ja", n = "zh-CN") => {
    if (!t) throw new Error("翻译文本不能为空");
    const a = "https://translate-pa.googleapis.com/v1/translate?" + new URLSearchParams({
        "params.client": "gtx",
        dataTypes: "TRANSLATION",
        key: "AIzaSyDLEeFI5OtFBwYBIoK_jj5m32rZK5CkCXA",
        "query.sourceLanguage": e,
        "query.targetLanguage": n,
        "query.text": t
    }), i = await fetch(a);
    if (!i.ok) throw new Error(`${i.status} ${i.statusText}`);
    return (await i.json()).translation;
};

class R extends U {
    getName() {
        return "DetailPagePlugin";
    }
    constructor() {
        super();
    }
    async initCss() {
        return "\n            .translated-title {\n                margin-top: 5px;\n                color: #666;\n                font-size: 0.9em;\n                padding: 3px;\n                border-left: 3px solid #4CAF50;\n                background-color: #f8f8f8;\n            }\n        ";
    }
    handle() {
        window.isDetailPage && ($(".video-meta-panel a").each((function() {
            const t = $(this).attr("href");
            t && (t.startsWith("http://") || t.startsWith("https://") || t.startsWith("/")) && $(this).attr("target", "_blank");
        })), this.translate().then());
    }
    async translate() {
        if ("yes" !== await storageManager.getSetting("translateTitle", "yes")) return;
        let t = document.querySelector(".origin-title");
        t || (t = document.querySelector(".current-title"));
        const e = t.textContent.trim();
        if (!e) return void console.log(".current-title元素内容为空");
        const n = document.createElement("div");
        n.textContent = "翻译中...", n.className = "translated-title", t.parentNode.insertBefore(n, t.nextSibling);
        const a = this.getPageInfo().carNum, i = localStorage.getItem("jhs_translate") ? JSON.parse(localStorage.getItem("jhs_translate")) : {};
        i[a] ? n.textContent = i[a] : O(e, "ja", "zh-CN").then((t => {
            n.textContent = t;
        })).catch((t => {
            console.error("翻译失败:", t), n.textContent = "翻译失败: " + t.message, n.style.color = "red";
        }));
    }
}

const K = async t => {
    const e = localStorage.getItem("jhs_dmm_video") ? JSON.parse(localStorage.getItem("jhs_dmm_video")) : {};
    if (e[t]) return e[t];
    const n = `https://www.dmm.co.jp/search/=/searchstr=${t}`, a = await gmHttp.get(n, null, {
        cookie: "age_check_done=1"
    });
    if (a.includes("このサービスはお住まいの地域からは")) return show.error("节点不可用,请将域名 cc3001.dmm.co.jp 分流到日本ip"), 
    null;
    const i = $(a);
    let s = t.split("-")[1], o = i.find(`a[href*="litevideo/freepv"][href*="${s}/"]`).attr("href"), r = null;
    if (o) {
        let t = o.split("/");
        r = t[t.length - 2];
    }
    const l = i.find('a[href*="cid="]');
    if (!r && l.length > 0) {
        const t = l.attr("href").match(/cid=([^/&]+)/);
        r = t ? t[1] : null;
    }
    if (!r) {
        const t = show.error("解析cid失败, 该番号可能没有预览视频, 点击将前往此网址: " + n, {
            onClick: () => {
                t.closeShow(), window.open(n);
            }
        });
        return null;
    }
    const c = r.charAt(0);
    let d = r.substring(0, 3);
    const h = [ "hhb", "hmb", "mhb", "mmb" ], p = {};
    for (const u of h) p[u] = `https://cc3001.dmm.co.jp/litevideo/freepv/${c}/${d}/${r}/${r}${u}.mp4`;
    const g = {
        403: "节点不可用,请将域名 cc3001.dmm.co.jp 分流到日本ip",
        404: "无可用预览视频",
        null: "网络错误"
    };
    try {
        const n = Object.entries(p).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
        }))))), a = await Promise.all(n), i = {}, s = new Set;
        for (const {type: t, url: e, status: r} of a) if (200 === r) i[t] = e; else {
            const t = g[r] || `未知错误状态码: ${r}`;
            s.add(t);
        }
        let o = Object.keys(i).length;
        return 0 === o && s.size > 0 && s.forEach((t => show.error(t))), Object.values(i).some((t => "" !== t)) && (e[t] = i, 
        localStorage.setItem("jhs_dmm_video", JSON.stringify(e))), o > 0 ? i : null;
    } catch (m) {
        return console.error("并行检查URL时出错:", m), show.error(g.null), null;
    }
};

class W extends U {
    getName() {
        return "PreviewVideoPlugin";
    }
    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        ";
    }
    async handle() {
        if (!isDetailPage) return;
        let t = await storageManager.getSetting();
        this.filterHotKey = t.filterHotKey, this.favoriteHotKey = t.favoriteHotKey, this.speedVideoHotKey = t.speedVideoHotKey;
        let e = $(".preview-video-container");
        if (e.on("click", (t => {
            utils.loopDetector((() => $(".fancybox-content #preview-video").length > 0), (() => {
                this.handleVideo().then();
            }));
        })), e.length) {
            if ("yes" === await storageManager.getSetting("enableLoadPreviewVideo", "yes")) {
                let t = await storageManager.getSetting("videoQuality") || "hhb";
                K(this.getPageInfo().carNum).then((e => {
                    if (console.log("预加载结果:", e), e) {
                        e[t] || (t = Object.keys(e)[0]), console.log("画质", t);
                        let n = e[t];
                        $("#preview-video source").attr("src", n);
                    }
                }));
            }
        }
        let n = window.location.href;
        (n.includes("gallery-1") || n.includes("gallery-2")) && utils.loopDetector((() => $(".fancybox-content #preview-video").length > 0), (() => {
            $(".fancybox-content #preview-video").length > 0 && this.handleVideo().then();
        })), n.includes("autoPlay=1") && e[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], i = localStorage.getItem("jhs_videoMuted");
        i && (a.muted = "yes" === i), a.addEventListener("volumechange", (function() {
            localStorage.setItem("jhs_videoMuted", a.muted ? "yes" : "no");
        })), a.play();
        let s = this.getPageInfo().carNum;
        const o = await K(s);
        let r = "";
        const l = "-133";
        if (o) {
            let t = await storageManager.getSetting("videoQuality") || "hhb";
            o[t] || (t = Object.keys(o)[0]);
            let n = o[t];
            e.attr("src", n), a.load(), a.play();
            let i = 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 = o[e.quality];
                if (n) {
                    const a = t === e.quality;
                    r += `\n                    <button class="video-control-btn${a ? " active" : ""}" \n                            id="${e.id}" \n                            data-quality="${e.quality}"\n                            data-video-src="${n}"\n                            style="bottom: ${50 * i}px; right: ${l}px;">\n                        ${e.text}\n                    </button>\n                `, 
                    i++;
                }
            }));
        }
        let c = o ? Object.keys(o).length : 0;
        r = `<button class="menu-btn" id="speed-btn" style="position: absolute; min-width: 120px; background-color:#76b45d;bottom: ${50 * (c + 2)}px; right: ${l + "px"};">快进 ${this.speedVideoHotKey ? "(" + this.speedVideoHotKey + ")" : ""}</button>` + r, 
        r = `<button class="menu-btn" id="video-filterBtn" style="position: absolute; min-width: 120px; background-color:#de3333;bottom: ${50 * (c + 1)}px; right: ${l + "px"};">屏蔽 ${this.filterHotKey ? "(" + this.filterHotKey + ")" : ""}</button>` + r, 
        r = `<button class="menu-btn" id="video-favoriteBtn" style="position: absolute; min-width: 120px; background-color:#25b1dc;bottom: ${50 * c}px; right: ${l + "px"};">收藏 ${this.favoriteHotKey ? "(" + this.favoriteHotKey + ")" : ""}</button>` + r, 
        n.append(r);
        const d = 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(), await a.play(), d.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 q = class t {
    constructor() {
        if (new.target === t) 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"), o = 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() === o;
    }
};

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

let V = q;

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

class J extends U {
    getName() {
        return "JavTrailersPlugin";
    }
    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/" + encodeURIComponent(e) + window.location.search);
        }
        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 void (window.location.href = $(a).attr("href") + window.location.search);
        }
        this.handlePlayJavTrailers(), $("#videoPlayerContainer").on("click", (() => {
            this.handlePlayJavTrailers();
        })), window.addEventListener("message", (t => {
            let e = document.getElementById("vjs_video_3_html5_api");
            e && (e.currentTime += 5);
        }));
        const n = new URLSearchParams(window.location.search), a = n.get("filterHotKey"), i = n.get("favoriteHotKey"), s = n.get("speedVideoHotKey");
        a && V.registerHotkey(a, (() => window.parent.postMessage(a, "*"))), i && V.registerHotkey(i, (() => window.parent.postMessage(i, "*"))), 
        s && V.registerHotkey(s, (() => {
            const t = document.getElementById("vjs_video_3_html5_api");
            t && (t.currentTime += 5);
        }));
    }
    handlePlayJavTrailers() {
        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 G extends U {
    getName() {
        return "SubTitleCatPlugin";
    }
    handle() {
        $(".t-banner-inner").hide(), $("#navbar").hide();
        let t = new URLSearchParams(window.location.search).get("search").toLowerCase(), e = $(".sub-table tr td a").toArray(), n = 0;
        e.forEach((e => {
            let a = $(e);
            a.text().toLowerCase().includes(t) ? n++ : a.parent().parent().hide();
        })), 0 === n && show.error("该番号无字幕!");
        const a = $(".sec-title"), i = a.html().replace(/^\d+/, n);
        a.html(i);
    }
}

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

async function Y() {
    const t = "jhs_review_ts", e = "jhs_review_sign", n = Math.floor(Date.now() / 1e3);
    if (n - (localStorage.getItem(t) || 0) <= 20) return localStorage.getItem(e);
    const a = `${n}.lpw6vgqzsp.${md5(`${n}71cf27bb3c0bcdf207b64abecddc970098c7421ee7203b9cdae54478478a199e7d5a6e1a57691123c1a931c057842fb73ba3b3c83bcd69c17ccf174081e3d8aa`)}`;
    return localStorage.setItem(t, n), localStorage.setItem(e, a), a;
}

const X = async (t, e = 1, n = 20) => {
    let a = `${Q}/v1/movies/${t}/reviews`, i = {
        jdSignature: await Y()
    };
    return (await http.get(a, {
        page: e,
        sort_by: "hotly",
        limit: n
    }, i)).data.reviews;
}, Z = async t => {
    let e = `${Q}/v4/movies/${t}`, n = {
        jdSignature: await Y()
    };
    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, o = [];
    return s.forEach((t => {
        o.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: o
    };
}, tt = async (t, e = 1, n = 20) => {
    let a = `${Q}/v1/lists/related?movie_id=${t}&page=${e}&limit=${n}`, i = {
        jdSignature: await Y()
    };
    const s = await gmHttp.get(a, null, i, 3e3), o = [];
    return s.data.lists.forEach((t => {
        o.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)
        });
    })), o;
};

class et extends U {
    getName() {
        return "Fc2Plugin";
    }
    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:${b}"><span>${w}</span></a>\n                        <a id="favoriteBtn" class="menu-btn" style="background-color:${S}"><span>${x}</span></a>\n                        <a id="hasDownBtn" class="menu-btn" style="background-color:${_}"><span>${C}</span></a>\n                        <a id="hasWatchBtn" class="menu-btn" style="background-color:${B};"><span>${I}</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: utils.getDefaultArea(),
            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, u), window.refresh(), layer.closeAll();
                })), $("#filterBtn").on("click", (t => {
                    utils.q(t, `是否屏蔽${e}?`, (async () => {
                        const t = $("#data-actress").text();
                        await storageManager.saveCar(e, n, t, m), 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) {
        let n = e.replace("FC2-", "");
        this.handleVideo(n), this.handleMovieDetail(t), this.handleLongImg(n), this.handleMagnets(t);
        this.getBean("reviewPlugin").showReview(t, $("#reviews-content")).then(), this.getBean("RelatedPlugin").showRelated($("#related-content")).then();
    }
    handleMovieDetail(t) {
        Z(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            `);
        }));
    }
    handleLongImg(t) {
        utils.loopDetector((() => $(".movie-gallery .image-list").length > 0), (async () => {
            $(".movie-gallery .image-list").prepend(' <a class="tile-item screen-container" style="overflow:hidden;max-height: 150px;max-width:150px; text-align:center;"><div style="margin-top: 50px;color: #000;cursor: auto">正在加载缩略图</div></a> ');
            const e = await this.getBean("ScreenShotPlugin").getScreenshot(t);
            e && ($(".screen-container").html(`<img src="${e}" alt="" loading="lazy" style="width: 100%;">`), 
            $(".screen-container").on("click", (t => {
                t.stopPropagation(), t.preventDefault(), showImageViewer(t.currentTarget);
            })));
        }));
    }
    handleMagnets(t) {
        (async t => {
            let e = `${Q}/v1/movies/${t}/magnets`, n = {
                jdSignature: await Y()
            };
            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 o = 0; o < s.length; o++) {
                const a = $(s[o]);
                let i = a.find("img").attr("title");
                const r = a.find(".detail a").attr("href"), l = n + (r.startsWith("/") ? r : "/" + r);
                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 nt extends U {
    getName() {
        return "FoldCategoryPlugin";
    }
    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, o] = i ? [ "展开", "icon-angle-double-down" ] : [ "折叠", "icon-angle-double-up" ];
        a.find("span").text(s).end().find("i").attr("class", o), 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 at extends U {
    constructor() {
        super(...arguments), r(this, "apiUrl", "https://ja.wikipedia.org/wiki/");
    }
    getName() {
        return "ActressInfoPlugin";
    }
    async handle() {
        "yes" === await storageManager.getSetting("enableLoadActressInfo", "yes") && this.loadActressInfo();
    }
    loadActressInfo() {
        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() {
        if ($(".actress-info").length > 0) return;
        let t = $(".female").prev().map(((t, e) => $(e).text().trim())).get();
        if (!t.length) return;
        const e = "jhs_actress_info", n = localStorage.getItem(e) ? JSON.parse(localStorage.getItem(e)) : {};
        let a = null, i = "";
        for (let o = 0; o < t.length; o++) {
            let e = t[o];
            if (a = n[e], !a) try {
                a = await this.searchInfo(e), a && (n[e] = a);
            } catch (s) {
                console.error("该名称查询失败,尝试其它名称");
            }
            let r = "";
            r = a ? `\n                    <div class="panel-block actress-info">\n                        <strong>${e}:</strong>\n                        <a href="${a.url}" style="margin-left: 5px" target="_blank">\n                            <span class="info-tag">${a.birthday} ${a.age}</span>\n                            <span class="info-tag">${a.height} ${a.weight}</span>\n                            <span class="info-tag">${a.threeSizeText} ${a.braSize}</span>\n                        </a>\n                    </div>\n                ` : `<div class="panel-block actress-info"><a href="${this.apiUrl + e}" target="_blank"><strong>${e}:</strong></a></div> `, 
            i += r;
        }
        $('strong:contains("演員")').parent().after(i), localStorage.setItem(e, JSON.stringify(n));
    }
    async handleStarPage() {
        if ($(".actress-info").length > 0) return;
        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;
        const a = "jhs_actress_info", i = localStorage.getItem(a) ? JSON.parse(localStorage.getItem(a)) : {};
        let s = null;
        for (let l = 0; l < t.length; l++) {
            let e = t[l];
            if (s = i[e], s) break;
            try {
                s = await this.searchInfo(e);
            } catch (r) {
                console.error("该名称查询失败,尝试其它名称");
            }
            if (s) break;
        }
        s && t.forEach((t => {
            i[t] = s;
        }));
        let o = '<div class="actress-info" style="font-size: 17px; font-weight: normal; margin-top: 5px;">无此相关演员信息</div>';
        s && (o = `\n                <a class="actress-info" href="${s.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;">出生日期: ${s.birthday}</span>\n                            <span style="width: 200px;">年龄: ${s.age}</span>\n                            <span style="width: 200px;">身高: ${s.height}</span>\n                        </div>\n                        <div style="display: flex; margin-bottom: 10px;">\n                            <span style="width: 300px;">体重: ${s.weight}</span>\n                            <span style="width: 200px;">三围: ${s.threeSizeText}</span>\n                            <span style="width: 200px;">罩杯: ${s.braSize}</span>\n                        </div>\n                    </div>\n                </a>\n            `), 
        e.parent().append(o), localStorage.setItem(a, JSON.stringify(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(), o = i.find("th:contains('現年齢')").parent().find("td").text().trim() ? parseInt(i.find("th:contains('現年齢')").parent().find("td").text().trim()) + "岁" : "", r = 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: o,
            height: r,
            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 it extends U {
    getName() {
        return "AliyunPanPlugin";
    }
    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 st extends U {
    getName() {
        return "HitShowPlugin";
    }
    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 = `${Q}/v1/rankings/playback?period=${t}&filter_by=${e}`, a = {
                    jdSignature: await Y()
                };
                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 = [];
            let n = "jhs_score_info";
            for (const i of t) try {
                const t = i.id;
                if ($(`#${t}`).is(":hidden")) continue;
                const e = localStorage.getItem(n) ? JSON.parse(localStorage.getItem(n)) : {}, a = e[t];
                if (a) {
                    this.appendScoreHtml(t, a);
                    continue;
                }
                for (;!document.hasFocus(); ) await new Promise((t => setTimeout(t, 500)));
                const s = await Z(t);
                let o = s.score, r = s.watchedCount, l = `\n                        <span class="value">\n                            <span class="score-stars">${this.getStarRating(o)}</span> \n                            &nbsp; ${o}分,由${r}人評價\n                        </span>\n                    `;
                this.appendScoreHtml(t, l), e[t] = l, localStorage.setItem(n, JSON.stringify(e)), 
                await new Promise((t => setTimeout(t, 500)));
            } catch (a) {
                e.push({
                    carNum: i.number,
                    error: a.message,
                    stack: a.stack
                }), console.error(`🚨 解析评分数据失败 | 编号: ${i.number}\n`, `错误详情: ${a.message}\n`, a.stack ? `调用栈:\n${a.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 ot extends U {
    constructor() {
        super(), r(this, "has_cnsub", ""), r(this, "movies", []);
    }
    getName() {
        return "TOP250Plugin";
    }
    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 = `${Q}/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 Y()
                };
                return await gmHttp.get(i, null, s);
            })(e, n, a, 50);
            let s = t.success, o = t.message, r = 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>${o}</h3>`), show.error(o);
            "JWTVerificationError" === r && (await storageManager.removeItem("appAuthorization"), 
            await this.checkLogin(null, new URLSearchParams(window.location.search)));
        } catch (o) {
            console.error("获取Top数据失败:", o), show.error(`获取Top数据失败: ${o ? o.message : o}`);
        } 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 = `${Q}//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 Y()
                        };
                        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 rt extends U {
    getName() {
        return "NavBarPlugin";
    }
    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 lt {
    constructor() {
        this.queue = Promise.resolve();
    }
    addTask(t) {
        this.queue = this.queue.then((() => t())).catch((t => {
            console.error("执行异步任务失败:", t);
        }));
    }
}

class ct extends U {
    constructor() {
        super(...arguments), r(this, "okBackgroundColor", "#7bc73b"), r(this, "errorBackgroundColor", "#de3333"), 
        r(this, "timeout", "2000"), r(this, "retry", 10), r(this, "settingCache", null), 
        r(this, "lastFetchTime", 0), r(this, "CACHE_DURATION", 1e4);
    }
    getName() {
        return "OtherSitePlugin";
    }
    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() {
        isDetailPage && this.loadOtherSite().then();
    }
    async loadOtherSite() {
        if ("yes" !== await storageManager.getSetting("enableLoadOtherSite", "yes")) 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: "123AvBtn",
            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 id="otherSiteBox" class="panel-block" style="${h ? "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", (async t => {
            t.preventDefault();
            let n = await storageManager.getSetting();
            const a = n.filterHotKey, i = n.favoriteHotKey, s = n.speedVideoHotKey;
            let o = $("#javTrailersBtn").attr("href"), r = o + `?handle=1&filterHotKey=${a}&favoriteHotKey=${i}&speedVideoHotKey=${s}`;
            t && (t.ctrlKey || t.metaKey) && (r = o), utils.openPage(r, 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 = "jhs_other_site", i = localStorage.getItem(a) ? JSON.parse(localStorage.getItem(a)) : {}, s = t + "_" + e.id.replace("Btn", ""), o = i[s];
            if (o) return void ("single" === o.type ? (n.attr("href", o.url), n.css("backgroundColor", this.okBackgroundColor)) : "multiple" === o.type && (n.attr("href", o.url), 
            n.append('<span class="site-tag" style="top:-15px">多结果</span>'), n.css("backgroundColor", this.okBackgroundColor)));
            const r = await e.getUrl(), l = e.searchPath(r, t);
            n.attr("href", l);
            const c = await this.retryWithTimeout((() => gmHttp.get(l, null, null, this.timeout)), this.retry, e), d = utils.htmlTo$dom(c), h = [];
            d.find(e.boxSelector).each(((n, a) => {
                const i = $(a), s = e.getTitle(i).toLowerCase();
                if (!s.includes(t.toLowerCase())) return;
                let o = e.getHref(i);
                if (!o) throw console.log(i, s, o), new Error("解析href失败");
                o.includes("http") || (o = r + (o.startsWith("/") ? o : "/" + o)), h.push(o);
            }));
            let p = "", g = null;
            if (1 === h.length) {
                let t = h[0];
                n.attr("href", t), n.css("backgroundColor", this.okBackgroundColor), g = {
                    type: "single",
                    url: t
                };
            } else h.length > 1 ? (n.attr("href", l), p += '<span class="site-tag" style="top:-15px">多结果</span>', 
            n.css("backgroundColor", this.okBackgroundColor), g = {
                type: "multiple",
                urls: l
            }) : (n.attr("href", l), n.css("backgroundColor", this.errorBackgroundColor));
            g && (new lt).addTask((() => {
                const t = localStorage.getItem(a) ? JSON.parse(localStorage.getItem(a)) : {};
                t[s] = g, localStorage.setItem(a, JSON.stringify(t));
            })), p && n.append(p);
        } catch (a) {
            console.error("请求失败:", 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://www.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 dt extends U {
    getName() {
        return "BusDetailPagePlugin";
    }
    async initCss() {
        return window.isDetailPage ? ($("h4:contains('論壇熱帖')").hide(), $("h4:contains('同類影片')").hide(), 
        $("h4:contains('推薦')").hide(), "\n            .translated-title {\n                margin-top: 5px;\n                color: #666;\n                padding: 3px;\n                border-left: 3px solid #4CAF50;\n                background-color: #f8f8f8;\n            }\n        ") : "";
    }
    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").each((function() {
            const t = $(this).attr("href");
            t && (t.startsWith("http://") || t.startsWith("https://") || t.startsWith("/")) && $(this).attr("target", "_blank");
        })), this.translate().then();
    }
    async translate() {
        if (!isDetailPage) return;
        if ("yes" !== await storageManager.getSetting("translateTitle", "yes")) return;
        let t = document.querySelector("h3");
        const e = t.textContent.trim();
        if (!e) return void console.log(".current-title元素内容为空");
        const n = document.createElement("h4");
        n.textContent = "翻译中...", n.className = "translated-title", t.parentNode.insertBefore(n, t.nextSibling);
        const a = this.getPageInfo().carNum, i = localStorage.getItem("jhs_translate") ? JSON.parse(localStorage.getItem("jhs_translate")) : {};
        i[a] ? n.textContent = i[a] : O(e, "ja", "zh-CN").then((t => {
            n.textContent = t;
        })).catch((t => {
            console.error("翻译失败:", t), n.textContent = "翻译失败: " + t.message, n.style.color = "red";
        }));
    }
}

class ht extends U {
    getName() {
        return "DetailPageButtonPlugin";
    }
    constructor() {
        super(), this.answerCount = 1;
    }
    async handle() {
        let t = await storageManager.getSetting();
        this.filterHotKey = t.filterHotKey, this.favoriteHotKey = t.favoriteHotKey, this.hasDownHotKey = t.hasDownHotKey, 
        this.hasWatchHotKey = t.hasWatchHotKey, this.speedVideoHotKey = t.speedVideoHotKey, 
        this.bindHotkey().then(), this.hideVideoControls(), window.isDetailPage && this.createMenuBtn();
    }
    async createMenuBtn() {
        const t = this.getPageInfo(), e = t.carNum, n = `\n            <div style="margin: 10px auto; display: flex; justify-content: space-between; align-items: center; flex-wrap:wrap;gap: 20px;">\n                <div style="display: flex; gap: 10px; flex-wrap:wrap;">\n                    <a id="filterBtn" class="menu-btn" style="width: 120px; background-color:${b}; color: white; text-align: center; padding: 8px 0;">\n                        <span>${w}</span>\n                    </a>\n                    <a id="favoriteBtn" class="menu-btn" style="width: 120px; background-color:${S}; color: white; text-align: center; padding: 8px 0;">\n                        <span>${x}</span>\n                    </a>\n                    <a id="hasDownBtn" class="menu-btn" style="width: 120px; background-color:${_}; color: white; text-align: center; padding: 8px 0;">\n                        <span>${C}</span>\n                    </a>\n                    <a id="hasWatchBtn" class="menu-btn" style="width: 120px; background-color:${B}; color: white; text-align: center; padding: 8px 0;">\n                        <span>${I}</span>\n                    </a>\n                </div>\n        \n                <div style="display: flex; gap: 10px; flex-wrap:wrap;">\n                    <a id="enable-magnets-filter" class="menu-btn" style="width: 140px; background-color: #c2bd4c; color: white; text-align: center; padding: 8px 0;">\n                        <span id="magnets-span">关闭磁力过滤</span>\n                    </a>\n                    <a id="magnetSearchBtn" class="menu-btn" style="width: 120px; background: linear-gradient(to right, rgb(245,140,1), rgb(84,161,29)); color: white; text-align: center; padding: 8px 0;">\n                        <span>磁力搜索</span>\n                    </a>\n                    <a id="xunLeiSubtitleBtn" class="menu-btn" style="width: 120px; background: linear-gradient(to left, #375f7c, #2196F3); color: white; text-align: center; padding: 8px 0;">\n                        <span>字幕 (迅雷)</span>\n                    </a>\n                    <a id="search-subtitle-btn" class="menu-btn" style="width: 160px; background: linear-gradient(to bottom, #8d5656, rgb(196,159,91)); color: white; text-align: center; padding: 8px 0;">\n                        <span>字幕 (SubTitleCat)</span>\n                    </a>\n                </div>\n            </div>\n        `;
        h && $(".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 () => this.hasDownOne())), 
        $("#hasWatchBtn").on("click", (async () => this.hasWatchOne())), $("#magnetSearchBtn").on("click", (() => {
            let e = this.getBean("MagnetHubPlugin").createMagnetHub(t.carNum);
            layer.open({
                type: 1,
                title: "磁力搜索",
                content: '<div id="magnetHubBox"></div>',
                area: utils.getResponsiveArea([ "60%", "80%" ]),
                scrollbar: !1,
                success: () => {
                    $("#magnetHubBox").append(e);
                }
            });
        }));
        const a = this.getBean("HighlightMagnetPlugin"), i = await storageManager.getSetting("enableMagnetsFilter", D);
        $("#magnets-span").text(i === D ? "关闭磁力过滤" : "开启磁力过滤"), i === D && a.doFilterMagnet(), 
        $("#enable-magnets-filter").on("click", (t => {
            let e = $("#magnets-span");
            "关闭磁力过滤" === e.text() ? (a.showAll(), e.text("开启磁力过滤"), storageManager.saveSettingItem("enableMagnetsFilter", P)) : (a.doFilterMagnet(), 
            e.text("关闭磁力过滤"), storageManager.saveSettingItem("enableMagnetsFilter", D));
        })), $("#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"), s = t => t ? `(${t})` : "";
        e.text(`${w} ${s(this.filterHotKey)}`), n.text(`${x} ${s(this.favoriteHotKey)}`), 
        a.text(`${C} ${s(this.hasDownHotKey)}`), i.text(`${I} ${s(this.hasWatchHotKey)}`);
        const o = await storageManager.getCar(t);
        if (o) switch (o.status) {
          case m:
            e.text(`${y} ${s(this.filterHotKey)}`);
            break;

          case u:
            n.text(`${k} ${s(this.favoriteHotKey)}`);
            break;

          case f:
            a.text(`📥️ 已标记下载 ${s(this.hasDownHotKey)}`);
            break;

          case v:
            i.text(`🔍 已标记观看 ${s(this.hasWatchHotKey)}`);
        }
    }
    async favoriteOne() {
        let t = this.getPageInfo();
        await storageManager.saveCar(t.carNum, t.url, t.actress, u), this.showStatus(t.carNum).then(), 
        window.refresh(), utils.closePage();
    }
    async hasDownOne() {
        let t = this.getPageInfo();
        await storageManager.saveCar(t.carNum, t.url, t.actress, f), this.showStatus(t.carNum).then(), 
        window.refresh(), utils.closePage();
    }
    async hasWatchOne() {
        let t = this.getPageInfo();
        await storageManager.saveCar(t.carNum, t.url, t.actress, v), 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, m), this.showStatus(n.carNum).then(), 
        window.refresh(), utils.closePage(), layer.closeAll(), this.answerCount = 1) : utils.q(t, `是否屏蔽${n.carNum}?`, (async () => {
            await storageManager.saveCar(n.carNum, n.url, n.actress, m), 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);
        }));
    }
    async bindHotkey() {
        const t = {};
        this.filterHotKey && (t[this.filterHotKey] = () => {
            this.answerCount >= 2 ? this.filterOne(null, !0) : this.filterOne(null), this.answerCount++;
        }), this.favoriteHotKey && (t[this.favoriteHotKey] = () => this.favoriteOne(null)), 
        this.hasDownHotKey && (t[this.hasDownHotKey] = () => this.hasDownOne()), this.hasWatchHotKey && (t[this.hasWatchHotKey] = () => this.hasWatchOne()), 
        this.speedVideoHotKey && (t[this.speedVideoHotKey] = () => this.speedVideo());
        const e = (t, e) => {
            V.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 pt extends U {
    constructor() {
        super(...arguments), r(this, "dataType", "all"), r(this, "tableObj", null);
    }
    getName() {
        return "HistoryPlugin";
    }
    async initCss() {
        return "\n            .history-btn.active {\n                border: 2px solid #333;\n            }\n            \n            /* 下拉菜单容器(相对定位) */\n            .sub-btns {\n                position: relative;\n                display: inline-block;\n            }\n            \n            /* 下拉菜单内容(默认隐藏) */\n            .sub-btns-menu {\n                display: none;\n                position: absolute;\n                right: 80px;\n                top:-10px;\n                background: white;\n                padding:10px;\n                box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);\n                z-index: 100;\n                border-radius: 4px;\n                overflow: hidden;\n            }\n            \n            \n            /* 点击后显示菜单(JS 控制) */\n            .sub-btns-menu.show {\n                display: block;\n            }\n        ";
    }
    handle() {
        if (h) {
            let t = function() {
                $(".navbar-search").is(":hidden") ? ($(".historyBtnBox").show(), $(".miniHistoryBtnBox").hide()) : ($(".historyBtnBox").hide(), 
                $(".miniHistoryBtnBox").show());
            };
            $(".navbar-end").prepend('<div class="navbar-item has-sub-btns 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())), this.bindClick();
    }
    bindClick() {
        document.addEventListener("click", (function(t) {
            if (t.target.closest(".sub-btns-toggle")) {
                const e = t.target.closest(".sub-btns").querySelector(".sub-btns-menu");
                document.querySelectorAll(".sub-btns-menu.show").forEach((t => {
                    t !== e && t.classList.remove("show");
                })), e.classList.toggle("show");
            } else document.querySelectorAll(".sub-btns-menu.show").forEach((t => {
                t.classList.remove("show");
            }));
        })), this.getBean("ListPagePlugin"), $(document).on("click", ".histroy-deleteBtn, .histroy-filterBtn, .histroy-favoriteBtn, .histroy-hasDownBtn, .histroy-hasWatchBtn, .histroy-detailBtn", (t => {
            t.preventDefault(), t.stopPropagation();
            const e = $(t.currentTarget), n = e.closest(".action-btns"), a = n.attr("data-car-num"), i = n.attr("data-href"), s = async t => {
                await storageManager.saveCar(a, i, null, t), window.refresh(), await this.reloadTable(null);
            };
            e.hasClass("histroy-filterBtn") ? utils.q(t, `是否屏蔽${a}?`, (() => s(m))) : e.hasClass("histroy-favoriteBtn") ? s(u).then() : e.hasClass("histroy-hasDownBtn") ? s(f).then() : e.hasClass("histroy-hasWatchBtn") ? s(v).then() : e.hasClass("histroy-deleteBtn") ? this.handleDelete(t, a) : e.hasClass("histroy-detailBtn") && this.handleClickDetail(t, {
                carNum: a,
                url: i
            }).then();
        }));
    }
    openHistory() {
        let t = `\n            <div style="margin: 10px;display: flex;gap: 5px;">\n                <a class="menu-btn history-btn active" data-action="all" style="background-color:#d3c8a5">所有</a>\n                <a class="menu-btn history-btn" data-action="filter" style="background-color:${b}">${y}</a>\n                <a class="menu-btn history-btn" data-action="favorite" style="background-color:${S};">${k}</a>\n                <a class="menu-btn history-btn" data-action="hasDown" style="background-color:${_};">${C}</a>\n                <a class="menu-btn history-btn" data-action="hasWatch" style="background-color:${B};">${I}</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([ "70%", "90%" ]),
            success: async t => {
                const e = await this.getDataList();
                this.loadTableData(e), $(".layui-layer-content").on("click", ".history-btn", (async t => {
                    $(".history-btn").removeClass("active"), $(t.currentTarget).addClass("active"), 
                    this.dataType = $(t.target).data("action"), await this.reloadTable(1);
                })).on("click", "#clearSearchbtn", (async t => {
                    $("#searchCarNum").val(""), await this.reloadTable(1);
                })).on("keydown", "#searchCarNum", (async t => {
                    await this.reloadTable(1);
                }));
            },
            end: async () => window.refresh()
        });
    }
    async handleClickDetail(t, e) {
        if (h) 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(t) {
        const e = await this.getDataList();
        console.log("页码", t), this.tableObj.update(e, t);
    }
    handleDelete(t, e) {
        utils.q(t, `是否移除${e}?`, (async () => {
            await storageManager.removeCar(e), this.getBean("listPagePlugin").showCarNumBox(e), 
            this.reloadTable(null).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 m:
                this.filterCount++;
                break;

              case u:
                this.favoriteCount++;
                break;

              case f:
                this.hasDownCount++;
                break;

              case v:
                this.hasWatchCount++;
            }
        })), $('a[data-action="all"]').text(`所有 (${this.allCount})`), $('a[data-action="filter"]').text(`${y} (${this.filterCount})`), 
        $('a[data-action="favorite"]').text(`${k} (${this.favoriteCount})`), $('a[data-action="hasDown"]').text(`${C} (${this.hasDownCount})`), 
        $('a[data-action="hasWatch"]').text(`${I} (${this.hasWatchCount})`);
        const e = "all" === this.dataType ? t : t.filter((t => t.status === this.dataType)), n = $("#searchCarNum").val().trim();
        return n ? e.filter((t => {
            const e = t.carNum.toLowerCase().includes(n.toLowerCase());
            const a = (t.actress ? t.actress : "").toLowerCase().includes(n.toLowerCase());
            return e || a;
        })) : e;
    }
    loadTableData(t) {
        this.tableObj = new TableGenerator({
            containerId: "table-container",
            columns: [ {
                title: "序号",
                key: "_index",
                width: "70px"
            }, {
                key: "carNum",
                title: "番号"
            }, {
                key: "actress",
                title: "演员",
                width: "500px"
            }, {
                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 = b, n = y;
                        break;

                      case "favorite":
                        e = S, n = k;
                        break;

                      case "hasDown":
                        e = _, n = C;
                        break;

                      case "hasWatch":
                        e = B, n = I;
                    }
                    return `<span style="color:${e}">${n}</span>`;
                }
            }, {
                key: "change",
                title: "操作",
                render: t => `\n                            <div class="action-btns" style="display: flex; gap: 5px;justify-content:center" data-car-num="${t.carNum}" data-href="${t.url}">\n                                <div class="sub-btns">\n                                    <button class="menu-btn sub-btns-toggle" style="background-color:#c59d36; color:white; margin-bottom: 5px;">\n                                        <span>✏️ 变更</span>\n                                    </button>\n                                    <div class="sub-btns-menu">\n                                        <a class="menu-btn histroy-deleteBtn" style="background-color:#c63b3b; color:white; margin-bottom: 5px;"> <span>✂️ 移除</span> </a>\n                                        <a class="menu-btn histroy-hasWatchBtn" style="background-color:${B};margin-bottom: 5px">${I}</a>\n                                        <a class="menu-btn histroy-hasDownBtn" style="background-color:${_};margin-bottom: 5px">${C}</a>\n                                        <a class="menu-btn histroy-favoriteBtn" style="background-color:${S};margin-bottom: 5px">${x}</a>\n                                        <a class="menu-btn histroy-filterBtn" style="background-color:${b};margin-bottom: 5px">${w}</a>\n                                    </div>\n                                </div>\n                                \n                                <a class="menu-btn histroy-detailBtn" style="background-color:#3397de; color:white; margin-bottom: 5px;"> <span>📄 详情页</span> </a>\n                                \n                            </div>\n                        `
            } ],
            data: t,
            pagination: {
                enable: !0,
                pageSize: 10,
                pageSizeOptions: [ 10, 20, 50, 100 ],
                currentPage: 1,
                showTotal: !0,
                showSizeChanger: !0,
                showQuickJumper: !0
            }
        });
    }
}

class gt extends U {
    constructor() {
        super(...arguments), r(this, "floorIndex", 1);
    }
    getName() {
        return "ReviewPlugin";
    }
    async handle() {
        if (window.isDetailPage) {
            if (h) {
                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 = `${Q}/v2/search`, n = {
                        "user-agent": "Dart/3.5 (dart:io)",
                        "accept-language": "zh-TW",
                        host: "jdforrepam.com",
                        jdsignature: await Y()
                    }, 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;
                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 X(t, 1, a);
        } catch (o) {
            console.error("获取评论失败:", o);
        } 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, r = $("#loadMoreReviews");
            r.on("click", (async () => {
                let n;
                r.text("加载中...").prop("disabled", !0), i++;
                try {
                    n = await X(t, i, a);
                } catch (o) {
                    console.error("加载更多评论失败:", o);
                } finally {
                    r.text("加载失败, 请点击重试").prop("disabled", !1);
                }
                n && (this.displayReviews(n, e, s), n.length < a ? (r.remove(), $("#reviewsEnd").show()) : r.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 mt extends U {
    getName() {
        return "FilterTitleKeywordPlugin";
    }
    handle() {
        if (!window.isDetailPage) return;
        let t;
        h && (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 ut extends U {
    getName() {
        return "ListPageButtonPlugin";
    }
    handle() {
        window.isListPage && this.createMenuBtn();
    }
    createMenuBtn() {
        if (h) {
            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                '), 
            g || $(".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                '), 
            g || $(".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 (g) 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 = [ y, k, C, I ];
        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: o, aHref: r, title: l} = this.getBean("ListPagePlugin").findCarNumAndHref(s);
            if (o.includes("FC2-")) {
                const t = this.parseMovieId(r);
                this.getBean("Fc2Plugin").openFc2Page(t, o, r);
            } else {
                let t = r + (r.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 === u));
        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 ft extends U {
    constructor() {
        super(...arguments), r(this, "noDataCount", 1), r(this, "cache", localStorage.getItem("jhs_translate") ? JSON.parse(localStorage.getItem("jhs_translate")) : {}), 
        r(this, "writeQueue", Promise.resolve());
    }
    getName() {
        return "ListPagePlugin";
    }
    async handle() {
        this.cleanRepeatId(), this.replaceHdImg(), this.fixBusTitleBox(), 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.fixBusTitleBox(), 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);
    }
    fixBusTitleBox() {
        if (!p) return;
        $(this.getSelector().itemSelector).toArray().forEach((t => {
            var e;
            let n = $(t);
            if (n.find(".avatar-box").length > 0) return;
            const a = (null == (e = n.find("img").attr("title")) ? void 0 : e.trim()) || "";
            n.find(".photo-info span:first").contents().first().wrap(`<span class="video-title" title="${a}">${a}</span>`), 
            n.find("br").remove();
        }));
    }
    async showNoItem() {
        if (await storageManager.getSetting("autoPage") === D && $(this.getSelector().itemSelector).length > 0) {
            0 === $(this.getSelector().itemSelector).filter((function() {
                return "none" !== $(this).css("display");
            })).length ? this.noDataCount += 1 : this.noDataCount = 1, this.noDataCount >= 10 && (show.error("连续翻页均为空白页, 为防无限翻页, 已终止翻页操作, 请手动确认或重新开启自动翻页"), 
            storageManager.saveSettingItem("autoPage", P).then());
        }
    }
    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);
    }
    async filterMovieList(t) {
        const e = await storageManager.getCarList(), n = await storageManager.getTitleFilterKeyword(), a = e.filter((t => t.status === m)).map((t => t.carNum)), i = e.filter((t => t.status === u)).map((t => t.carNum)), s = e.filter((t => t.status === f)).map((t => t.carNum)), o = e.filter((t => t.status === v)).map((t => t.carNum)), r = await storageManager.getActorFilterCarMap(), l = await storageManager.getActressFilterCarMap(), c = Object.values(l).flatMap((t => t.map((t => t.carNum)))), d = Object.values(r).flatMap((t => t.map((t => t.carNum))));
        let w = await storageManager.getSetting("showFilterItem", P), x = await storageManager.getSetting("showFavoriteItem", D), M = await storageManager.getSetting("showHasDownItem", D), A = await storageManager.getSetting("showHasWatchItem", D);
        t.forEach((t => {
            let e = $(t);
            if (p && e.find(".avatar-box").length > 0) return;
            const {carNum: r, title: l} = this.findCarNumAndHref(e);
            if (!g) {
                const t = w === P && (a.includes(r) || n.some((t => l.includes(t) || r.includes(t))) || d.includes(r) || c.includes(r)) && !i.includes(r) && !s.includes(r) && !o.includes(r), h = x === P && i.includes(r) || M === P && s.includes(r) || A === P && o.includes(r) || t;
                e.attr("data-hide") === D ? h || e.show().removeAttr("data-hide") : h && e.hide().attr("data-hide", D);
            }
            let m = "", u = "";
            if (a.includes(r) ? (m = y, u = b) : i.includes(r) ? (m = k, u = S) : s.includes(r) ? (m = C, 
            u = _) : o.includes(r) ? (m = I, u = B) : n.some((t => l.includes(t) || r.includes(t))) ? (m = "关键词屏蔽", 
            u = b) : d.includes(r) ? (m = "男演员屏蔽", u = b) : c.includes(r) && (m = "女演员屏蔽", u = b), 
            e.find(".status-tag").remove(), m && (h && 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: ${u} !important;">\n                        ${m}\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: ${u} !important;"><span class="tag">${m}</span></a>`;
                e.find(".item-tag").append(t);
            }
            this.translate(e);
        })), $("#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", D);
            if (n.includes("FC2-")) {
                let t = this.parseMovieId(a);
                this.getBean("fc2Plugin").openFc2Dialog(t, n, a);
            } else i === D ? 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, "", m), window.refresh(), show.ok("操作成功");
            }));
        }));
    }
    findCarNumAndHref(t) {
        let e, n, a = t.find("a").attr("href");
        if (h) {
            e = t.find(".video-title").find("strong").text().trim(), n = t.find(".video-title").text().trim();
        }
        if (p && (e = a.split("/").filter(Boolean).pop().trim(), n = t.find("img").attr("title").trim()), 
        !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)), h && t.forEach((t => {
            t.src = t.src.replace("thumbs", "covers"), t.title = "";
        })), 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.dataset.title = t.title, t.title = "");
            };
            t.forEach((t => {
                a(t);
            }));
        }
        storageManager.getSetting("hoverBigImg", "yes").then((t => {
            "yes" === t && (window.imageHoverPreviewObj ? window.imageHoverPreviewObj.bindEvents() : window.imageHoverPreviewObj = new ImageHoverPreview({
                selector: this.getSelector().coverImgSelector
            }));
        }));
    }
    async translate(t) {
        if (await storageManager.getSetting("translateTitle", D) !== D) return;
        let e, n, a = t.find(".video-title");
        if (h ? (e = a.contents().filter(((t, e) => 3 === e.nodeType && "" !== e.textContent.trim())).text().trim(), 
        n = t.find(".video-title strong").text().trim()) : (e = t.find("img").attr("data-title").trim(), 
        n = t.find("a").attr("href").split("/").filter(Boolean).pop().trim()), this.cache[n]) {
            let t = this;
            h ? a.contents().each((function() {
                3 !== this.nodeType || "" === this.textContent.trim() || this.textContent.includes(n) || (this.textContent = " " + t.cache[n] + " ");
            })) : a.text(this.cache[n]);
        } else O(e).then((t => {
            h ? a.contents().each((function() {
                3 !== this.nodeType || "" === this.textContent.trim() || this.textContent.includes(n) || (this.textContent = " " + t + " ");
            })) : a.text(t), this.writeQueue = this.writeQueue.then((() => {
                this.cache[n] = t, localStorage.setItem("jhs_translate", JSON.stringify(this.cache));
            }));
        })).catch((t => {
            console.error("翻译失败:", t);
        }));
    }
    async revertTranslation() {
        $(this.getSelector().itemSelector).toArray().forEach((t => {
            let e = $(t);
            const n = e.find(".box").attr("title") || e.find(".video-title").attr("title") || e.find("img").attr("data-title");
            let a;
            h && (a = e.find(".video-title strong").text().trim());
            const i = e.find(".video-title");
            h ? (i.contents().each((function() {
                3 !== this.nodeType || "" === this.textContent.trim() || this.textContent.includes(a) || (this.textContent = " " + n + " ");
            })), a && !i.find("strong").length && i.prepend(`<strong>${a}</strong>`)) : i.text(n);
        }));
    }
}

class vt extends U {
    constructor() {
        super(...arguments), r(this, "preloadDistance", 500);
    }
    getName() {
        return "AutoPagePlugin";
    }
    async initCss() {
        return "\n            <style>\n                .jhs-scroll {\n                    text-align: center;\n                    padding-top: 20px;\n                    font-size: 14px;\n                }\n                .jhs-scroll.waterfall-loading { color: #000; }\n                .jhs-scroll.waterfall-error { color: #f44336; cursor: pointer; }\n                .jhs-scroll.waterfall-no-more { color: #4CAF50; }\n            </style>\n        ";
    }
    async handle() {
        this.waterfall().then();
    }
    async waterfall() {
        if (this.shouldDisablePaging()) return;
        const t = this.getSelector();
        this.container = document.querySelector(t.boxSelector), this.loader = document.createElement("div"), 
        this.loader.className = "jhs-scroll", this.container.parentNode.insertBefore(this.loader, this.container.nextSibling), 
        this.loader.addEventListener("click", (() => {
            this.loader.classList.contains("waterfall-error") && this.loadNextPage().then();
        })), window.addEventListener("scroll", (() => {
            this.checkLoad();
        }));
        const e = document.querySelector(t.nextPageSelector);
        this.nextUrl = null == e ? void 0 : e.href, this.hasMore = !!this.nextUrl, setTimeout((() => {
            this.checkLoad();
        }), 1e3), this.hasMore || this.setState("waterfall-no-more", "已经到底了");
    }
    async loadNextPage() {
        var t;
        if (await storageManager.getSetting("autoPage", D) === P) return void this.setState("waterfall-loading", "");
        if (this.isLoading || !this.nextUrl) return;
        this.isLoading = !0, this.setState("waterfall-loading", "加载中...");
        const e = this.getSelector();
        try {
            console.log("请求下一页内容:", this.nextUrl);
            const n = await gmHttp.get(this.nextUrl), a = (new DOMParser).parseFromString(n, "text/html");
            p && $(a).find(".avatar-box").length > 0 && $(a).find(".avatar-box").parent().remove();
            let i = a.querySelectorAll(this.getSelector().requestDomItemSelector);
            const s = this.getBean("listPagePlugin");
            await s.filterMovieList(i);
            let o = a.querySelectorAll(this.getSelector().coverImgSelector);
            s.replaceHdImg(o), $(this.getSelector().boxSelector).append(i), this.updatePageUrl(this.nextUrl), 
            this.nextUrl = null == (t = a.querySelector(e.nextPageSelector)) ? void 0 : t.href, 
            this.hasMore = !!this.nextUrl;
            let r = a.querySelectorAll(".pagination");
            $(".pagination").replaceWith(r), this.setState("waterfall-loading", ""), this.hasMore || this.setState("waterfall-no-more", "已经到底了");
        } catch (n) {
            console.error("加载失败:", n), this.setState("waterfall-error", "加载失败,点击重试");
        } finally {
            this.isLoading = !1, this.checkLoad();
        }
    }
    checkLoad() {
        this.loader.getBoundingClientRect().top < window.innerHeight + this.preloadDistance && this.loadNextPage();
    }
    shouldDisablePaging() {
        if (!window.isListPage) return !0;
        return [ "search?q", "handlePlayback=1", "handleTop=1", "/want_watch_videos", "/watched_videos", "/advanced_search?type=100" ].some((t => window.location.href.includes(t)));
    }
    updatePageUrl(t) {
        if (window.history.pushState({}, "", t), p) {
            const e = t.match(/\/(page|star\/.*?)\/(\d+)/), n = e ? parseInt(e[2], 10) : null;
            document.title = document.title.replace(/第\d+頁/, "第" + n + "頁");
        }
    }
    setState(t, e) {
        this.loader.className = `jhs-scroll ${t}`, this.loader.textContent = e;
    }
}

class wt extends U {
    getName() {
        return "HighlightMagnetPlugin";
    }
    doFilterMagnet() {
        this.handleDb(), this.handleBus();
    }
    handleDb() {
        if (!h) return;
        let t = $("#magnets-content .name");
        if (0 === t.length) return;
        const e = [ "4k", "-c", "-u", "-uc" ];
        let n = !1;
        t.each(((t, a) => {
            const i = $(a), s = i.text().toLowerCase(), o = e.some((t => s.includes(t)));
            i.parent().parent().parent().addClass("magnet-row"), s.includes("4k") && i.css("color", "#f40"), 
            o && (n = !0, i.parent().parent().parent().addClass("high-quality"));
        })), console.log(n), n ? $("#magnets-content .magnet-row").not(".high-quality").hide() : $("#enable-magnets-filter").addClass("do-hide");
    }
    handleBus() {
        p && isDetailPage && utils.loopDetector((() => $("#magnet-table td a").length > 0), (() => {
            const t = $("#magnet-table tr"), e = [ "4k", "-c", "-u", "-uc" ];
            let n = !1;
            t.each(((t, a) => {
                const i = $(a), s = i.find("td:first-child"), o = s.find("a:first-child"), r = s.find("a:nth-child(2)"), l = o.text().toLowerCase();
                l.includes("4k") && o.css("color", "#f40");
                (e.some((t => l.includes(t))) || r.length && r.text().includes("字幕")) && (n = !0, 
                i.addClass("high-quality"));
            })), n ? t.each(((t, e) => {
                const n = $(e);
                n.hasClass("high-quality") || n.hide();
            })) : $("#enable-magnets-filter").addClass("do-hide");
        }));
    }
    showAll() {
        if (h) {
            $("#magnets-content .item").toArray().forEach((t => $(t).show()));
        }
        p && $("#magnet-table tr").toArray().forEach((t => $(t).show()));
    }
}

class yt {
    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 o = await this.getHeaders(), r = await gmHttp.post(i, s, o), l = r.upload_id, c = r.file_id, d = r.part_info_list[0].upload_url;
        console.log("创建完成: ", r), await this._doUpload(d, n);
        const h = await gmHttp.post("https://api.aliyundrive.com/v2/file/complete", s = {
            drive_id: a,
            file_id: c,
            upload_id: l
        }, o);
        console.log("标记完成:", h);
    }
    _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 bt {
    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 o = this.davUrl + e, r = {
                ...this._getAuthHeaders(),
                ...n
            };
            GM_xmlhttpRequest({
                method: t,
                url: o,
                headers: r,
                data: a,
                onload: t => {
                    t.status >= 200 && t.status < 300 ? i(t) : (console.error(t), s(new Error(`请求失败 ${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"
        }, '<?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 o = 0; o < i.length; o++) {
            if (0 === o) continue;
            let t = i[o];
            const a = t.getElementsByTagNameNS("DAV:", "displayname")[0].textContent, r = (null == (e = t.getElementsByTagNameNS("DAV:", "getcontentlength")[0]) ? void 0 : e.textContent) || "0", l = (null == (n = t.getElementsByTagNameNS("DAV:", "creationdate")[0]) ? void 0 : n.textContent) || "";
            "0" !== r && s.push({
                fileId: a,
                name: a,
                size: Number(r),
                createTime: l
            });
        }
        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 xt extends U {
    constructor() {
        super(...arguments), l(this, a), r(this, "folderName", "JHS-数据备份"), r(this, "cacheItems", [ {
            key: "jhs_dmm_video",
            text: "🎥 预览视频缓存",
            title: "预览视频缓存"
        }, {
            key: "jhs_other_site",
            text: "🌍 第三方站点缓存",
            title: "第三方站点资源检测结果, 如missav,123Av等"
        }, {
            key: "jhs_screenShot",
            text: "🖼️ 缩略图缓存",
            title: "缩略图缓存"
        }, {
            key: "jhs_translate",
            text: "🆎 标题翻译",
            title: "标题翻译"
        }, {
            key: "jhs_actress_info",
            text: "👩 演员信息",
            title: "演员的年龄三围等数据信息"
        }, {
            key: "jhs_score_info",
            text: "⭐ Top250|热播 评分数据",
            title: "Top250及热播的评分数据"
        } ]);
    }
    getName() {
        return "SettingPlugin";
    }
    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());
        this.applyImageMode(e);
        let 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                .container {\n                    max-width: 1000px !important;\n                    min-width: 80%;\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                #cache-data-display pre {\n                    font-family: Consolas, Monaco, 'Andale Mono', monospace;\n                    white-space: pre-wrap;\n                    word-wrap: break-word;\n                    line-height: 1.5;\n                    color: #333;\n                    border: 1px solid #ddd;\n                }\n                \n                .cache-item {\n                    transition: all 0.2s ease;\n                }\n                .cache-item:hover {\n                    box-shadow: 0 2px 8px rgba(0,0,0,0.1);\n                    transform: translateY(-2px);\n                }\n\n                .tooltip-icon {\n                    display: inline-block;\n                    width: 16px;\n                    height: 16px;\n                    line-height: 16px;\n                    text-align: center;\n                    border-radius: 50%;\n                    background-color: #ccc;\n                    color: white;\n                    font-size: 12px;\n                    margin-right: 5px;\n                    cursor: help;\n                }\n                .setting-item {\n                    display: flex;\n                    align-items: baseline;\n                    justify-content: space-between;\n                    margin-bottom: 10px;\n                    padding: 5px;\n                    /*border: 1px solid #ddd;\n                    border-radius: 5px;*/\n                }\n                .simple-setting .setting-item{\n                    align-items:center;\n                }\n                .setting-label {\n                    font-size: 14px;\n                    min-width: 240px;\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,#helpBtn,#clean-all {\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: #5cb85c;\n                    color: white;\n                }\n                #moreBtn:hover {\n                    background-color: #4cae4c;\n                }\n                #helpBtn {\n                    background-color: #e67e22;\n                    color: white;\n                }\n                #helpBtn:hover {\n                    background-color: #d35400;\n                }\n                .simple-setting, .mini-simple-setting {\n                    display: none; /* 默认隐藏 */\n                    background: rgba(255,255,255,1); \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                \n                .side-menu-item {\n                    padding: 12px 12px;\n                    cursor: pointer;\n                    color: #333;\n                    border-left: 3px solid transparent;\n                    transition: all 0.2s;\n                }\n                \n                .side-menu-item:hover {\n                    background-color: #e9e9e9;\n                }\n                \n                .side-menu-item.active {\n                    background-color: #e0e0e0;\n                    border-left: 3px solid #5d87c2;\n                    font-weight: bold;\n                }\n                \n                .content-panel {\n                    display: none;\n                    margin-top:20px;\n                }\n                \n                .content-panel.active {\n                    display: block;\n                }\n            </style\n        `;
    }
    async handle() {
        if (h) {
            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           `);
        const t = await storageManager.getActressFilterCarMap(), e = await storageManager.getActorFilterCarMap(), n = Object.values(t).reduce(((t, e) => t + e.length), 0), a = Object.values(e).reduce(((t, e) => t + e.length), 0), i = this.cacheItems.map((t => `\n            <div class="cache-item" style="border: 1px solid #eee; border-radius: 8px; padding: 12px;">\n                <div style="font-weight: bold; margin-bottom: 8px;">${t.text}</div>\n                <div style="display: flex; gap: 8px;">\n                    <a class="menu-btn clean-btn" data-key="${t.key}" style="background-color:#448cc2; flex:1; text-align:center;" title="${t.title}">\n                        <span>清理</span>\n                    </a>\n                    <a class="menu-btn view-btn" data-key="${t.key}" style="background-color:#b2bec0; flex:1; text-align:center;" >\n                        <span>查看</span>\n                    </a>\n                </div>\n            </div>\n        `)).join("");
        let s = `\n            <div style="display: flex; height: 100%;">\n                <div style="width: 140px; flex-shrink: 0; padding: 15px 0; background: #f5f5f5; border-right: 1px solid #ddd;">\n                    <div class="side-menu-item active" data-panel="backup-panel">💾 数据备份</div>\n                    <div class="side-menu-item" data-panel="video-panel">⚙️ 基础配置</div>\n                    <div class="side-menu-item" data-panel="filter-panel">🚫 屏蔽演员</div>\n                    <div class="side-menu-item" data-panel="domain-panel">🌐 域名设置</div>\n                    <div class="side-menu-item" data-panel="hotkey-panel">⌨️ 快捷键配置</div>\n                    <div class="side-menu-item" data-panel="netdisk115-panel">☁️ 115网盘</div>\n                    <div class="side-menu-item" data-panel="cache-panel">🧹 清理缓存</div>\n                </div>\n        \n                \x3c!-- 右侧内容区域 --\x3e\n                <div style="flex: 1; display: flex; flex-direction: column; height: 100%; overflow: hidden;">\n                    \x3c!-- 内容面板容器 --\x3e\n                    <div style="flex: 1; margin: 0 20px; padding-bottom: 20px; overflow-x: hidden; overflow-y: auto;">\n                        \x3c!-- 阿里云盘面板 --\x3e\n                        <div id="backup-panel" class="content-panel" style="display: block;">\n                            <div style="margin-bottom: 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                            </div>\n                            \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                            <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: 1px; margin:20px 0;background-image: linear-gradient(to right, rgba(0,0,0,0), rgba(159,137,137,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                            <div class="setting-item">\n                                <span class="setting-label">服务地址:</span>\n                                <div class="form-content">\n                                    <input id="webDavUrl">\n                                </div>\n                            </div>\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                            <div class="setting-item">\n                                <span class="setting-label">密码:</span>\n                                <div class="form-content">\n                                    <input id="webDavPassword">\n                                </div>\n                            </div>\n                        </div>\n                        \n                        \x3c!-- 115网盘面板 --\x3e\n                        <div id="netdisk115-panel" class="content-panel" style="display: none;">\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                            <h1 style="padding-left: 5px">115相关功能, 待开发...</h1>\n                        </div>\n                        \n                        \x3c!-- 视频设置面板 --\x3e\n                        <div id="video-panel" class="content-panel" style="display: none;">\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                        </div>\n                        \n                        \x3c!-- 域名设置面板 --\x3e\n                        <div id="domain-panel" class="content-panel" style="display: none;">\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                            <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                            <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                        </div>\n                         \n                         \x3c!-- 快捷键 --\x3e\n                        <div id="hotkey-panel" class="content-panel" style="display: none;">\n                            <div class="setting-item">\n                                <span class="setting-label">${w}:</span>\n                                <div class="form-content">\n                                    <input id="filterHotKey" placeholder="录入快捷键" data-default-hotkey="a">\n                                </div>\n                            </div>\n                            <div class="setting-item">\n                                <span class="setting-label">${x}:</span>\n                                <div class="form-content">\n                                    <input id="favoriteHotKey" placeholder="录入快捷键" data-default-hotkey="s">\n                                </div>\n                            </div>\n                            <div class="setting-item">\n                                <span class="setting-label">${C}:</span>\n                                <div class="form-content">\n                                    <input id="hasDownHotKey" placeholder="录入快捷键">\n                                </div>\n                            </div>\n                            <div class="setting-item">\n                                <span class="setting-label">${I}:</span>\n                                <div class="form-content">\n                                    <input id="hasWatchHotKey" placeholder="录入快捷键">\n                                </div>\n                            </div>\n                            <div class="setting-item">\n                                <span class="setting-label">⏩ 快进:</span>\n                                <div class="form-content">\n                                    <input id="speedVideoHotKey" placeholder="录入快捷键" data-default-hotkey="z">\n                                </div>\n                            </div>\n                        </div>\n                        \n                        \x3c!-- 屏蔽设置面板 --\x3e\n                        <div id="filter-panel" class="content-panel" style="display: none;">\n                            <div class="setting-item">\n                                <span class="setting-label">评论区屏蔽词:</span>\n                                <div id="reviewKeywordContainer" style="width:100%">\n                                    <div class="tag-box"></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                            <hr style="border: 0; height: 1px; margin:20px 0;background-image: linear-gradient(to right, rgba(0,0,0,0), rgba(159,137,137,0.75), rgba(0,0,0,0));"/>\n                             \n                            <div class="setting-item">\n                                <span class="setting-label">视频标题屏蔽词:</span>\n                                <div id="filterKeywordContainer" style="width:100%">\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                            <hr style="border: 0; height: 1px; margin:20px 0;background-image: linear-gradient(to right, rgba(0,0,0,0), rgba(159,137,137,0.75), rgba(0,0,0,0));"/>\n                            \n                            <div class="setting-item">\n                                <span class="setting-label">屏蔽男演员(番号总屏蔽数-${a}):</span>\n                                <div id="filterActorContainer" style="width:100%">\n                                    <div class="tag-box"></div>\n                                </div>\n                            </div>\n                            \n                            <hr style="border: 0; height: 1px; margin:20px 0;background-image: linear-gradient(to right, rgba(0,0,0,0), rgba(159,137,137,0.75), rgba(0,0,0,0));"/>\n                            \n                            <div class="setting-item">\n                                <span class="setting-label">屏蔽女演员(番号总屏蔽数-${n}):</span>\n                                <div id="filterActressContainer" style="width:100%">\n                                    <div class="tag-box"></div>\n                                </div>\n                            </div>\n                        </div>\n                        \n                         \x3c!-- 清理缓存 --\x3e\n                        <div id="cache-panel" class="content-panel" style="display: none;">\n                            <h1 style="text-align:center;font-size: 20px;font-weight: bold">以下操作, 不会对核心数据造成影响</h1>\n                            <br/>               \n                            <div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 15px; margin-top: 20px;">\n                                ${i}\n                            </div>    \n                            <div id="cache-data-display" style="margin-top: 20px; display: none;">\n                                <pre style="background: #f5f5f5; padding: 10px; border-radius: 5px; max-height: 400px; overflow: auto;"></pre>\n                            </div>\n                        </div>\n                    </div>\n                    \n                    \x3c!-- 底部保存按钮 --\x3e\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                        <button id="clean-all" style="display: none">♾️ 清理全部缓存</button>\n                    </div>\n                </div>\n            </div>\n        `;
        $(".main-nav, .top-bar").on("click", "#setting-btn, #mini-setting-btn", (() => {
            layer.open({
                type: 1,
                title: "设置",
                content: s,
                area: utils.getResponsiveArea([ "55%", "90%" ]),
                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; margin: 0 10px; ">\n                    <div class="setting-item">\n                        <span class="setting-label">\n                            显示已鉴定内容:\n                        </span>\n                        <div class="form-content">\n                            <span style="display:inline-block; width: 65px; font-size:13px; font-weight:bold; text-align: left">屏蔽: </span><input type="checkbox" id="showFilterItem" class="mini-switch"><br/>\n                            <span style="display:inline-block; width: 65px; font-size:13px; font-weight:bold; text-align: left">收藏: </span><input type="checkbox" id="showFavoriteItem" class="mini-switch"><br/>\n                            <span style="display:inline-block; width: 65px; font-size:13px; font-weight:bold; text-align: left">已下载: </span><input type="checkbox" id="showHasDownItem" class="mini-switch"><br/>\n                            <span style="display:inline-block; width: 65px; font-size:13px; font-weight:bold; text-align: left">已观看: </span><input type="checkbox" id="showHasWatchItem" class="mini-switch"><br/>\n                        </div>\n                    </div>\n                    \n                    <div class="setting-item">\n                        <span class="setting-label">\n                            <span data-tip="点击封面的打开方式,弹窗|新窗口">❓ </span>弹窗方式打开页面:\n                        </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">\n                             瀑布流模式:\n                        </span>\n                        <div class="form-content">\n                            <input type="checkbox" id="autoPage" 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="translateTitle" 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="hoverBigImg" class="mini-switch">\n                        </div>\n                    </div>\n                    \n                    ${h ? '\n                    <div class="setting-item">\n                        <span class="setting-label">\n                            <span data-tip="详情页是否展示女优年龄、三围等信息">❓ </span>加载女优信息:\n                        </span>\n                        <div class="form-content">\n                            <input type="checkbox" id="enableLoadActressInfo" class="mini-switch">\n                        </div>\n                    </div>' : ""}\n                    \n                    <div class="setting-item">\n                        <span class="setting-label">\n                            <span data-tip="详情页第三方资源检测,如missAv,123AV">❓ </span>加载第三方视频资源:\n                        </span>\n                        <div class="form-content">\n                            <input type="checkbox" id="enableLoadOtherSite" class="mini-switch">\n                        </div>\n                    </div>\n                    \n                    <div class="setting-item">\n                        <span class="setting-label">\n                            <span data-tip="详情页图片区首列位置加载长缩略图">❓ </span>加载长缩略图:\n                        </span>\n                        <div class="form-content">\n                            <input type="checkbox" id="enableLoadScreenShot" class="mini-switch">\n                        </div>\n                    </div>\n                    \n                     <div class="setting-item">\n                        <span class="setting-label">\n                            <span data-tip="详情页提前加载是否有其它画质的预览视频">❓ </span>详情页预加载预览视频:\n                        </span>\n                        <div class="form-content">\n                            <input type="checkbox" id="enableLoadPreviewVideo" class="mini-switch">\n                        </div>\n                    </div>\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="helpBtn" style="float:left;">常见问题</button>\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(), o = await e.getAv123Url(), r = await e.getJavDbUrl(), l = await e.getJavBusUrl();
        $("#missAvUrl").val(n), $("#jableUrl").val(a), $("#avgleUrl").val(i), $("#javTrailersUrl").val(s), 
        $("#av123Url").val(o), $("#javDbUrl").val(r), $("#javBusUrl").val(l);
        const c = await storageManager.getSetting(storageManager.filter_actor_actress_info_list_key, []), d = t => t.sort(((t, e) => {
            var n, a;
            const i = null == (n = c.find((e => e.key === t))) ? void 0 : n.recordTime, s = null == (a = c.find((t => t.key === e))) ? void 0 : a.recordTime;
            if (!i) return -1;
            if (!s) return 1;
            return new Date(i) - new Date(s);
        })), h = await storageManager.getActressFilterCarMap();
        d(Object.keys(h)).forEach((t => {
            var e;
            const n = h[t].length, a = t.split("_").filter(Boolean).pop(), i = null == (e = c.find((e => e.key === t))) ? void 0 : e.url;
            this.addLabelTag("#filterActressContainer", `${a} (${n})`, `当前已屏蔽数量:${n}`, t, i);
        }));
        const p = await storageManager.getActorFilterCarMap();
        d(Object.keys(p)).forEach((t => {
            var e;
            const n = p[t].length, a = t.split("_").filter(Boolean).pop(), i = null == (e = c.find((e => e.key === t))) ? void 0 : e.url;
            this.addLabelTag("#filterActorContainer", `${a} (${n})`, `当前已屏蔽数量:${n}`, t, i);
        }));
        let g = await storageManager.getReviewFilterKeywordList(), m = await storageManager.getTitleFilterKeyword();
        g && g.forEach((t => {
            this.addLabelTag("#reviewKeywordContainer", t);
        })), m && m.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);
            }));
        })), $("#hotkey-panel [id]").map(((t, e) => e.id)).get().forEach((e => {
            const n = $(`#${e}`), a = void 0 !== t[e] ? t[e] : n.attr("data-default-hotkey") || "";
            n.val(a).on("input", (t => {
                let e = $(t.target).val();
                (/[\u4e00-\u9fa5]/.test(e) || /^Shift[a-zA-Z0-9]+$/.test(e)) && ($(t.target).val(""), 
                show.error("非法输入:不能输入中文或输入法转换错误"));
            })).on("keydown", (t => this.handleHotkeyInput(t, n)));
        }));
    }
    handleHotkeyInput(t, e) {
        t.preventDefault();
        const n = this.parseHotkey(t);
        "" !== n ? this.isDuplicateHotkey(n, e.attr("id")) ? show.error("该快捷键已被其他功能使用!") : e.val(n) : e.val("");
    }
    parseHotkey(t) {
        if ("Backspace" === t.key || "Process" === t.key) return "";
        const e = [];
        t.ctrlKey && e.push("Ctrl"), t.shiftKey && e.push("Shift"), t.altKey && e.push("Alt"), 
        t.metaKey && e.push("Cmd");
        const n = {
            " ": "Space",
            Control: "Ctrl",
            Meta: "Cmd",
            ArrowUp: "Up",
            ArrowDown: "Down",
            ArrowLeft: "Left",
            ArrowRight: "Right"
        }[t.key] || (t.key.length > 1 ? t.key.replace("Arrow", "") : t.key);
        return [ "Control", "Shift", "Alt", "Meta" ].includes(t.key) || e.push(n), e.length > 0 ? e.join("+") : "";
    }
    isDuplicateHotkey(t, e) {
        let n = !1;
        return $("#hotkey-panel [id]").each(((a, i) => {
            if (i.id !== e && t && t === $(i).val()) return n = !0, !1;
        })), n;
    }
    async initSimpleSettingForm() {
        let t = await storageManager.getSetting();
        $("#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), $("#translateTitle").prop("checked", !t.translateTitle || "yes" === t.translateTitle), 
        $("#enableLoadActressInfo").prop("checked", !t.enableLoadActressInfo || "yes" === t.enableLoadActressInfo), 
        $("#enableLoadOtherSite").prop("checked", !t.enableLoadOtherSite || "yes" === t.enableLoadOtherSite), 
        $("#containerColumns").on("input", (t => {
            let e = $("#containerColumns").val();
            if ($("#showContainerColumns").text(e), h) {
                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), this.applyImageMode(parseInt(e));
        })), $("#containerWidth").on("input", (t => {
            let e = parseInt($(t.target).val());
            const n = e + 70 + "%";
            if ($("#showContainerWidth").text(n), h) {
                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);
        })), $("#showFilterItem").prop("checked", !!t.showFilterItem && "yes" === t.showFilterItem), 
        $("#showFavoriteItem").prop("checked", !t.showFavoriteItem || "yes" === t.showFavoriteItem), 
        $("#showHasDownItem").prop("checked", !t.showHasDownItem || "yes" === t.showHasDownItem), 
        $("#showHasWatchItem").prop("checked", !t.showHasWatchItem || "yes" === t.showHasWatchItem), 
        $("#showFilterItem").on("change", (t => {
            let e = $("#showFilterItem").is(":checked") ? "yes" : "no";
            storageManager.saveSettingItem("showFilterItem", e), window.refresh();
        })), $("#showFavoriteItem").on("change", (t => {
            let e = $("#showFavoriteItem").is(":checked") ? "yes" : "no";
            storageManager.saveSettingItem("showFavoriteItem", e), window.refresh();
        })), $("#showHasDownItem").on("change", (t => {
            let e = $("#showHasDownItem").is(":checked") ? "yes" : "no";
            storageManager.saveSettingItem("showHasDownItem", e), window.refresh();
        })), $("#showHasWatchItem").on("change", (t => {
            let e = $("#showHasWatchItem").is(":checked") ? "yes" : "no";
            storageManager.saveSettingItem("showHasWatchItem", e), window.refresh();
        })), $("#needClosePage").on("change", (t => {
            storageManager.saveSettingItem("needClosePage", $("#needClosePage").is(":checked") ? "yes" : "no"), 
            window.refresh();
        })), $("#autoPage").on("change", (async t => {
            this.getBean("ListPagePlugin").noDataCount = 1;
            const e = $("#autoPage").is(":checked") ? "yes" : "no";
            await storageManager.saveSettingItem("autoPage", e);
        })), $("#translateTitle").on("change", (async t => {
            const e = $("#translateTitle").is(":checked") ? "yes" : "no";
            await storageManager.saveSettingItem("translateTitle", e), "yes" === e ? (await this.getBean("ListPagePlugin").doFilter(), 
            h ? await this.getBean("DetailPagePlugin").translate() : await this.getBean("BusDetailPagePlugin").translate()) : (await this.getBean("ListPagePlugin").revertTranslation(), 
            $(".translated-title").remove());
        })), $("#hoverBigImg").prop("checked", !t.hoverBigImg || "yes" === t.hoverBigImg), 
        $("#hoverBigImg").on("change", (async t => {
            const e = $("#hoverBigImg").is(":checked") ? "yes" : "no";
            await storageManager.saveSettingItem("hoverBigImg", e), "yes" === e ? window.imageHoverPreviewObj = new ImageHoverPreview({
                selector: this.getSelector().coverImgSelector
            }) : window.imageHoverPreviewObj && window.imageHoverPreviewObj.destroy();
        })), $("#enableLoadActressInfo").on("change", (async t => {
            const e = $("#enableLoadActressInfo").is(":checked") ? "yes" : "no";
            await storageManager.saveSettingItem("enableLoadActressInfo", e), "yes" === e ? this.getBean("ActressInfoPlugin").loadActressInfo() : $(".actress-info").remove();
        })), $("#enableLoadOtherSite").on("change", (async t => {
            const e = $("#enableLoadOtherSite").is(":checked") ? "yes" : "no";
            await storageManager.saveSettingItem("enableLoadOtherSite", e), "yes" === e ? this.getBean("OtherSitePlugin").loadOtherSite().then() : $("#otherSiteBox").remove();
        })), $("#enableLoadScreenShot").prop("checked", !t.enableLoadScreenShot || "yes" === t.enableLoadScreenShot), 
        $("#enableLoadScreenShot").on("change", (async t => {
            const e = $("#enableLoadScreenShot").is(":checked") ? "yes" : "no";
            await storageManager.saveSettingItem("enableLoadScreenShot", e), "yes" === e ? this.getBean("ScreenShotPlugin").loadScreenShot().then() : $(".screen-container").remove();
        })), $("#enableLoadPreviewVideo").prop("checked", !t.enableLoadPreviewVideo || "yes" === t.enableLoadPreviewVideo), 
        $("#enableLoadPreviewVideo").on("change", (async t => {
            const e = $("#enableLoadPreviewVideo").is(":checked") ? "yes" : "no";
            await storageManager.saveSettingItem("enableLoadPreviewVideo", e);
        })), $("#moreBtn").on("click", (() => {
            $(".simple-setting").html("").hide(), $("#setting-btn")[0].click();
        })), $("#helpBtn").on("click", (() => {
            layer.open({
                type: 1,
                title: "",
                shadeClose: !0,
                scrollbar: !1,
                content: '\n<style>\n    .help-container {\n        font-family: \'Segoe UI\', Tahoma, Geneva, Verdana, sans-serif;\n        color: #333;\n        padding: 15px;\n        max-height: 100%;\n        overflow-y: auto;\n    }\n    \n    .help-section {\n        margin-bottom: 25px;\n    }\n    \n    .help-section h1 {\n        font-size: 18px;\n        color: #3498db;\n        margin-bottom: 12px;\n    }\n    \n    .help-content {\n        background-color: #f9f9f9;\n        border-radius: 5px;\n        padding: 15px;\n        border-left: 4px solid #3498db;\n    }\n    \n    .help-content p {\n        line-height: 1.6;\n        margin-bottom: 10px;\n    }\n    .help-section img {\n        max-width: 100%;\n        height: auto;\n        border: 1px solid #ddd;\n        border-radius: 4px;\n        box-shadow: 0 2px 4px rgba(0,0,0,0.1);\n    }\n</style>\n\n<div class="help-container">\n    <h1 style="font-size: 22px; margin-bottom: 20px; color: #2c3e50; border-bottom: 1px solid #eee; padding-bottom: 10px;">使用说明</h1>\n    \n    <div class="help-section">\n        <h1>1. 无法查看预览视频,提示分流</h1>\n        <div class="help-content">\n            <p>JavDB限制日本IP的访问,而预览视频来自DMM,需要日本IP才能访问。</p>\n            <p>这样会导致二者无法同时使用,需要对其一进行代理转发。</p>\n            <p>将 cc3001.dmm.co.jp 及 dmm.co 分流到日本ip。</p>\n            <p><a href="https://youtu.be/wQUK8z_YeU4?t=121" target="_blank">Clash Verge分流规则设置 </a> (如果你是别的代理软件,自行搜索如何分流)</p>\n        </div>\n    </div>\n    \n    <div class="help-section">\n        <h1>2. 如何屏蔽某一系列的番号</h1>\n        <div class="help-content">\n            <p>方法一:设置中-添加视频标题关键词,如: VENX-</p>\n            <p>方法二:进入详情页,选中标题文字,右键可加入</p>\n            <img src="https://i.imgur.com/lVnhK5A.png" alt="进入详情页,选中标题,进行右键"/>\n        </div>\n    </div>\n\n    <div class="help-section">\n        <h1>3. 屏蔽某演员,如何只屏蔽单体影片</h1>\n        <div class="help-content">\n            <p>屏蔽演员前,先筛选分类,再点屏蔽</p>\n            <img src="https://i.imgur.com/nr3Dwb8.png" alt="屏蔽演员前,先筛选分类,再点屏蔽"/>\n        </div>\n    </div>\n</div>\n',
                area: utils.getResponsiveArea([ "50%", "90%" ])
            });
        }));
    }
    applyImageMode(t) {
        if ($("#verticalImgStyle").remove(), t >= 6) {
            let t = "100% 50% !important";
            window.location.href.includes("/advanced_search?type=100") && (t = "50% 50% !important");
            const e = `\n                .cover {\n                    min-height: 350px !important;\n                    overflow: hidden !important;\n                }\n                \n                .cover img {\n                    object-fit: cover !important;\n                    object-position: ${t};\n                }\n                \n                /* bus的 */\n                .masonry .movie-box img {\n                    min-height: 300px;\n                    object-fit: cover !important;\n                    object-position: top right;\n                }\n            `;
            $("<style>").attr("id", "verticalImgStyle").text(e).appendTo("head");
        } else {
            const t = "\n                .cover {\n                    min-height:auto !important;\n                }\n                .cover img {\n                    object-fit: contain !important;\n                    object-position: 50% 50% !important\n                }\n                \n                /* bus的 */\n                 .masonry .movie-box img {\n                    min-height:auto !important;\n                    object-fit: contain !important;\n                    object-position: top;\n                }\n            ";
            $("<style>").attr("id", "verticalImgStyle").text(t).appendTo("head");
        }
    }
    bindClick() {
        $(".side-menu-item").on("click", (function() {
            $(".side-menu-item").removeClass("active"), $(this).addClass("active"), $(".content-panel").hide();
            const t = $(this).data("panel");
            $("#" + t).show(), "cache-panel" === t ? ($("#saveBtn").hide(), $("#clean-all").show()) : ($("#saveBtn").show(), 
            $("#clean-all").hide());
        })), $("#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())), $(".clean-btn").on("click", (t => {
            const e = $(t.currentTarget).data("key"), n = this.cacheItems.find((t => t.key === e));
            localStorage.removeItem(e), show.ok(`${n.text} 清理成功`), $("#cache-data-display").hide();
        })), $("#clean-all").on("click", (() => {
            this.cacheItems.forEach((t => localStorage.removeItem(t.key))), show.ok("全部缓存已清理"), 
            $("#cache-data-display").hide();
        })), $(".view-btn").on("click", (t => {
            const e = $(t.currentTarget).data("key"), n = localStorage.getItem(e), a = $("#cache-data-display"), i = a.find("pre");
            if (a.show(), n) try {
                const t = JSON.parse(n);
                i.text(JSON.stringify(t, null, 2));
            } catch {
                i.text(n);
            } else i.text("无数据");
        }));
    }
    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 = $("#javDbUrl").val(), t.javBusUrl = $("#javBusUrl").val(), 
        t.cookie115 = $("#cookie115").val(), $("#hotkey-panel [id]").map(((t, e) => e.id)).get().forEach((e => {
            t[e] = $(`#${e}`).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, i) {
        const s = $(`${t} .tag-box`);
        let o = "div", r = "#c9a561";
        i && (o = "a"), i && i.includes("?") && (r = "#c5b9a0", n = "该屏蔽为分类过滤型, " + n);
        const l = $(`\n            <${o} class="keyword-label" data-keyword="${e}" data-key="${a}" style="background-color: ${r}" title="${n || ""}" href="${i}" target="_blank">\n                ${e}\n                <span class="keyword-remove">×</span>\n            </${o}>\n        `);
        l.find(".keyword-remove").click((t => {
            t.stopPropagation(), t.preventDefault();
            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);
                    const t = (await storageManager.getSetting(storageManager.filter_actor_actress_info_list_key, [])).filter((t => t.key !== i));
                    await storageManager.saveSettingItem(storageManager.filter_actor_actress_info_list_key, t), 
                    e.parent().remove();
                }));
            } else e.parent().remove();
        })), s.append(l);
    }
    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 = kt(a);
        let i = loading();
        try {
            const t = new yt(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 yt(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", o = JSON.stringify(await storageManager.exportData());
        o = kt(o);
        let r = loading();
        try {
            const t = new bt(n, a, i);
            await t.backup(this.folderName, s, o), show.ok("备份完成");
        } catch (l) {
            console.error(l), show.error(l.toString());
        } finally {
            r.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 bt(n, a, i), e = await t.getBackupList(this.folderName);
            this.openFileListDialog(e, t, "WebDav");
        } catch (o) {
            console.error(o), show.error(`发生错误: ${o ? o.message : o}`);
        } 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 (o) {
                                    console.error(o), show.error(`发生错误: ${o ? o.message : o}`);
                                } 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 = St(e), utils.download(e, t.name);
                                    }));
                                })).catch((t => {
                                    console.error(t), show.error("下载失败: " + t);
                                })) : e.getFileContent(t.fileId).then((e => {
                                    e = St(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 = St(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");
        h && (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, o = 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 r = () => {
                if (o >= 8) return clearInterval(s), console.log("超过最大重试次数,停止尝试"), void show.error("合并失败, 目标网站已中断, 请检查是否登录后再试!", {
                    close: !0,
                    duration: -1
                });
                console.log(`第 ${o + 1} 次ping...`), t.postMessage("ping", e), o++;
            };
            s = setInterval(r, 1e3), r();
        }));
    }
}

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() || [], o = await storageManager.getTitleFilterKeyword() || [], r = 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([ ...o, ...n ]) ], h = [ ...new Set([ ...r, ...a ]) ], p = {
            ...l
        };
        Object.keys(i).forEach((t => {
            t === storageManager.filter_actor_actress_info_list_key && Array.isArray(i[t]) ? p[t] && Array.isArray(p[t]) && 0 !== p[t].length ? p[t] = [ ...new Set([ ...p[t], ...i[t] ]) ] : p[t] = [ ...i[t] ] : t in p && p[t] || (p[t] = i[t]);
        })), await storageManager.overrideCarList(c), await storageManager.saveTitleFilterKeyword(d), 
        await storageManager.saveReviewFilterKeyword(h), await storageManager.saveSetting(p);
        const g = await storageManager.getActressFilterCarMap(), m = await storageManager.getActorFilterCarMap(), u = {
            ...g,
            ...m
        };
        for (const f of Object.keys(t)) if (f.startsWith("car_list_")) {
            let e = [];
            u[f] && u[f].length > 0 ? (e = [ ...u[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 $t = "x7k9p3";

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

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

class Ct extends U {
    getName() {
        return "SyncDataPlugin";
    }
    async handle() {
        if (!window.location.href.includes("syncData=1")) return;
        p && $("h4").html("临时页面, 用于合并数据");
        let t = null;
        const e = this.getBean("OtherSitePlugin");
        h && (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(), o = await storageManager.getActorFilterCarMap();
                e.source.postMessage({
                    carList: t,
                    titleFilterKeyword: n,
                    reviewFilterKeyword: a,
                    setting: i,
                    ...s,
                    ...o
                }, e.origin), show.ok("数据已传输, 即将关闭页面...", {
                    callback: () => {
                        window.close();
                    }
                });
            }
        }));
    }
}

class _t extends U {
    getName() {
        return "BusPreviewVideoPlugin";
    }
    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        ";
    }
    async 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);
        "yes" === await storageManager.getSetting("enableLoadPreviewVideo", "yes") && K(this.getPageInfo().carNum).then();
        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 K(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 o = a[0], r = localStorage.getItem("jhs_videoMuted");
        r && (o.muted = "yes" === r), o.addEventListener("volumechange", (function() {
            localStorage.setItem("jhs_videoMuted", o.muted ? "yes" : "no");
        })), o.play();
        let l = "";
        [ {
            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;
                l += `\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(l);
        const c = 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), o.load(), await o.play(), c.removeClass("active"), e.addClass("active");
            } catch (e) {
                show.error("切换画质失败"), console.error("切换画质失败:", e);
            }
        }));
    }
}

class It extends U {
    constructor() {
        super(...arguments), r(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"
        } ]), r(this, "isUploading", !1);
    }
    getName() {
        return "SearchByImagePlugin";
    }
    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"), o = $("#handle-btn"), r = $("#cancel-btn"), l = $("#url-input-container"), c = $("#image-url"), d = $("#search-results"), h = $("#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());
        })), o.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(), h.empty();
                        const n = "jhs_selectedSites", a = JSON.parse(localStorage.getItem(n) || "{}");
                        this.siteList.forEach((t => {
                            const n = t.url.replace("{占位符}", encodeURIComponent(e)), i = !1 !== a[t.name];
                            h.append(`\n                        <a href="${n}" 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                    `);
                        })), h.on("change", ".site-checkbox", (function() {
                            const t = $(this).data("site-name");
                            a[t] = $(this).is(":checked"), localStorage.setItem(n, JSON.stringify(a));
                        })), h.show();
                    } finally {
                        this.isUploading = !1;
                    }
                }
            } else show.info("请粘贴或上传图片");
        })), r.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), o = new Array(s.length);
                    for (let p = 0; p < s.length; p++) o[p] = s.charCodeAt(p);
                    const r = new Uint8Array(o), l = new Blob([ r ], {
                        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
                    }), h = await d.json();
                    if (h.success && h.data && h.data.link) return h.data.link;
                    throw new Error((null == (e = h.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 Bt extends U {
    getName() {
        return "BusNavBarPlugin";
    }
    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 Pt extends U {
    constructor() {
        super(...arguments), r(this, "floorIndex", 1), r(this, "isInit", !1);
    }
    getName() {
        return "RelatedPlugin";
    }
    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 tt(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 tt(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 Dt extends U {
    constructor() {
        super(...arguments), r(this, "type", null);
    }
    getName() {
        return "WantAndWatchedVideosPlugin";
    }
    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 = u, 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 Mt extends U {
    constructor() {
        super(...arguments), r(this, "currentImageIndex", 0), r(this, "currentImageGroup", []), 
        r(this, "processedArticles", new Set);
    }
    getName() {
        return "SeHuaTangPlugin";
    }
    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, m), 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, u), 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: o} = this.getInfo(a), r = t.find((t => t.articleId === i));
            r && r.status === u && a.find(".common em a").text("已收藏").css("color", "#14e097"), 
            r && r.status === m && (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 At extends U {
    constructor() {
        super(...arguments), r(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>'), 
        r(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>'), 
        r(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>'), 
        r(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>'), 
        r(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>'), 
        r(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>'), 
        r(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>'), 
        r(this, "screenSvg", '<svg t="1750691468062" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2693" width="200" height="200"><path d="M288 160a64 64 0 0 0-64 64v576a64 64 0 0 0 64 64h448a64 64 0 0 0 64-64v-576a64 64 0 0 0-64-64h-448m0-64h448a128 128 0 0 1 128 128v576a128 128 0 0 1-128 128h-448a128 128 0 0 1-128-128v-576a128 128 0 0 1 128-128z" fill="#4078FD" p-id="2694"></path><path d="M416 352m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0Z" fill="#FE9C23" p-id="2695"></path><path d="M352 732.448a32 32 0 0 1-32-32v-160a32 32 0 0 1 44.224-29.568l130.112 53.632 153.952-169.984a32 32 0 0 1 55.712 21.472v284.448a32 32 0 0 1-32 32z m0-32h320z" fill="#4078FD" opacity=".2" p-id="2696"></path><path d="M672 416l-169.088 186.656-150.912-62.208v160h320V416m0-32a32 32 0 0 1 32 32v284.448a32 32 0 0 1-32 32h-320a32 32 0 0 1-32-32v-160a32 32 0 0 1 44.192-29.6l130.112 53.632 153.984-169.984a32 32 0 0 1 23.712-10.496z" fill="#4078FD" p-id="2697"></path></svg>'), 
        r(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>');
    }
    getName() {
        return "CopyTitleOrDownImgPlugin";
    }
    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) && (h && e.find(".tags").append(`\n                    <div class="tool-box" style="margin-left: auto; display: flex; align-items: center">\n                        <span class="screenSvg" title="长缩略图" style="margin-right: 15px;">${this.screenSvg}</span>\n                        \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 !important;margin-bottom: 5px"><span style="opacity: 1;">${I}</span></a>\n                                <a class="menu-btn hasDownBtn" style="background-color:${_}; color:white !important;margin-bottom: 5px"><span style="opacity: 1;">${C}</span></a>\n                                <a class="menu-btn favoriteBtn" style="background-color:${S}; color:white !important;margin-bottom: 5px"><span style="opacity: 1;">${x}</span></a>\n                                <a class="menu-btn filterBtn" style="background-color:${b};   color:white !important;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 !important;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 !important;margin-bottom: 5px;background-color:#71bb59;">\n                                    <span style="opacity: 1;">MissAv</span>\n                                </a>\n                                <a class="site-btn site-123-av" style="color:white !important;margin-bottom: 5px;background-color:#71bb59;">\n                                    <span style="opacity: 1;">123Av</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="screenSvg" title="长缩略图" style="margin-right: 15px;">${this.screenSvg}</span>\n\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; color:white !important">${I}</span></a>\n                                <a class="menu-btn hasDownBtn" style="background-color:${_}; color:white;margin-bottom: 5px"><span style="opacity: 1;display: inline; color:white !important">${C}</span></a>\n                                <a class="menu-btn favoriteBtn" style="background-color:${S}; color:white;margin-bottom: 5px"><span style="opacity: 1;display: inline; color:white !important">${x}</span></a>\n                                <a class="menu-btn filterBtn" style="background-color:${b};   color:white;margin-bottom: 5px"><span style="opacity: 1;display: inline; color:white !important">${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; color:white !important">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; color:white !important">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; color:white !important">MissAv</span>\n                                </a>\n                                <a class="site-btn site-123-av" style="color:white;margin-bottom: 5px;background-color:#71bb59;">\n                                    <span style="opacity: 1;display: inline; color:white !important">123Av</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"), o = s.find(t.coverImgSelector), {carNum: r} = e.findCarNumAndHref(s);
                this.showImg(i, o, r), 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", ".screenSvg", (async t => {
            t.preventDefault();
            let n = loading();
            try {
                const a = $(t.currentTarget).closest(".item");
                let {carNum: i, aHref: s, title: o} = e.findCarNumAndHref(a);
                i = i.replace("FC2-", "");
                const r = await this.getBean("ScreenShotPlugin").getScreenshot(i);
                n.close(), showImageViewer(r);
            } catch (a) {
                console.error("图片预览出错:", a), show.error("图片预览出错:" + a);
            } finally {
                n.close();
            }
        })), $(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: o} = e.findCarNumAndHref(a), r = async t => {
                await storageManager.saveCar(i, s, null, t), window.refresh();
            };
            n.hasClass("filterBtn") ? utils.q(t, `是否屏蔽${i}?`, (() => r(m))) : n.hasClass("favoriteBtn") ? r(u).then() : n.hasClass("hasDownBtn") ? r(f).then() : n.hasClass("hasWatchBtn") && r(v).then();
        }));
        const n = this.getBean("OtherSitePlugin"), a = await n.getMissAvUrl(), i = await n.getjableUrl(), s = await n.getAvgleUrl(), o = await n.getAv123Url();
        $(document).on("click", ".site-jable, .site-avgle, .site-miss-av, .site-123-av", (t => {
            t.preventDefault(), t.stopPropagation();
            const n = $(t.currentTarget), r = n.closest(".item"), {carNum: l, aHref: c, title: d} = e.findCarNumAndHref(r);
            let h = null;
            n.hasClass("site-jable") ? h = `${i}/search/${l}/` : n.hasClass("site-avgle") ? h = `${s}/vod/search.html?wd=${l}` : n.hasClass("site-miss-av") ? h = `${a}/search/${l}` : n.hasClass("site-123-av") && (h = `${o}/ja/search?keyword=${l}`), 
            t && (t.ctrlKey || t.metaKey) ? GM_openInTab(h, {
                insert: 0
            }) : window.open(h);
        })), $(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), o = 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(o.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"), o = await K(n);
        if (!o) return void this.showImg(t, e, n);
        let r = await storageManager.getSetting("videoQuality") || "hhb";
        o[r] || (r = Object.keys(o)[0]);
        let l = o[r], c = `\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="${l}" \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        `;
        p && (c = `\n                <div>\n                    <video \n                        src="${l}" \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(c), e.hide(), e.removeClass("loading"), e.next(".loading-spinner").remove(), 
        i = $(`#${a}`);
        let d = i[0];
        d.load(), d.muted = !1, d.play(), i.trigger("focus");
    }
}

class Tt extends U {
    constructor() {
        super(...arguments), r(this, "$contentBox", $(".section .container")), r(this, "urlParams", new URLSearchParams(window.location.search)), 
        r(this, "sortVal", this.urlParams.get("sort") || "release_date"), r(this, "currentPage", this.urlParams.get("page") ? parseInt(this.urlParams.get("page")) : 1), 
        r(this, "maxPage", null), r(this, "keyword", this.urlParams.get("keyword") || null);
    }
    getName() {
        return "Fc2By123AvPlugin";
    }
    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 o of a) {
                let t = $(o);
                if (t.find(".box-item").each(((t, n) => {
                    const a = $(n), s = a.find("img").attr("data-src");
                    let o = a.find("img").attr("title");
                    const r = a.find(".detail a"), l = r.attr("href"), c = e + (l.startsWith("/") ? l : "/" + l), d = r.text().trim().replace(o + " - ", "");
                    o = o.replace("FC2-PPV", "FC2"), i.push({
                        imgSrc: s,
                        carNum: o,
                        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:${b}"><span>${w}</span></a>\n                        <a id="favoriteBtn" class="menu-btn" style="background-color:${S}"><span>${x}</span></a>\n                        <a id="hasDownBtn" class="menu-btn" style="background-color:${_}"><span>${C}</span></a>\n                        <a id="hasWatchBtn" class="menu-btn" style="background-color:${B};"><span>${I}</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: utils.getDefaultArea(),
            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, u), window.refresh(), layer.closeAll();
                })), $("#filterBtn").on("click", (n => {
                    utils.q(n, `是否屏蔽${t}?`, (async () => {
                        const n = $("#data-actress").text();
                        await storageManager.saveCar(t, e, n, m), 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();
        }
    }
    handleLongImg(t) {
        console.log($(".movie-gallery .image-list")), utils.loopDetector((() => $(".movie-gallery .image-list").length > 0), (async () => {
            $(".movie-gallery .image-list").prepend(' <a class="tile-item screen-container" style="overflow:hidden;max-height: 150px;max-width:150px; text-align:center;"><div style="margin-top: 50px;color: #000;cursor: auto">正在加载缩略图</div></a> ');
            const e = await this.getBean("ScreenShotPlugin").getScreenshot(t);
            e && ($(".screen-container").html(`<img src="${e}" alt="" loading="lazy" style="width: 100%;">`), 
            $(".screen-container").on("click", (t => {
                t.stopPropagation(), t.preventDefault(), showImageViewer(t.currentTarget);
            })));
        }));
    }
    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 o = "主演: ";
        if (s.length > 0) {
            let t = "";
            s.each(((e, n) => {
                let a = $(n), i = a.text(), s = a.attr("href");
                o += `<span class="actor-tag"><a href="https://fc2ppvdb.com${s}" target="_blank">${i}</a></span>`, 
                t += i + " ";
            })), $("#data-actress").text(t);
        } else o += "<span>暂无演员信息</span>";
        $(".actor-list").html(o);
        const r = a.find("div").filter((function() {
            return 0 === $(this).text().trim().indexOf("販売者:");
        }));
        if (r.length > 0) {
            const t = $(r[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 = t.replace("FC2-", ""), n = `https://adult.contents.fc2.com/article/${t.replace("FC2-", "")}/`;
        const a = await gmHttp.get(n, null, {
            referer: n
        });
        let i = $(a).find(".items_article_SampleImagesArea img").map((function() {
            return $(this).attr("src");
        })).get(), s = "";
        Array.isArray(i) && i.length > 0 ? s = i.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(s), this.handleLongImg(e);
    }
    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 Lt extends U {
    getName() {
        return "video123AvPlugin";
    }
    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 Ht extends U {
    constructor() {
        super(...arguments), r(this, "currentEngine", null), r(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
        } ]);
    }
    getName() {
        return "MagnetHubPlugin";
    }
    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 = "jhs_magnetHub_selectedEngine", i = localStorage.getItem(a);
        let s = 0;
        this.searchEngines.forEach(((t, e) => {
            const a = $(`<div class="magnet-tab" data-engine="${t.id}">${t.name}</div>`);
            i && t.id === i ? (a.addClass("active"), this.currentEngine = t, s = e) : 0 !== e || i || (a.addClass("active"), 
            this.currentEngine = t), n.append(a);
        })), e.append(n);
        const o = $('<div class="magnet-results"></div>');
        return e.append(o), e.on("click", ".magnet-tab", (n => {
            const i = $(n.target).data("engine");
            this.currentEngine = this.searchEngines.find((t => t.id === i)), localStorage.setItem(a, i), 
            e.find(".magnet-tab").removeClass("active"), $(n.target).addClass("active"), this.searchEngine(o, this.currentEngine, t);
        })), this.searchEngine(o, this.currentEngine || this.searchEngines[s], 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 (o) {
            t.html(`<div class="magnet-error">解析 ${e.name} 缓存结果失败: ${o.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 (o) {
                    t.html(`<div class="magnet-error">解析 ${e.name} 结果失败: ${o.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"), o = "magnet:?xt=urn:btih:" + i.attr("href").split("/").pop(), r = a.find(".size").text(), l = a.find(".date").text();
            n.push({
                title: s,
                magnet: o,
                size: r,
                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"), o = a.find("td:nth-child(4)").text().trim(), r = a.find("td:nth-child(5)").text().trim();
            s && n.push({
                title: i,
                magnet: s,
                size: o,
                date: r
            });
        })), 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"), o = a.find("td:nth-child(4)").text().trim(), r = a.find("td:nth-child(5)").text().trim();
            s && n.push({
                title: i,
                magnet: s,
                size: o,
                date: r
            });
        })), n;
    }
}

class Et extends U {
    getName() {
        return "ScreenShotPlugin";
    }
    async handle() {
        this.loadScreenShot().then();
    }
    async loadScreenShot() {
        if (!isDetailPage) return;
        if ("yes" !== await storageManager.getSetting("enableLoadScreenShot", "yes")) return;
        let t = this.getPageInfo().carNum;
        h && $(".tile-item").first().before(' <a class="tile-item screen-container" style="overflow:hidden;max-height: 215px;text-align:center;"><div style="margin-top: 50px;color: #000;cursor: auto">正在加载缩略图</div></a> '), 
        p && $("#sample-waterfall .sample-box:first").after(' <a class="sample-box screen-container" style="overflow:hidden; height: 110px; text-align:center;"><div style="margin-top: 30px;color: #000;cursor: auto">正在加载缩略图</div></a> ');
        try {
            const e = await this.getScreenshot(t);
            this.addImg("缩略图", e);
        } catch (e) {
            this.showErrorFallback(t, e);
        }
    }
    async getScreenshot(t) {
        const e = localStorage.getItem("jhs_screenShot") ? JSON.parse(localStorage.getItem("jhs_screenShot")) : {};
        if (e[t]) return e[t];
        let n;
        try {
            n = await this.getJavStoreScreenShot(t);
        } catch (i) {
            console.error("获取JavStore缩略图发生错误:", i), console.log("尝试JavBest缩略图"), n = await this.getJavBestScreenShot(t);
        }
        const a = n.indexOf("https://");
        return -1 !== a && (n = n.substring(a)), e[t] = n, localStorage.setItem("jhs_screenShot", JSON.stringify(e)), 
        n;
    }
    async getJavStoreScreenShot(t) {
        let e = `https://javstore.net/search/${t}.html`, n = await gmHttp.get(e);
        const a = utils.htmlTo$dom(n);
        let i = null;
        if (a.find("#content_news h3 span a").each((function() {
            if ($(this).attr("title").toLowerCase().includes(t.toLowerCase())) return i = $(this).attr("href"), 
            !1;
        })), !i) throw new Error("查询番号失败: " + e);
        let s = await gmHttp.get(i);
        const o = utils.htmlTo$dom(s);
        let r = o.find("a:contains('CLICK HERE')").attr("href") || o.find("img[src*='_s.jpg']").attr("src");
        if (!r) throw new Error("解析预览图失败");
        return r.replace(".th", "");
    }
    addImg(t, e) {
        e && (h && $(".screen-container").html(`<img src="${e}" alt="${t}" loading="lazy" style="width: 100%;">`), 
        p && $(".screen-container").html(`<div class="photo-frame"><img src="${e}" style="height: inherit;width: 100%;" title="${t}" alt="${t}"></div>`), 
        $(".screen-container").on("click", (t => {
            t.stopPropagation(), t.preventDefault(), showImageViewer(t.currentTarget);
        })));
    }
    showErrorFallback(t, e) {
        console.error("获取缩略图失败:", e.message.substring(0, 100));
        let n = p ? "margin-top: 30px" : "margin-top: 50px";
        $(".screen-container").html(`<div style="${n}; cursor:auto;color:#000;">获取缩略图失败</div><br/><a href='#' class='retry-link'>点击重试</a> 或 <a class="check-link" href='https://javstore.net/search/${t}.html' target='_blank'>前往确认</a>`).on("click", ".retry-link", (async e => {
            e.stopPropagation(), e.preventDefault(), $(".screen-container").html(`<div style="${n};cursor:auto;color:#000;">正在重新加载...</div>`);
            try {
                const e = await this.getScreenshot(t);
                this.addImg("缩略图", e);
            } catch (a) {
                this.showErrorFallback(t, a);
            }
        })).on("click", ".check-link", (async e => {
            e.stopPropagation(), e.preventDefault(), window.open(`https://javstore.net/search/${t}.html`, "_blank");
        }));
    }
    async getJavBestScreenShot(t) {
        let e = `https://javbest.net/?s=${t}`, n = await gmHttp.get(e);
        const a = utils.htmlTo$dom(n).find(".app_loop_thumb a").first().attr("href");
        if (!a) throw console.error("解析JavBest搜索页失败:", e), new Error("解析JavBest搜索页失败");
        const i = await gmHttp.get(a);
        let s = $(i).find('#content a img[src*="_t.jpg"]').attr("src");
        if (!s) throw console.error("解析JavBest缩略图失败:", e), new Error("解析JavBest缩略图失败");
        return s = s.replace("_t", "").replace("http:", "https:"), s;
    }
}

class Nt extends U {
    getName() {
        return "FilterActorVideoPlugin";
    }
    async handle() {
        d.includes("/actors/") && $("h2").append('<a class="a-danger" id="filterActorVideo" style="padding:8px;" data-tip="选定好分类, 屏蔽该演员后, 有新作品也会纳入屏蔽中" >屏蔽该演员所有作品</a>'), 
        d.includes("/star/") && $("#waitDownBtn").after(' \n                <a id="filterActorVideo" title="屏蔽该演员后, 有新作品也会纳入屏蔽中" 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", (async t => {
            let e = {
                clientX: t.clientX,
                clientY: t.clientY + 80
            }, n = h ? $(".actor-section-name") : $(".avatar-box .photo-info .pb10");
            if (0 === n.length) return void show.error("获取演员名称失败");
            let a, i = n.text().trim().split(",")[0], s = $(".section-meta:contains('男優')").length > 0;
            a = s ? storageManager.filter_actor_car_list_key + i : storageManager.filter_actress_car_list_key + i;
            let o = "是否屏蔽该演员下的所有作品?";
            (await storageManager.getActorFilterCarList(a)).length > 0 && (o = "该演员已屏蔽过, 是否清空该数据并重新屏蔽?"), 
            utils.q(e, o, (async () => {
                this.loadObj = loading();
                try {
                    await storageManager.removeActorFilter(a), await storageManager.saveSettingItem("autoPage", "yes");
                    const t = await storageManager.getSetting(storageManager.filter_actor_actress_info_list_key, []), e = this.getCurrentStarUrl(), n = t.find((t => t.name === i));
                    n ? (n.recordTime = utils.getNowStr(), n.url = e) : t.push({
                        name: i,
                        key: a,
                        url: e,
                        isActor: s,
                        recordTime: utils.getNowStr(),
                        checkTime: ""
                    }), await storageManager.saveSettingItem(storageManager.filter_actor_actress_info_list_key, t), 
                    await this.filterActorVideo(a, i);
                } catch (t) {
                    console.error(t), this.loadObj.close();
                } finally {
                    this.loadObj.close();
                }
            }));
        })), this.checkNewActressActorFilterCar().then();
    }
    getCurrentStarUrl() {
        let t = d.replace(/([&?])page=\d+(&|$)/, "$1");
        return t = t.replace(/[&?]$/, ""), t = t.replace(/\?&/, "?"), t = t.replace(/\/(\d+)(?:\/(\d+))?(\?|$)/, ((t, e, n, a) => void 0 !== n ? `/${e}${a}` : t)), 
        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 o of a) {
            const t = $(o), {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) {
            let e;
            try {
                const n = i.split("_").pop(), a = t.find((t => t.name === n));
                if (!a) continue;
                let s = a.url;
                e = s;
                const o = new URL(window.location.href).hostname;
                if (o !== new URL(s).hostname) continue;
                let r = a.checkTime;
                if (r && this.isToday(r)) continue;
                const l = await http.get(s), c = $(l);
                console.log("检测屏蔽演员最新番号:", n, s), await this.parseAndSaveFilterInfo(c, i, n), a.checkTime = utils.getNowStr();
            } catch (a) {
                console.error("检测屏蔽演员信息, 发生错误:", e, a), show.error("检测屏蔽演员信息, 发生错误:" + a, "bottom", "right");
            } 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 jt extends U {
    getName() {
        return "WangPan115TaskPlugin";
    }
    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 t => {
                t.stopPropagation(), t.preventDefault();
                let n = loading();
                try {
                    await this.handleAddTask($(e).attr("data-clipboard-text"));
                } catch (a) {
                    show.error("发生错误:" + a), console.error(a);
                } finally {
                    n.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 o = 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("离线下载返回值:", o), !1 === o.state ? show.error(o.error_msg || "添加任务失败") : utils.q(null, "添加成功, 是否前往查看?", (() => {
            window.open("https://115.com/?tab=offline&mode=wangpan");
        }));
    }
}

class Ft extends U {
    getName() {
        return "WangPan115Plugin";
    }
    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"), 
utils.importResource("https://cdn.jsdelivr.net/npm/[email protected]/dist/viewer.min.css"), 
window.onload = async function() {
    window.isDetailPage = function() {
        let t = window.location.href;
        return h ? t.split("?")[0].includes("/v/") : !!p && $("#magnet-table").length > 0;
    }(), window.isListPage = function() {
        let t = window.location.href;
        return h ? $(".movie-list").length > 0 || t.includes("advanced_search") : !!p && $(".masonry > div .item").length > 0;
    }(), h && /(^|;)\s*locale\s*=\s*en\s*($|;)/i.test(document.cookie) && show.error("请切换到中文语言下才可正常使用本脚本", {
        duration: -1
    }), function() {
        const t = new z;
        let e = window.location.hostname;
        h && (t.register(ft), t.register(vt), t.register(et), t.register(nt), t.register(ut), 
        t.register(pt), t.register(xt), t.register(rt), t.register(st), t.register(ot), 
        t.register(Ct), t.register(It), t.register(At), t.register(Tt), t.register(R), t.register(gt), 
        t.register(Pt), t.register(ht), t.register(wt), t.register(W), t.register(mt), t.register(at), 
        t.register(ct), t.register(jt), t.register(Dt), t.register(Ht), t.register(Et), 
        t.register(Nt)), p && (t.register(ft), t.register(ut), t.register(xt), t.register(pt), 
        t.register(Ct), t.register(vt), t.register(It), t.register(Bt), t.register(At), 
        t.register(dt), t.register(ht), t.register(gt), t.register(mt), t.register(wt), 
        t.register(_t), t.register(Ht), t.register(Et), t.register(ct), t.register(jt), 
        t.register(Nt)), e.includes("sehuatang") && t.register(Mt), e.includes("javtrailers") && t.register(J), 
        e.includes("subtitlecat") && t.register(G), (e.includes("aliyundrive") || e.includes("alipan")) && t.register(it), 
        e.includes("5masterzzz") && t.register(Lt), e.includes("115.com") && t.register(Ft), 
        t.process().then();
    }();
};