+ .search-img-site-btns-container {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 10px;
+ margin-top: 15px;
+ }
+ .search-img-site-btn {
+ display: flex;
+ align-items: center;
+ padding: 8px 12px;
+ background-color: var(--jhs-surface-2);
+ border-radius: var(--jhs-radius-sm);
+ text-decoration: none;
+ color: var(--jhs-text);
+ transition: all 0.2s;
+ font-size: 14px;
+ border: 1px solid var(--jhs-border);
+ }
+ .search-img-site-btn:hover {
+ background-color: var(--jhs-border);
+ transform: translateY(-2px);
+ box-shadow: 0 2px 5px rgba(0,0,0,0.1);
+ }
+ .search-img-site-btn img {
+ width: 16px;
+ height: 16px;
+ margin-right: 6px;
+ }
+ .search-img-site-btn span {
+ white-space: nowrap;
+ }
+
+ `}open(e){this.getRuntimeService("dialog").open({type:1,title:"以图识图",content:`
+
+
+
+
拖拽图片到此处 或 点击按钮选择图片
+
也可以直接 Ctrl+V 粘贴图片或 图片URL
+
+
+
-
-
+
+
- placeholder="密码"
- onfocus="this.style.borderColor='var(--jhs-accent)'; this.style.background='var(--jhs-surface)'"
- onblur="this.style.borderColor='var(--jhs-border-strong)'; this.style.background='var(--jhs-surface-2)'" class="jhs-field">
+
+
![]()
+
+
+
-
-
- `,
- success: /* @__PURE__ */ __name((e2, t2) => {
- $("#loginBtn").click((function() {
- const e3 = $("#username").val(), n2 = $("#password").val();
- if (!e3 || !n2) return void show.error("请输入用户名和密码");
- let a2 = loading();
- (async (e4, t3) => {
- let n3 = `${U}/v1/sessions?username=${encodeURIComponent(e4)}&password=${encodeURIComponent(t3)}&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`, a3 = {
- "user-agent": "Dart/3.5 (dart:io)",
- "accept-language": "zh-TW",
- "content-type": "multipart/form-data; boundary=--dio-boundary-2210433284",
- jdsignature: await O()
- };
- return await gmHttp.post(n3, null, a3);
- })(e3, n2).then((async (e4) => {
- let n3 = e4.success;
- if (0 === n3) show.error(e4.message);
- else {
- if (1 !== n3) throw clog.error("登录失败", e4), new Error(e4.message);
- {
- let n4 = e4.data.token;
- await localStorage.setItem(me, await encryptData(n4)), show.ok("登录成功"), layer.close(t2), "function" === typeof onSuccess ? await onSuccess() : window.location.href = "/advanced_search?handleTop=1&period=daily";
- }
- }
- })).catch(((e4) => {
- clog.error("登录异常:", e4), show.error(e4.message);
- })).finally((() => {
- a2.close();
- }));
- }));
- }, "success")
- });
- }
- };
- __name(_Top250Plugin, "Top250Plugin");
- var Top250Plugin = _Top250Plugin;
- var _NavBarPlugin = class _NavBarPlugin extends BasePlugin {
- getName() {
- return "NavBarPlugin";
- }
- async initCss() {
- return "\n .highlight-red {\n /* 核心要求:高亮红色文本 */\n color: var(--jhs-status-filter) !important;\n \n /* 建议:增加字体加粗,效果更明显 */\n font-weight: bold;\n \n /* 建议:增加背景色,效果更突出 */\n /* background-color: yellow; */ \n}\n ";
- }
- handle() {
- if (this.margeNav(), this.hookSearch(), this.hookOldSearch(), this.toggleOtherNavItem(), $(window).resize(this.toggleOtherNavItem), JhsSelect.enhance("#search-box"), window.location.href.includes("/search")) {
- const e2 = new URLSearchParams(window.location.search);
- let t2 = e2.get("q"), n2 = e2.get("f");
- $("#search-keyword").val(t2), n2 && JhsSelect.setValue("#search-type", n2), t2 && this.highlightKeyword(t2);
- }
- }
- highlightKeyword(e2) {
- const t2 = e2.trim();
- if (!t2) return;
- const n2 = t2.toLowerCase();
- $(".video-title strong, .actor-box strong").each((function() {
- const e3 = $(this);
- e3.text().toLowerCase().includes(n2) && e3.addClass("highlight-red");
- }));
- }
- hookSearch() {
- $("#navbar-menu-hero").after('\n \n '), $("#search-keyword").on("paste", ((e2) => {
- const t2 = e2.originalEvent.clipboardData.items;
- for (let n2 = 0; n2 < t2.length; n2++) if (-1 !== t2[n2].type.indexOf("image")) {
- const e3 = t2[n2].getAsFile();
- $("#search-keyword").blur();
- const a2 = this.getBean("SearchByImagePlugin");
- return void a2.open((() => {
- a2.handleImageFile(e3), a2.resetSearchUI();
- }));
- }
- })).on("keypress", ((e2) => {
- "Enter" === e2.key && setTimeout((() => {
- $("#search-btn").click();
- }), 0);
- })), $("#search-btn").on("click", ((e2) => {
- let t2 = $("#search-keyword").val(), n2 = $("#search-type").val();
- "" !== t2 && (window.location.href.includes("/search") ? window.location.href = "/search?q=" + t2 + "&f=" + n2 : window.open("/search?q=" + t2 + "&f=" + n2));
- })), $("#search-img-btn").on("click", (() => {
- this.getBean("SearchByImagePlugin").open();
- }));
- }
- hookOldSearch() {
- const e2 = document.querySelector(".search-image");
- if (!e2) return;
- const t2 = e2.cloneNode(true);
- e2.parentNode.replaceChild(t2, e2), $("#button-search-image").attr("data-tooltip", "以图识图"), $(".search-image").on("click", ((e3) => {
- this.getBean("SearchByImagePlugin").open();
- }));
- }
- margeNav() {
- $('a[href*="/feedbacks/new"]').remove(), $('a[href*="theporndude.com"]').remove(), $('a.navbar-link[href="/makers"]').parent().after('\n
\n ');
- }
- toggleOtherNavItem() {
- let e2 = $("#search-box"), t2 = $("#search-bar-container");
- $(window).width() < 1600 && $(window).width() > 1023 && (e2.hide(), t2.show()), $(window).width() > 1600 && (e2.show(), t2.hide());
- }
- };
- __name(_NavBarPlugin, "NavBarPlugin");
- var NavBarPlugin = _NavBarPlugin;
- var _StorageQueue = class _StorageQueue {
- constructor() {
- this.queue = Promise.resolve();
- }
- addTask(e2) {
- const task = this.queue.then((() => e2()));
- return this.queue = task.catch(((e3) => {
- clog.error("执行异步队列任务失败:", e3);
- })), task;
- }
- async waitAllFinished() {
- return this.queue;
- }
- };
- __name(_StorageQueue, "StorageQueue");
- var StorageQueue = _StorageQueue;
- var _OtherSitePlugin = class _OtherSitePlugin extends BasePlugin {
- constructor() {
- super(...arguments), i(this, "siteConfigs", [{
- id: "javTrailersBtn",
- getBaseUrl: /* @__PURE__ */ __name(async () => await this.getJavTrailersUrl(), "getBaseUrl"),
- itemSelector: ".videos-list .video-link",
- searchPath: /* @__PURE__ */ __name((e2, t2) => `${e2}/search/${t2}`, "searchPath"),
- getDetailPageHref: /* @__PURE__ */ __name((e2) => e2.attr("href"), "getDetailPageHref"),
- findCarNumOrTitle: /* @__PURE__ */ __name((e2) => e2.find("p.card-text").text(), "findCarNumOrTitle")
- }, {
- id: "123AvBtn",
- getBaseUrl: /* @__PURE__ */ __name(async () => `${await this.getAv123Url()}/cn`, "getBaseUrl"),
- itemSelector: ".card",
- searchPath: /* @__PURE__ */ __name((e2, t2) => `${e2}/search?keyword=${encodeURIComponent(t2)}`, "searchPath"),
- requestOptions: { cookiePartitionTopLevelSite: "https://123av.com" },
- getDetailPageHref: /* @__PURE__ */ __name((e2, t2) => {
- const href = e2.find('a.card__link[href*="/cn/v/"]').first().attr("href");
- return href ? new URL(href, t2).href : null;
- }, "getDetailPageHref"),
- findCarNumOrTitle: /* @__PURE__ */ __name((e2) => e2.find(".card__link").first().text(), "findCarNumOrTitle"),
- matches: /* @__PURE__ */ __name((text, carNum) => text.replace(/FC2-PPV-/gi, "FC2-").toLowerCase().includes(carNum.toLowerCase()), "matches")
- }, {
- id: "jableBtn",
- getBaseUrl: /* @__PURE__ */ __name(async () => await this.getjableUrl(), "getBaseUrl"),
- itemSelector: "#list_videos_videos_list_search_result .detail .title a",
- searchPath: /* @__PURE__ */ __name((e2, t2) => `${e2}/search/${t2}/`, "searchPath"),
- getDetailPageHref: /* @__PURE__ */ __name((e2) => e2.attr("href"), "getDetailPageHref"),
- findCarNumOrTitle: /* @__PURE__ */ __name((e2) => e2.text(), "findCarNumOrTitle")
- }, {
- id: "avgleBtn",
- getBaseUrl: /* @__PURE__ */ __name(async () => await this.getAvgleUrl(), "getBaseUrl"),
- itemSelector: ".text-secondary",
- searchPath: /* @__PURE__ */ __name((e2, t2) => `${e2}/vod/search.html?wd=${t2}`, "searchPath"),
- getDetailPageHref: /* @__PURE__ */ __name((e2) => e2.attr("href"), "getDetailPageHref"),
- findCarNumOrTitle: /* @__PURE__ */ __name((e2) => e2.text(), "findCarNumOrTitle")
- }, {
- id: "missAvBtn",
- getBaseUrl: /* @__PURE__ */ __name(async () => await this.getMissAvUrl(), "getBaseUrl"),
- itemSelector: ".text-secondary",
- searchPath: /* @__PURE__ */ __name((e2, t2) => `${e2}/search/${t2}`, "searchPath"),
- getDetailPageHref: /* @__PURE__ */ __name((e2) => e2.attr("href"), "getDetailPageHref"),
- findCarNumOrTitle: /* @__PURE__ */ __name((e2) => e2.text(), "findCarNumOrTitle")
- }, {
- id: "supJavBtn",
- getBaseUrl: /* @__PURE__ */ __name(async () => await this.getSupJavUrl(), "getBaseUrl"),
- itemSelector: ".posts post",
- searchPath: /* @__PURE__ */ __name((e2, t2) => `${e2}/?s=${t2}`, "searchPath"),
- getDetailPageHref: /* @__PURE__ */ __name((e2, t2, n2) => e2.attr("href"), "getDetailPageHref"),
- findCarNumOrTitle: /* @__PURE__ */ __name((e2) => e2.attr("title"), "findCarNumOrTitle")
- }, {
- id: "javDbBtn",
- getBaseUrl: /* @__PURE__ */ __name(async () => await this.getJavDbUrl(), "getBaseUrl"),
- itemSelector: ".movie-list .item",
- searchPath: /* @__PURE__ */ __name((e2, t2) => `${e2}/search?q=${t2}`, "searchPath"),
- getDetailPageHref: /* @__PURE__ */ __name((e2) => e2.find("a").attr("href"), "getDetailPageHref"),
- findCarNumOrTitle: /* @__PURE__ */ __name((e2) => e2.find(".video-title").text(), "findCarNumOrTitle"),
- condition: /* @__PURE__ */ __name((e2) => l, "condition")
- }, {
- id: "javBusBtn",
- getBaseUrl: /* @__PURE__ */ __name(async () => await this.getJavBusUrl(), "getBaseUrl"),
- itemSelector: ".container h3",
- searchPath: /* @__PURE__ */ __name((e2, t2) => `${e2}/${t2}`, "searchPath"),
- getDetailPageHref: /* @__PURE__ */ __name((e2, t2, n2) => `${t2}/${n2}`, "getDetailPageHref"),
- findCarNumOrTitle: /* @__PURE__ */ __name((e2) => e2.text(), "findCarNumOrTitle"),
- condition: /* @__PURE__ */ __name((e2) => r && e2 && !e2.includes("FC2"), "condition")
- }, {
- id: "fanzaBtn",
- noHandle: true,
- initUrl: /* @__PURE__ */ __name((e2) => `https://www.dmm.co.jp/search/=/searchstr=${e2}`, "initUrl"),
- condition: /* @__PURE__ */ __name((e2) => e2 && !e2.includes("FC2"), "condition")
- }]), i(this, "settingCache", null), i(this, "lastFetchTime", 0), i(this, "CACHE_DURATION", 1e4);
- }
- getName() {
- return "OtherSitePlugin";
- }
- async initCss() {
- return `
- `;
- }
- async handle() {
- isDetailPage && await this.loadOtherSite(null, null, {
- autoDetect: false
- });
- }
- async loadOtherSite(e2, t2, n2 = {}) {
- if ("yes" !== await storageManager.getSetting("enableLoadOtherSite", "yes")) return;
- const root = n2.root ? $(n2.root) : $(document), target = n2.target ? $(n2.target) : root.find(".movie-panel-info,.container .info").first();
- if (!target.length || n2.isActive && !n2.isActive()) return;
- root.find("#otherSiteBox,#settingsArea,[data-jhs-other-site-box],[data-jhs-other-site-settings]").remove();
- e2 = normalizeCarNum(e2) || this.getPageInfo().carNum;
- const enabled = this.getEnabledSites(), configs = this.siteConfigs.map(((config) => ({ ...config, sourceCarNum: t2 }))), view = { root, target, configs, carNum: e2, isActive: "function" === typeof n2.isActive ? n2.isActive : () => true };
- const box = $('
'), list = box.find(".jhs-site-list"), settings = $('
');
- configs.forEach(((config) => {
- if (config.condition && false === config.condition(config.sourceCarNum)) return;
- const button = $('
').attr("data-jhs-site-id", config.id).toggleClass("jhs-is-hidden", !enabled.includes(config.id));
- button.find("span").text(config.id.replace("Btn", "")), list.append(button);
- }));
- list.append('
', '
'), target.append(box, settings), view.box = box, view.settings = settings;
- if (!e2) return box.find(".site-btn").removeAttr("href").attr({ "aria-disabled": "true", title: "番号不可用" }), box.find('[data-jhs-role="detect-sites"]').prop("disabled", true), this.renderSettingsArea(view), this.setupEventListeners(view), void clog.warn("跳过第三方站点解析:番号不可用");
- box.find('[data-jhs-site-id="javTrailersBtn"]').on("click", ((event) => {
- event.preventDefault();
- const original = $(event.currentTarget).attr("href"), destination = event.ctrlKey || event.metaKey ? original : original + "?handle=1";
- utils.openPage(destination, e2, false, event);
- })), await Promise.all(configs.map((async (config) => {
- config.condition && false === config.condition(config.sourceCarNum) || await this.prepareSiteLink(e2, config, view);
- }))), this.renderSettingsArea(view), this.setupEventListeners(view), box.find('[data-jhs-role="detect-sites"]').off("click").on("click", ((event) => {
- event.preventDefault(), this.detectOtherSites(e2, view);
- })), n2.autoDetect && await this.detectOtherSites(e2, view);
- return box;
- }
- async prepareSiteLink(e2, t2, view = { root: $(document), isActive: /* @__PURE__ */ __name(() => true, "isActive") }) {
- const n2 = view.root.find(`[data-jhs-site-id="${t2.id}"],#${t2.id}`).first();
- if (!(e2 = normalizeCarNum(e2))) return n2.removeAttr("href").attr({ "aria-disabled": "true", title: "番号不可用" }), void this.setSiteState(n2, "idle");
- if (t2.initUrl) return void (n2.attr("href", t2.initUrl(e2)), this.setSiteState(n2, "idle"), n2.attr("title", "点击前往外部搜索页"));
- try {
- const a2 = await t2.getBaseUrl(), i2 = t2.searchPath(a2, e2);
- view.isActive() && (n2.attr("href", i2), n2.attr("title", "点击前往外部搜索页;点击检测按钮后才自动检测"), this.setSiteState(n2, "idle"));
- } catch (a2) {
- view.isActive() && (n2.attr("title", "外部站点地址未配置或不可用"), this.setSiteState(n2, "domain-error"));
- }
- }
- async detectOtherSites(e2, view = { root: $(document), configs: this.siteConfigs, isActive: /* @__PURE__ */ __name(() => true, "isActive") }) {
- const t2 = view.root.find('[data-jhs-role="detect-sites"],#detectOtherSiteBtn').first(), n2 = t2.text();
- if (!(e2 = normalizeCarNum(e2))) return t2.prop("disabled", true), void clog.warn("跳过第三方站点检测:番号不可用");
- return t2.text("检测中").prop("disabled", true).addClass("is-checking"), await Promise.all(view.configs.map((async (t3) => {
- t3.condition && false === t3.condition(t3.sourceCarNum) || await this.handleSite(e2, t3, view);
- }))), view.isActive() && t2.text(n2).prop("disabled", false).removeClass("is-checking");
- }
- setSiteState(e2, t2) {
- e2.removeClass("is-checking is-available is-unavailable is-domain-error"), "idle" !== t2 && e2.addClass(`is-${t2}`);
- }
- async handleSite(e2, t2, view = { root: $(document), isActive: /* @__PURE__ */ __name(() => true, "isActive") }) {
- const n2 = view.root.find(`[data-jhs-site-id="${t2.id}"],#${t2.id}`).first();
- n2.removeAttr("href").find(".site-tag").remove(), this.setSiteState(n2, "checking");
- if (t2.initUrl && n2.attr("href", t2.initUrl(e2)), t2.noHandle && true === t2.noHandle) {
- const t3 = "jhs_other_site_dmm", a2 = (localStorage.getItem(t3) ? JSON.parse(localStorage.getItem(t3)) : {})[e2];
- a2 ? (n2.attr("href", a2.url), "multiple" === a2.type && n2.append('
多结果'), this.setSiteState(n2, "available")) : this.setSiteState(n2, "idle");
- } else try {
- if (n2.attr("href")) return void this.setSiteState(n2, "idle");
- if (utils.isHidden(n2)) return;
- const a2 = "jhs_other_site", i2 = localStorage.getItem(a2) ? JSON.parse(localStorage.getItem(a2)) : {}, s2 = e2 + "_" + t2.id.replace("Btn", ""), o2 = i2[s2], m2 = Date.now();
- if (o2 && o2.time && m2 - o2.time < 864e5) return void (n2.attr("href", o2.url), "multiple" === o2.type && n2.append('
多结果'), this.setSiteState(n2, "available"));
- const r2 = await t2.getBaseUrl(), l2 = t2.searchPath(r2, e2);
- n2.attr("href", l2);
- const _breaker = gmHttp.isDomainCircuitBroken(l2);
- if (_breaker) {
- n2.attr("title", `站点已熔断,${_breaker.remaining}秒后重试`), this.setSiteState(n2, "domain-error");
- return;
- }
- const c2 = await storageManager.cachedRequest(`other-site:${t2.id}:${e2}`, 864e5, (() => gmHttp.get(l2, null, t2.headers, true, t2.requestOptions || {})));
- if (!view.isActive()) return;
- const d2 = utils.htmlTo$dom(c2), h2 = [];
- d2.find(t2.itemSelector).each(((n3, a3) => {
- const i3 = $(a3);
- const itemText = t2.findCarNumOrTitle(i3);
- if (t2.matches ? !t2.matches(itemText, e2) : !itemText.toLowerCase().includes(e2.toLowerCase())) return;
- let s3 = t2.getDetailPageHref(i3, r2, e2);
- if (!s3) throw new Error("解析href失败");
- s3.includes("http") || (s3 = r2 + (s3.startsWith("/") ? s3 : "/" + s3)), h2.push(s3);
- }));
- let g2 = "", p2 = null;
- if (1 === h2.length) {
- let e3 = h2[0];
- n2.attr("href", e3), this.setSiteState(n2, "available"), p2 = {
- type: "single",
- url: e3,
- time: m2
- };
- } else h2.length > 1 ? (n2.attr("href", l2), g2 += '
多结果', this.setSiteState(n2, "available"), p2 = {
- type: "multiple",
- url: l2,
- time: m2
- }) : (n2.attr("href", l2), n2.attr("title", "未查询到, 点击前往搜索页"), this.setSiteState(n2, "unavailable"));
- if (p2) {
- const e3 = localStorage.getItem(a2) ? JSON.parse(localStorage.getItem(a2)) : {};
- e3[s2] = p2, localStorage.setItem(a2, JSON.stringify(e3));
- }
- g2 && n2.append(g2);
- } catch (a2) {
- const e3 = String(a2), i2 = t2.id.replace("Btn", "");
- a2._circuitBroken ? (n2.attr("title", e3), this.setSiteState(n2, "domain-error"), clog.warn(`检测第三方资源跳过, ${i2} 已熔断`)) : a2?._cfBlocked ? (n2.attr("title", "请求失败:Cloudflare 安全检查。"), this.setSiteState(n2, "domain-error"), clog.warn(`检测第三方资源失败, ${i2} 需Cloudflare安全检查`)) : e3.includes("重定向") ? (n2.attr("title", "域名失效"), this.setSiteState(n2, "domain-error"), clog.warn(`检测第三方资源失败, ${i2} 域名被重定向`)) : e3.includes("404 Page Not Found") ? (n2.attr("title", "未查询到, 点击前往搜索页"), this.setSiteState(n2, "unavailable")) : (clog.error(a2), n2.attr("title", "请求失败。"), this.setSiteState(n2, "unavailable"), clog.warn(`检测第三方资源失败, ${i2}`));
- }
- }
- async getSettingCache() {
- const e2 = Date.now();
- return (!this.settingCache || e2 - this.lastFetchTime > this.CACHE_DURATION) && (this.settingCache = await storageManager.getSetting(), this.lastFetchTime = e2), 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://jav.rs";
- }
- 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 getSupJavUrl() {
- return (await this.getSettingCache()).supJavUrl || "https://supjav.com";
- }
- getEnabledSites() {
- const fallback = this.siteConfigs.map(((site) => site.id));
- try {
- const raw = localStorage.getItem("jhs_enabled_sites");
- if (!raw) return fallback;
- const parsed = JSON.parse(raw);
- return Array.isArray(parsed) ? parsed.filter(((id) => fallback.includes(id))) : fallback;
- } catch (error) {
- return clog.warn("外部站点配置损坏,已回退默认值", error), fallback;
- }
- }
- saveEnabledSites(e2) {
- localStorage.setItem("jhs_enabled_sites", JSON.stringify(e2));
- }
- renderSettingsArea(view = { root: $(document), configs: this.siteConfigs }) {
- const enabled = this.getEnabledSites(), target = view.root.find('[data-jhs-role="site-checkboxes"],#siteCheckboxes').first().empty();
- view.configs.forEach(((config) => {
- const input = $('
').attr("data-site-id", config.id).prop("checked", enabled.includes(config.id));
- target.append($('
').append(input, $("
").text(config.id.replace("Btn", ""))));
- }));
- }
- setupEventListeners(view = { root: $(document), configs: this.siteConfigs, carNum: null }) {
- const $settingsArea = view.root.find("[data-jhs-other-site-settings],#settingsArea").first();
- view.root.find('[data-jhs-role="site-settings"],#settingSiteBtn').off("click.jhsOtherSite").on("click.jhsOtherSite", (() => {
- $settingsArea.toggleClass("jhs-is-hidden");
- })), $settingsArea.off("change.jhsOtherSite").on("change.jhsOtherSite", 'input[type="checkbox"]', (async (event) => {
- const siteId = $(event.currentTarget).attr("data-site-id");
- try {
- if (event.currentTarget.checked) {
- view.root.find(`[data-jhs-site-id="${siteId}"],#${siteId}`).removeClass("jhs-is-hidden");
- const carNum = view.carNum || this.getPageInfo().carNum, site = view.configs.find(((item) => item.id === siteId));
- site && await this.prepareSiteLink(carNum, site, view);
- } else view.root.find(`[data-jhs-site-id="${siteId}"],#${siteId}`).addClass("jhs-is-hidden");
- this.saveEnabledSites($settingsArea.find('input[type="checkbox"]:checked').map(((index, input) => $(input).attr("data-site-id"))).get());
- } catch (error) {
- clog.warn(`外部站点 ${siteId || "unknown"} 状态更新失败`, error);
- }
- }));
- }
- };
- __name(_OtherSitePlugin, "OtherSitePlugin");
- var OtherSitePlugin = _OtherSitePlugin;
- var _BusDetailPagePlugin = class _BusDetailPagePlugin extends BasePlugin {
- getName() {
- return "BusDetailPagePlugin";
- }
- async initCss() {
- if (!window.isDetailPage) return "";
- $("h4:contains('推薦')").hide();
- }
- async handle() {
- if (window.location.href.includes("/star/")) {
- const e2 = $(".avatar-box");
- if (e2.length > 0) {
- let t2 = e2.parent();
- t2.css("position", "initial"), t2.insertBefore(t2.parent());
- }
- }
- $(".genre a").each((function() {
- const e2 = $(this).attr("href");
- e2 && (e2.startsWith("http://") || e2.startsWith("https://") || e2.startsWith("/")) && $(this).attr("target", "_blank");
- })), this.addCopyCarNumBtn();
- }
- addCopyCarNumBtn() {
- let e2 = null;
- const t2 = document.querySelectorAll("span.header");
- for (const n2 of t2) if ("識別碼:" === n2.textContent.trim()) {
- e2 = n2;
- break;
- }
- if (e2) {
- const t3 = e2.nextElementSibling;
- if (t3 && "SPAN" === t3.tagName) {
- const e3 = t3.textContent.trim(), n2 = document.createElement("button");
- n2.type = "button", n2.className = "jhs-btn jhs-btn--secondary jhs-copy-car-number", n2.textContent = "复制", n2.addEventListener("click", (async function(t4) {
- t4.preventDefault();
- await utils.copyToClipboard("番号", e3) && (() => {
- this.textContent = "已复制", setTimeout((() => {
- this.textContent = "复制";
- }), 1500);
- })();
- })), t3.parentNode.insertBefore(n2, t3.nextSibling);
- }
- }
- }
- };
- __name(_BusDetailPagePlugin, "BusDetailPagePlugin");
- var BusDetailPagePlugin = _BusDetailPagePlugin;
- var _DetailPageButtonPlugin = class _DetailPageButtonPlugin extends BasePlugin {
- getName() {
- return "DetailPageButtonPlugin";
- }
- constructor() {
- super(), this.answerCount = 1, this.stateBinding = null;
- }
- async handle() {
- this.hideVideoControls(), window.isDetailPage && (await this.createMenuBtn(), await this.autoRemoveNewVideoMark());
- }
- async autoRemoveNewVideoMark() {
- try {
- const e2 = await storageManager.getSetting("autoRemoveNewVideoMarkAfterBrowse", C);
- if (e2 !== _) return;
- const t2 = this.getPageInfo();
- if (!t2.carNum) return;
- await stateService.removeFromNewVideoList([t2.carNum], "browse");
- } catch (e2) {
- clog.error("自动移除新作品标记失败:", e2);
- }
- }
- async createMenuBtn() {
- const e2 = this.getPageInfo(), t2 = e2.carNum, n2 = `
-
-
-
-
-
-
+
-
-
-
-
-
-
- `;
- const workspaceSlot = this.getBean("DetailWorkspacePlugin")?.getSlot("summary-actions");
- workspaceSlot?.length ? workspaceSlot.append(n2) : r ? $(".tabs").after(n2) : l && $("#mag-submit-show").before(n2), $("#magnetSearchBtn").on("click", (async () => {
- let t3 = await this.getBean("MagnetHubPlugin").createMagnetHub(e2.carNum);
- layer.open({
- type: 1,
- title: "磁力搜索 " + e2.carNum,
- content: '
',
- area: utils.getResponsiveArea(["60%", "80%"]),
- scrollbar: false,
- success: /* @__PURE__ */ __name(() => {
- $("#magnetHubBox").append(t3);
- }, "success")
- });
- }));
- const a2 = this.getBean("HighlightMagnetPlugin"), i2 = await storageManager.getSetting("enableMagnetsFilter", _);
- $("#magnets-span").text(i2 === _ ? "关闭磁力过滤" : "开启磁力过滤"), i2 === _ && a2.doFilterMagnet(), $("#enable-magnets-filter").on("click", ((e3) => {
- let t3 = $("#magnets-span");
- "关闭磁力过滤" === t3.text() ? (a2.showAll(), t3.text("开启磁力过滤"), storageManager.saveSettingItem("enableMagnetsFilter", C)) : (a2.doFilterMagnet(), t3.text("关闭磁力过滤"), storageManager.saveSettingItem("enableMagnetsFilter", _));
- })), $("#search-subtitle-btn").on("click", ((e3) => utils.openPage(`https://subtitlecat.com/index.php?search=${t2}`, t2, false, e3))), $("#xunLeiSubtitleBtn").on("click", (() => this.searchXunLeiSubtitle(t2)));
- if (!t2) {
- $("#filterBtn, #favoriteBtn, #hasDownBtn, #hasWatchBtn, #magnetSearchBtn, #xunLeiSubtitleBtn, #search-subtitle-btn").prop("disabled", true).attr("title", "番号不可用");
- return void clog.warn("详情操作不可用:番号不可用");
- }
- this.stateBinding = detailStateController.bind({ root: document, carNum: t2, activityType: "detail-state", getRecord: /* @__PURE__ */ __name(() => this.getStateRecord(), "getRecord") });
- }
- async showStatus(e2) {
- return detailStateController.render({ root: document, carNum: e2 });
- }
- getStateRecord() {
- const info = this.getPageInfo();
- return { carNum: info.carNum, url: info.url, names: info.actress, publishTime: info.publishTime };
- }
- getStateBinding() {
- if (this.stateBinding) return this.stateBinding;
- const info = this.getPageInfo();
- return this.stateBinding = { root: document, layerIndex: null, carNum: normalizeCarNum(info.carNum), getRecord: /* @__PURE__ */ __name(() => this.getStateRecord(), "getRecord"), activityType: "detail-state", selectors: {} };
- }
- async favoriteOne(event) {
- return detailStateController.requestToggle(this.getStateBinding(), "favorite", event);
- }
- async hasDownOne(event) {
- return detailStateController.requestToggle(this.getStateBinding(), "downloaded", event);
- }
- async hasWatchOne(event) {
- return detailStateController.requestToggle(this.getStateBinding(), "watched", event);
- }
- searchXunLeiSubtitle(e2) {
- let t2 = loading();
- gmHttp.get(`https://api-shoulei-ssl.xunlei.com/oracle/subtitle?gcid=&cid=&name=${e2}`).then(((t3) => {
- let n2 = t3.data;
- n2 && 0 !== n2.length ? layer.open({
- type: 1,
- title: "迅雷字幕",
- content: '\n
\n ',
- scrollbar: false,
- area: utils.getResponsiveArea(["60%", "70%"]),
- anim: -1,
- success: /* @__PURE__ */ __name((t4, a2) => {
- new Tabulator("#xunlei-table-container", {
- layout: "fitColumns",
- placeholder: "暂无数据",
- virtualDom: true,
- data: n2,
- responsiveLayout: "collapse",
- responsiveLayoutCollapse: true,
- columnDefaults: {
- headerHozAlign: "center",
- hozAlign: "center"
- },
- columns: [{
- title: "文件名",
- field: "name",
- headerSort: false,
- responsive: 0
- }, {
- title: "类型",
- field: "ext",
- headerSort: false,
- responsive: 0
- }, {
- title: "操作",
- responsive: 0,
- headerSort: false,
- formatter: /* @__PURE__ */ __name((t5, n3, a3) => {
- const i2 = t5.getData();
- return a3((() => {
- const n4 = t5.getElement().querySelector(".subtitle-preview-btn"), a4 = t5.getElement().querySelector(".subtitle-download-btn");
- n4 && n4.addEventListener("click", (async (t6) => {
- let n5 = i2.url, a5 = e2 + "." + i2.ext;
- this.previewSubtitle(n5, a5);
- })), a4 && a4.addEventListener("click", (async (t6) => {
- let n5 = i2.url, a5 = e2 + "." + i2.ext, s2 = await gmHttp.get(n5);
- utils.download(s2, a5);
- }));
- })), '\n
\n
\n ';
- }, "formatter")
- }],
- locale: "zh-cn",
- langs: {
- "zh-cn": {
- pagination: {
- first: "首页",
- first_title: "首页",
- last: "尾页",
- last_title: "尾页",
- prev: "上一页",
- prev_title: "上一页",
- next: "下一页",
- next_title: "下一页",
- all: "所有",
- page_size: "每页行数"
- }
- }
- }
- }), utils.setupEscClose(a2);
- }, "success")
- }) : show.error("迅雷中找不到相关字幕!");
- })).catch(((e3) => {
- clog.error(e3), show.error(e3);
- })).finally((() => {
- t2.close();
- }));
- }
- async filterOne(e2, t2) {
- e2 && e2.preventDefault();
- return detailStateController.requestToggle(this.getStateBinding(), "blocked", e2);
- }
- hideVideoControls() {
- $(document).on("mouseenter", "#preview-video", (function() {
- $(this).prop("controls", true);
- }));
- }
- async previewSubtitle(e2, t2) {
- if (!e2) return void clog.error("未提供文件URL");
- const n2 = e2.split(".").pop().toLowerCase();
- if ("ass" === n2 || "srt" === n2) try {
- let a2 = await gmHttp.get(e2), i2 = "字幕预览";
- "ass" === n2 ? i2 = "ASS字幕预览 - " + t2 : "srt" === n2 && (i2 = "SRT字幕预览 - " + t2);
- const s2 = a2.split("\n");
- let o2 = "";
- const r2 = String(s2.length).length;
- s2.forEach(((e3, t3) => {
- const n3 = String(t3 + 1).padStart(r2, " ");
- o2 += `
${n3}. ${e3}
-`;
- }));
- const l2 = o2;
- layer.open({
- type: 1,
- title: i2,
- area: utils.getResponsiveArea(["80%", "80%"]),
- scrollbar: false,
- content: `
${l2}
`,
- btn: ["下载", "关闭"],
- btn1: /* @__PURE__ */ __name(function(e3, n3, i3) {
- return utils.download(a2, t2), false;
- }, "btn1")
- });
- } catch (a2) {
- show.error(`预览失败: ${a2.message}`), clog.error("预览字幕文件出错:", a2);
- }
- else show.error("仅支持预览ASS和SRT字幕文件");
- }
- };
- __name(_DetailPageButtonPlugin, "DetailPageButtonPlugin");
- var DetailPageButtonPlugin = _DetailPageButtonPlugin;
- var _HistoryPlugin = class _HistoryPlugin extends BasePlugin {
- constructor() {
- super(...arguments), i(this, "tableObj", null), i(this, "historyRoot", null), i(this, "historySelectAll", false), i(this, "historyExcludedCarNums", /* @__PURE__ */ new Set()), i(this, "historySorters", []), i(this, "historyFilteredCount", 0), i(this, "historySelectionSyncing", false);
- }
- getName() {
- return "HistoryPlugin";
- }
- async initCss() {
- return `
- `;
- }
- handleResize() {
- $(".navbar-search").is(":hidden") ? ($(".historyBtnBox").show(), $(".miniHistoryBtnBox").hide()) : ($(".historyBtnBox").hide(), $(".miniHistoryBtnBox").show());
- }
- async handle() {
- r && ($(".navbar-end").prepend('
\n \n
'), $(".navbar-search").css("margin-left", "0").before('\n
\n \n
\n '), this.handleResize(), $(window).resize((() => {
- this.handleResize();
- })), $("#historyBtn,#miniHistoryBtn").on("click", ((e2) => this.openHistory()))), l && await this.createBusButton();
- }
- async createBusButton() {
- const ready = await new Promise(((resolve) => {
- const startedAt = Date.now(), timer = setInterval((() => {
- if ($("#setting-btn").length && $("#top-right-box").length) return clearInterval(timer), resolve(true);
- Date.now() - startedAt >= 2500 && (clearInterval(timer), resolve(false));
- }), 25);
- }));
- if (!ready) return void clog.warn("鉴定记录入口未创建:JavBus 顶部工具区未就绪");
- $("#top-right-box").append('
'), $("#historyBtn,#miniHistoryBtn").on("click", ((e2) => this.openHistory()));
- }
- openHistory() {
- let e2 = `
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- `;
- e2 = e2.replace('
{
- const root = $(e3);
- this.historyRoot = root, root.find("#allSelectBox").prepend('
'), this.resetHistorySelection(false), JhsSelect.enhance(root);
- await this.loadTableData(), root.on("click.jhsHistory", "#clearSearchbtn", (async (e4) => {
- root.find("#searchCarNum").val(""), JhsSelect.setValue(root.find("#dataType"), "all"), await this.reloadTable(), root.find("#allSelectBox").hide();
- })).on("focusout keydown", "#searchCarNum", (async (e4) => {
- if ("focusout" === e4.type || "Enter" === e4.key) {
- if ("Enter" === e4.key && e4.preventDefault(), "keydown" === e4.type && "Enter" !== e4.key) return;
- await this.reloadTable();
- }
- })).on("click", ".table-link-param", (async (e4) => {
- let t2 = $(e4.currentTarget);
- root.find("#searchCarNum").val(t2.text()), await this.reloadTable();
- })).on("change", "#dataType", (async () => {
- await this.reloadTable();
- })).on("click", "[data-history-view]", (async (event) => {
- const view = $(event.currentTarget).data("history-view");
- root.find("[data-history-view]").removeClass("active"), $(event.currentTarget).addClass("active"), await this.showHistoryView(view);
- })).on("click", ".jhs-undo-activity", (async (event) => {
- const result = await stateService.undoTransaction($(event.currentTarget).data("transaction"));
- show.info(`撤销完成:${result.reverted.length} 项成功,${result.conflicts.length} 项冲突`), await this.renderActivityHistory();
- })).on("click", ".jhs-copy-offline", (async (event) => {
- await utils.copyToClipboard("离线资源", $(event.currentTarget).data("resource"));
- })).on("click", ".jhs-retry-offline", (async (event) => {
- const id = $(event.currentTarget).data("id"), item = (await stateService.getOfflineHistory()).find(((entry) => entry.id === id));
- item && await this.getBean("UnifiedOfflinePlugin").submitResource(event, item.resource, $(event.currentTarget), { carNum: item.carNum }, item.id, { forceAvailabilityRefresh: true, preferredProviderId: item.providerId }), await this.renderOfflineHistory();
- })).on("click", ".jhs-open-offline", (async (event) => {
- const id = $(event.currentTarget).data("id"), item = (await stateService.getOfflineHistory()).find(((entry) => entry.id === id)), provider = this.getBean("UnifiedOfflinePlugin").registry.providers.get(item?.providerId), url = provider?.openUrl?.();
- url && window.open(url, "_blank", "noopener,noreferrer");
- })).on("click", ".jhs-delete-offline", (async (event) => {
- await stateService.removeOfflineHistory($(event.currentTarget).data("id")), await this.renderOfflineHistory();
- })), this.bindHistoryActions(root);
- }, "success"),
- end: /* @__PURE__ */ __name(() => {
- this.resetHistorySelection(), this.historyRoot?.off(".jhsHistory"), this.historyRoot = null, this.tableObj && (this.tableObj.destroy(), this.tableObj = null);
- }, "end")
- });
- }
- async showHistoryView(view) {
- const stateView = "state" === view;
- this.resetHistorySelection(), this.historyRoot?.find("#filterBox,#allSelectBox").toggle(stateView), this.tableObj?.destroy(), this.tableObj = null;
- return stateView ? this.loadTableData() : "activity" === view ? this.renderActivityHistory() : this.renderOfflineHistory();
- }
- async renderActivityHistory() {
- const log = await stateService.getActivityLog(), host = this.historyRoot.find("#table-container").empty();
- if (!log.entries.length) return void host.html('
暂无操作记录
');
- log.entries.slice().reverse().forEach(((entry) => {
- const reverted = entry.changes.filter(((change) => "reverted" === change.undoState)).length, conflicts = entry.changes.filter(((change) => "conflict" === change.undoState)).length;
- host.append($('
').append($("
").text(`${entry.type} · ${entry.changes.length} 项`), $("
").text(`${new Date(entry.createdAt).toLocaleString()} · 已撤销 ${reverted} · 冲突 ${conflicts}`), $("
").text(entry.changes.map(((change) => change.carNum)).join("、")), $('
').attr("data-transaction", entry.id).prop("disabled", "committed" !== entry.commitState || reverted === entry.changes.length)));
- }));
- }
- async renderOfflineHistory() {
- const history = await stateService.getOfflineHistory(), host = this.historyRoot.find("#table-container").empty();
- if (!history.length) return void host.html('
暂无离线任务
');
- history.slice().reverse().forEach(((item) => {
- const actions = $('
').append($('
').attr("data-resource", item.resource), $('
').attr("data-id", item.id), $('
').attr("data-id", item.id), $('
').attr("data-id", item.id));
- host.append($('
').append($("
").text(`${item.providerName || item.providerId} · ${item.status}`), $("
").text(`${item.carNum || "未关联番号"} · ${new Date(item.createdAt).toLocaleString()}${item.retryOf ? ` · 重试自 ${item.retryOf}` : ""}`), $("
").text(item.errorMessage || item.resource), actions));
- }));
- }
- async reloadTable(resetSelection = true) {
- resetSelection && this.resetHistorySelection(), await this.tableObj?.setPage(1);
- }
- normalizeHistoryCarNum(carNum) {
- return normalizeCarNum(carNum) || String(carNum || "").trim().toUpperCase();
- }
- /** 清空 History 的全选、排除项和当前页选择。 */
- resetHistorySelection(deselectRows = true) {
- this.historySelectAll = false, this.historyExcludedCarNums.clear();
- if (deselectRows && this.tableObj) {
- this.historySelectionSyncing = true;
- try {
- this.tableObj.deselectRow();
- } finally {
- this.historySelectionSyncing = false;
- }
- }
- this.updateHistorySelectionUi();
- }
- createHistorySelectAllCheckbox() {
- const checkbox = document.createElement("input");
- return checkbox.type = "checkbox", checkbox.className = "jhs-history-select-all", checkbox.setAttribute("aria-label", "选择当前筛选条件下的全部记录"), checkbox.addEventListener("change", (() => {
- checkbox.checked ? (this.historySelectAll = true, this.historyExcludedCarNums.clear(), this.syncHistoryPageSelection()) : this.resetHistorySelection();
- })), checkbox;
- }
- syncHistoryPageSelection() {
- if (!this.tableObj || !this.historySelectAll) return void this.updateHistorySelectionUi();
- const currentRows = this.tableObj.getData?.() || [], selectedIds = currentRows.map(((item) => item.carNum)).filter(((carNum) => !this.historyExcludedCarNums.has(this.normalizeHistoryCarNum(carNum))));
- this.historySelectionSyncing = true;
- try {
- this.tableObj.deselectRow(), selectedIds.length && this.tableObj.selectRow(selectedIds);
- } finally {
- this.historySelectionSyncing = false;
- }
- this.updateHistorySelectionUi();
- }
- updateHistoryRowSelection(row, selected) {
- if (!this.historySelectAll || this.historySelectionSyncing) return void this.updateHistorySelectionUi();
- const carNum = this.normalizeHistoryCarNum(row?.getData?.().carNum);
- carNum && (selected ? this.historyExcludedCarNums.delete(carNum) : this.historyExcludedCarNums.add(carNum)), this.updateHistorySelectionUi();
- }
- getHistorySelectionSummary() {
- if (this.historySelectAll) {
- const excluded = this.historyExcludedCarNums.size, selected = Math.max(0, this.historyFilteredCount - excluded);
- return {
- count: selected,
- text: excluded ? `已选择当前筛选结果 ${selected} 条,已排除 ${excluded} 条` : `已选择当前筛选结果 ${selected} 条`
- };
- }
- const count = this.tableObj?.getSelectedData?.().length || 0;
- return {
- count,
- text: `已选择当前页 ${count} 条`
- };
- }
- updateHistorySelectionUi() {
- if (!this.historyRoot) return;
- const summary = this.getHistorySelectionSummary(), batchBox = this.historyRoot.find("#allSelectBox"), filterBox = this.historyRoot.find("#filterBox");
- this.historyRoot.find("#historySelectionSummary").text(summary.text), summary.count > 0 ? (filterBox.hide(), batchBox.show()) : (filterBox.show(), batchBox.hide());
- this.historyRoot.find(".jhs-history-select-all").each(((index, element) => {
- element.checked = this.historySelectAll, element.indeterminate = this.historySelectAll ? this.historyExcludedCarNums.size > 0 : summary.count > 0;
- }));
- }
- /** 解析批量操作实际要处理的当前页或完整筛选结果。 */
- async getHistoryBatchSelection() {
- if (!this.historySelectAll) return this.tableObj?.getSelectedData?.() || [];
- const rows = await this.getFilteredHistoryData(this.historySorters), available = new Set(rows.map(((item) => this.normalizeHistoryCarNum(item.carNum))));
- for (const carNum of this.historyExcludedCarNums) available.has(carNum) || this.historyExcludedCarNums.delete(carNum);
- return this.historyFilteredCount = rows.length, rows.filter(((item) => !this.historyExcludedCarNums.has(this.normalizeHistoryCarNum(item.carNum))));
- }
- bindHistoryActions(root) {
- root.on("click.jhsHistory", (function(e2) {
- if (e2.target.closest(".sub-btns-toggle")) {
- const button = e2.target.closest(".sub-btns-toggle"), t2 = button.closest(".sub-btns").querySelector(".sub-btns-menu");
- root.find(".sub-btns-menu.show").each(((index, e3) => {
- e3 !== t2 && (e3.classList.remove("show"), e3.previousElementSibling?.setAttribute("aria-expanded", "false"));
- })), t2.classList.toggle("show"), button.setAttribute("aria-expanded", String(t2.classList.contains("show")));
- } else root.find(".sub-btns-menu.show").each(((index, e3) => {
- e3.classList.remove("show"), e3.previousElementSibling?.setAttribute("aria-expanded", "false");
- }));
- })), root.on("keydown.jhsHistory", ".sub-btns", ((e2) => {
- const menu = $(e2.currentTarget).find(".sub-btns-menu"), items = menu.find('[role="menuitem"]'), current = items.index(document.activeElement);
- if ("Escape" === e2.key) return e2.preventDefault(), menu.removeClass("show"), $(e2.currentTarget).find(".sub-btns-toggle").attr("aria-expanded", "false").trigger("focus");
- if (!["ArrowDown", "ArrowUp", "Home", "End"].includes(e2.key) || !menu.hasClass("show")) return;
- e2.preventDefault();
- const next = "Home" === e2.key ? 0 : "End" === e2.key ? items.length - 1 : "ArrowDown" === e2.key ? (current + 1 + items.length) % items.length : (current - 1 + items.length) % items.length;
- items.eq(next).trigger("focus");
- })), root.on("click.jhsHistory", ".history-deleteBtn, .history-filterBtn, .history-favoriteBtn, .history-hasDownBtn, .history-hasWatchBtn, .history-detailBtn", ((e2) => {
- e2.preventDefault(), e2.stopPropagation();
- const t2 = $(e2.currentTarget), n2 = t2.closest(".action-btns"), a2 = n2.attr("data-car-num"), i2 = n2.attr("data-href"), s2 = /* @__PURE__ */ __name(async (actionType) => {
- try {
- const flag = legacyActionToFlag(actionType);
- await stateService.toggle(a2, flag, { type: "history-state", record: { carNum: a2, url: i2 } }), await this.reloadTable();
- } catch (s3) {
- clog.error("历史记录操作失败:", s3), show.error("操作失败");
- }
- }, "s");
- if (t2.hasClass("history-filterBtn")) {
- const record = this.tableObj?.getRow(a2)?.getData(), isBlocked = normalizeStateFlags(record?.stateFlags).blocked;
- isBlocked ? void s2(d) : utils.q(e2, `是否屏蔽${a2}?`, (() => s2(d)));
- } else t2.hasClass("history-favoriteBtn") ? void s2(h) : t2.hasClass("history-hasDownBtn") ? void s2(g) : t2.hasClass("history-hasWatchBtn") ? void s2(p) : t2.hasClass("history-deleteBtn") ? this.handleDelete(e2, a2) : t2.hasClass("history-detailBtn") && void this.handleClickDetail(e2, {
- carNum: a2,
- url: i2
- }).catch(((error) => clog.error("历史详情打开失败", error)));
- })), root.on("click.jhsHistory", ".multiple-history-deleteBtn, .multiple-history-filterBtn, .multiple-history-favoriteBtn, .multiple-history-hasDownBtn, .multiple-history-hasWatchBtn", (async (e2) => {
- e2.preventDefault(), e2.stopPropagation();
- const t2 = $(e2.currentTarget);
- let n2 = await this.getHistoryBatchSelection(), a2 = "", i2 = "";
- t2.hasClass("multiple-history-filterBtn") ? (a2 = "屏蔽", i2 = d) : t2.hasClass("multiple-history-favoriteBtn") ? (a2 = "收藏", i2 = h) : t2.hasClass("multiple-history-hasDownBtn") ? (a2 = "已下载", i2 = g) : t2.hasClass("multiple-history-hasWatchBtn") ? (a2 = "已观看", i2 = p) : t2.hasClass("multiple-history-deleteBtn") && (a2 = "移除", i2 = "delete");
- if (!n2.length) return void show.info("请先选择要处理的记录");
- const selectionText = this.historySelectAll ? this.historyExcludedCarNums.size ? `当前筛选结果中已选择 ${n2.length} 条,排除 ${this.historyExcludedCarNums.size} 条` : `当前筛选结果中已选择全部 ${n2.length} 条` : `当前页已选择 ${n2.length} 条`;
- utils.q(e2, `${selectionText},是否标记为${a2}?`, (async () => {
- let e3 = loading();
- try {
- if ("delete" === i2) {
- const e4 = n2.map(((e5) => e5.carNum)), t3 = await stateService.remove(e4);
- if (!t3.changed.length) return void show.error("提供的番号中没有一个存在于列表中。");
- show.ok(`已成功删除 ${t3.changed.length} 个番号`);
- } else {
- const flag = legacyActionToFlag(i2);
- await stateService.patch(n2.map(((item) => item.carNum)), { [flag]: true }, { type: "history-batch-state", records: n2 }), show.ok("操作成功");
- }
- this.resetHistorySelection(), await this.reloadTable(false);
- } catch (t3) {
- clog.error(t3), show.error("操作失败,请稍后重试");
- } finally {
- e3.close();
- }
- }));
- }));
- }
- /** 返回应用当前搜索、状态筛选和排序后的完整 History 数据。 */
- async getFilteredHistoryData(sorters = this.historySorters) {
- let a2 = await storageManager.getCarList();
- this.allCount = a2.length, this.filterCount = 0, this.favoriteCount = 0, this.hasDownCount = 0, this.hasWatchCount = 0, this.waitCheckCount = 0, a2.forEach(((e2) => {
- const flags = normalizeStateFlags(e2.stateFlags);
- flags.blocked && this.filterCount++, flags.favorite && this.favoriteCount++, flags.downloaded && this.hasDownCount++, flags.watched && this.hasWatchCount++, hasAnyState(flags) || this.waitCheckCount++;
- })), this.historyRoot.find('#dataType option[value="all"]').text(`所有 (${this.allCount})`), this.historyRoot.find('#dataType option[value="waitCheck"]').text(`待鉴定 (${this.waitCheckCount})`), this.historyRoot.find('#dataType option[value="filter"]').text(`${u} (${this.filterCount})`), this.historyRoot.find('#dataType option[value="favorite"]').text(`${b} (${this.favoriteCount})`), this.historyRoot.find('#dataType option[value="hasDown"]').text(`${y} (${this.hasDownCount})`), this.historyRoot.find('#dataType option[value="hasWatch"]').text(`${k} (${this.hasWatchCount})`);
- const i2 = this.historyRoot.find("#dataType").val();
- const flagByFilter = { filter: "blocked", favorite: "favorite", hasDown: "downloaded", hasWatch: "watched" };
- let s2 = "all" === i2 ? a2 : "waitCheck" === i2 ? a2.filter(((e2) => !hasAnyState(e2.stateFlags))) : a2.filter(((e2) => normalizeStateFlags(e2.stateFlags)[flagByFilter[i2]]));
- const o2 = this.historyRoot.find("#searchCarNum").val().trim();
- if (o2) {
- let e2 = o2.toLowerCase().replace("-c", "").replace("-uc", "").replace("-4k", "");
- s2 = s2.filter(((t2) => {
- const n2 = t2.carNum.toLowerCase().includes(e2);
- const a3 = (t2.names ? t2.names : "").toLowerCase().includes(e2);
- return n2 || a3;
- }));
- }
- if (sorters && sorters.length > 0) {
- const e2 = sorters[0], t2 = e2.field, a3 = e2.dir;
- s2.sort(((e3, n2) => {
- const i3 = e3[t2], s3 = n2[t2], o3 = null == i3 || "" === i3, r2 = null == s3 || "" === s3;
- return o3 && !r2 ? 1 : !o3 && r2 ? -1 : o3 && r2 ? 0 : i3 < s3 ? "asc" === a3 ? -1 : 1 : i3 > s3 ? "asc" === a3 ? 1 : -1 : 0;
- }));
- }
- return s2;
- }
- async getDataList(e2, t2, n2) {
- this.historySorters = Array.isArray(n2) ? n2.map(((sorter) => ({
- ...sorter
- }))) : [];
- const rows = await this.getFilteredHistoryData(this.historySorters), r2 = rows.length, l2 = Math.ceil(r2 / t2), c2 = (e2 - 1) * t2, m2 = c2 + t2;
- return this.historyFilteredCount = r2, {
- maxPage: l2,
- dataList: rows.slice(c2, m2),
- totalCount: r2
- };
- }
- async loadTableData() {
- this.tableObj = new Tabulator(this.historyRoot.find("#table-container").get(0), {
- layout: "fitColumns",
- placeholder: "暂无数据",
- virtualDom: true,
- pagination: true,
- paginationMode: "remote",
- sortMode: "remote",
- ajaxURL: "queryRealm",
- dataLoader: false,
- ajaxRequestFunc: /* @__PURE__ */ __name(async (e2, t2, n2) => {
- const a2 = n2.page, i2 = n2.size, s2 = n2.sort;
- return await this.getDataList(a2, i2, s2);
- }, "ajaxRequestFunc"),
- dataReceiveParams: {
- last_page: "maxPage",
- last_row: "totalCount",
- data: "dataList"
- },
- paginationSize: 50,
- paginationSizeSelector: [50, 100, 1e3],
- paginationCounter: /* @__PURE__ */ __name((e2, t2, n2, a2, i2) => `共 ${a2} 条记录`, "paginationCounter"),
- responsiveLayout: "collapse",
- responsiveLayoutCollapse: true,
- columnDefaults: {
- headerHozAlign: "center",
- hozAlign: "center"
- },
- selectableRowsPersistence: false,
- index: "carNum",
- columns: [{
- formatter: "rowSelection",
- titleFormatter: /* @__PURE__ */ __name(() => this.createHistorySelectAllCheckbox(), "titleFormatter"),
- hozAlign: "center",
- headerSort: false,
- responsive: 0,
- width: 40,
- cellClick: /* @__PURE__ */ __name((e2, t2) => {
- t2.getRow().toggleSelect();
- }, "cellClick")
- }, {
- title: "番号",
- field: "carNum",
- width: 120,
- sorter: "string",
- responsive: 0,
- formatter: /* @__PURE__ */ __name((e2, t2, n2) => {
- const a2 = e2.getData().carNum, i2 = a2.indexOf("-");
- if (-1 === i2) return a2;
- return `
${a2.substring(i2 + 1)}`;
- }, "formatter")
- }, {
- title: "演员",
- field: "names",
- minWidth: 200,
- sorter: "string",
- responsive: 5,
- headerSort: true,
- formatter: /* @__PURE__ */ __name((e2, t2, n2) => (e2.getData().names || "").split(" ").filter(((e3) => "" !== e3.trim())).map(((e3) => `
`)).join(" "), "formatter")
- }, {
- title: "创建时间",
- field: "createDate",
- width: 170,
- sorter: "string",
- responsive: 4
- }, {
- title: "修改时间",
- field: "updateDate",
- width: 170,
- sorter: "string",
- responsive: 4
- }, {
- title: "发行时间",
- field: "publishTime",
- width: 170,
- sorter: "string",
- responsive: 4
- }, {
- title: "来源",
- field: "url",
- width: 80,
- sorter: "string",
- responsive: 5,
- hozAlign: "left",
- formatter: /* @__PURE__ */ __name((e2, t2, n2) => {
- let a2 = e2.getData().url;
- return a2 ? `
${a2.includes("javdb") ? "JavDB" : a2.includes("javbus") ? "JavBus" : a2.includes("123av") ? "123AV" : "其他"}` : "";
- }, "formatter")
- }, {
- title: "状态",
- field: "stateFlags",
- width: 220,
- sorter: false,
- responsive: 1,
- headerSort: false,
- formatter: /* @__PURE__ */ __name((e2, t2, n2) => {
- const flags = normalizeStateFlags(e2.getData().stateFlags), badges = [[flags.blocked, "filter", u], [flags.favorite, "fav", b], [flags.downloaded, "down", y], [flags.watched, "watch", k]].filter(((item) => item[0])).map(((item) => `
${item[2]}`));
- return badges.join(" ") || '
待鉴定';
- }, "formatter")
- }, {
- title: "备注",
- field: "remark",
- width: 100,
- sorter: "string",
- responsive: 6
- }, {
- title: "操作",
- sorter: "string",
- minWidth: 150,
- cssClass: "action-cell-dropdown",
- responsive: 0,
- headerSort: false,
- formatter: /* @__PURE__ */ __name((e2, t2, n2) => {
- const a2 = e2.getData();
- return n2((() => {
- var t3;
- null == (t3 = e2.getElement().querySelector(".history-editBtn")) || t3.addEventListener("click", ((e3) => {
- this.editRecord(a2);
- }));
- })), `
-
`;
- }, "formatter")
- }],
- initialSort: [{
- column: "updateDate",
- dir: "desc"
- }],
- locale: "zh-cn",
- langs: {
- "zh-cn": {
- pagination: {
- first: "首页",
- first_title: "首页",
- last: "尾页",
- last_title: "尾页",
- prev: "上一页",
- prev_title: "上一页",
- next: "下一页",
- next_title: "下一页",
- all: "所有",
- page_size: "每页行数"
- }
- }
- }
- }), this.tableObj.on("dataProcessed", (() => {
- this.historySelectAll ? this.syncHistoryPageSelection() : this.updateHistorySelectionUi();
- })), this.tableObj.on("rowSelected", ((row) => {
- this.updateHistoryRowSelection(row, true);
- })), this.tableObj.on("rowDeselected", ((row) => {
- this.updateHistoryRowSelection(row, false);
- })), this.tableObj.on("rowSelectionChanged", (() => {
- this.updateHistorySelectionUi();
- })), this.tableObj.on("rowDblClick", (function(e2, t2) {
- t2.toggleSelect();
- }));
- }
- handleDelete(e2, t2) {
- utils.q(e2, `是否移除${t2}?`, (async () => {
- await stateService.remove(t2), this.getBean("ListPagePlugin").showCarNumBox(t2), await this.reloadTable();
- }));
- }
- async handleClickDetail(e2, t2) {
- if (r) if (t2.carNum.includes("FC2-")) {
- const e3 = this.parseMovieId(t2.url);
- const plugin = this.getBean("Fc2Plugin"), source = await plugin.resolveFc2Source(t2);
- plugin.openFc2Dialog(e3, t2.carNum, t2.url, { source });
- } else {
- if (!t2.url) return void window.open("/search?q=" + t2.carNum, "_blank");
- utils.openPage(t2.url, t2.carNum, false, e2);
- }
- if (l) {
- let n2 = t2.url;
- if (n2.includes("javdb")) if (t2.carNum.includes("FC2-")) {
- const e3 = this.parseMovieId(n2);
- const plugin = this.getBean("Fc2Plugin"), source = await plugin.resolveFc2Source(t2);
- await plugin.openFc2Page(e3, t2.carNum, n2, { newTab: true }, { source });
- } else window.open(n2, "_blank");
- else utils.openPage(t2.url, t2.carNum, false, e2);
- }
- }
- async editRecord(e2) {
- const t2 = e2.carNum, n2 = e2.names || "", a2 = e2.url || "", flags = normalizeStateFlags(e2.stateFlags), s2 = e2.remark || "";
- let editRoot = $();
- const c2 = `
-
- `;
- layer.open({
- type: 1,
- title: `编辑记录: ${t2}`,
- area: utils.getDialogArea("sm"),
- content: c2,
- btn: ["保存", "取消"],
- success: /* @__PURE__ */ __name((e3, t3) => {
- editRoot = $(e3);
- const n3 = /* @__PURE__ */ __name((e4) => {
- e4.css("height", "auto"), e4.css("height", e4[0].scrollHeight + 15 + "px");
- }, "n"), a3 = editRoot.find("#edit-names");
- a3.on("input", (function() {
- n3($(this));
- })), n3(a3);
- const i2 = editRoot.find("#edit-remark");
- i2.on("input", (function() {
- n3($(this));
- })), n3(i2);
- }, "success"),
- yes: /* @__PURE__ */ __name(async (t3) => {
- const n3 = editRoot.find("#edit-names").val().trim(), i2 = editRoot.find("#edit-url").val().trim(), s3 = editRoot.find("#edit-remark").val().trim(), nextFlags = {
- favorite: editRoot.find("#edit-favorite").prop("checked"),
- downloaded: editRoot.find("#edit-downloaded").prop("checked"),
- watched: editRoot.find("#edit-watched").prop("checked"),
- blocked: editRoot.find("#edit-blocked").prop("checked")
- };
- const save = /* @__PURE__ */ __name(async () => {
- await stateService.patch(e2.carNum, nextFlags, { type: "history-edit", replaceMetadata: true, record: { ...e2, names: n3, url: i2, remark: s3 } }), this.tableObj.setData(), layer.close(t3);
- }, "save");
- if (!flags.blocked && nextFlags.blocked) return utils.q(null, `是否屏蔽${e2.carNum}?`, (() => void save())), false;
- await save();
- }, "yes")
- });
- }
- };
- __name(_HistoryPlugin, "HistoryPlugin");
- var HistoryPlugin = _HistoryPlugin;
- var _ReviewPlugin = class _ReviewPlugin extends BasePlugin {
- getName() {
- return "ReviewPlugin";
- }
- async initCss() {
- return `
- `;
- }
- async handle() {
- if (!window.isDetailPage) return;
- if (r) {
- const movieId = this.parseMovieId(window.location.href);
- const workspace = this.getBean("DetailWorkspacePlugin");
- await Promise.all([this.showReview(movieId, workspace?.getSlot("reviews")), this.getBean("RelatedPlugin").showRelated(workspace?.getSlot("related"), movieId)]);
- }
- if (l) {
- const carNumber = this.getPageInfo().carNum;
- if (!carNumber) return void clog.warn("跳过 JavBus 评论解析:番号不可用");
- const movieId = await resolveJavDbMovieId(carNumber);
- movieId && await this.showReview(movieId, this.getBean("DetailWorkspacePlugin")?.getSlot("reviews"));
- }
- }
- async showReview(movieId, target, options = {}) {
- const isActive = "function" === typeof options.isActive ? options.isActive : () => true;
- const enabled = await storageManager.getSetting("enableLoadReview", _), host = target?.length ? target : this.getBean("DetailWorkspacePlugin")?.getSlot("reviews") || $("#magnets-content");
- if (!isActive() || !host?.length) return $();
- const existing = host.children('[data-jhs-panel="reviews"]').filter(((_2, element) => $(element).attr("data-jhs-movie-id") === String(movieId))).first();
- if (existing.length) return existing;
- const panel = $('
').attr("data-jhs-movie-id", String(movieId));
- const header = $('');
- const toggle = $('
');
- const state = { movieId, panel, floorIndex: 1, loaded: false, loading: false, page: 1 };
- header.append(toggle), options.ownedSection ? options.ownedSection.find('[data-jhs-section-actions="reviews"]').first().append(toggle) : panel.append(header), panel.append('
', ''), host.append(panel), this.bindRightClickFilter();
- state.isActive = isActive;
- this.updateToggle(toggle, enabled === _);
- toggle.on("click", ((event) => {
- event.preventDefault(), event.stopPropagation();
- const expanded = "展开" === toggle.find(".toggle-text").text();
- this.updateToggle(toggle, expanded), panel.find(".jhs-review-container, .jhs-review-footer").toggle(expanded), expanded && !state.loaded && !state.loading && void this.fetchAndDisplayReviews(state), storageManager.saveSettingItem("enableLoadReview", expanded ? _ : C);
- }));
- enabled === _ ? await this.fetchAndDisplayReviews(state) : panel.find(".jhs-review-container, .jhs-review-footer").hide();
- return panel;
- }
- updateToggle(toggle, expanded) {
- toggle.attr("aria-expanded", String(expanded)), toggle.find(".toggle-text").text(expanded ? "折叠" : "展开"), toggle.find(".toggle-icon").text(expanded ? "▲" : "▼");
- }
- async fetchAndDisplayReviews(state) {
- if (state.loading || !state.isActive?.()) return;
- state.loading = true;
- const { movieId, panel } = state, container = panel.find(".jhs-review-container"), footer = panel.find(".jhs-review-footer");
- container.empty().append($('
').text("获取评论中...")), footer.empty();
- const pageSize = await storageManager.getSetting("reviewCount", 20);
- let reviews;
- try {
- reviews = await R(movieId, 1, pageSize);
- } catch (error) {
- error.toString().includes("簽名已過期") && show.error("生成签名失败, 请检查系统时间及时区是否正确!"), clog.error("获取评论失败:", error), clog.error("获取评论失败:", error);
- state.loading = false;
- return void this.renderRetry(container, "获取评论失败", (() => this.fetchAndDisplayReviews(state)));
- }
- if (!state.isActive?.()) return void (state.loading = false);
- state.loading = false, state.loaded = true;
- container.empty();
- if (!reviews.length) return void container.append($('
').text("无评论"));
- const keywords = await storageManager.getReviewFilterKeywordList();
- await this.displayReviews(state, reviews, container, keywords);
- reviews.length === pageSize ? this.bindLoadMore(state, pageSize, keywords, container, footer) : footer.append($('
').text("已加载全部评论"));
- }
- renderRetry(container, message, retry) {
- container.empty();
- const state = $('
').append(document.createTextNode(`${message} `));
- const button = $('
').on("click", retry);
- state.append(button), container.append(state);
- }
- bindLoadMore(state, pageSize, keywords, container, footer) {
- const button = $('
'), end = $('
已加载全部评论
').hide();
- footer.empty().append(button, end);
- button.on("click", (async () => {
- const nextPage = state.page + 1;
- button.text("加载中...").prop("disabled", true);
- try {
- const reviews = await R(state.movieId, nextPage, pageSize);
- if (!state.isActive?.()) return;
- state.page = nextPage;
- await this.displayReviews(state, reviews, container, keywords), reviews.length < pageSize ? (button.remove(), end.show()) : button.text("加载更多评论").prop("disabled", false);
- } catch (error) {
- clog.error("加载更多评论失败:", error), button.text("加载失败,请重试").prop("disabled", false);
- }
- }));
- }
- async displayReviews(state, reviews, container, keywords) {
- if (!reviews.length) return;
- const filter = keywords.length > 0 ? new RegExp(keywords.map(((value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"))).join("|")) : null;
- for (const review of reviews) {
- const content = String(review.content || "");
- if (filter?.test(content)) continue;
- const item = $('
'), meta = $('
'), body = $('
');
- meta.append($("
").addClass("jhs-review-author").text(review.username || "匿名用户"));
- const stars = $('
'), score = Math.max(0, Math.min(5, Number(review.score) || 0));
- for (let index = 0; index < score; index++) stars.append('
');
- meta.append(
- stars,
- $("
").text(utils.formatDate(review.created_at)),
- $("
").text(`点赞:${Number(review.likes_count) || 0}`),
- $("
").addClass("jhs-review-floor").text(`#${state.floorIndex++}楼`)
- );
- await this.appendReviewContent(body, content), item.append(meta, body), container.append(item);
- }
- }
- appendReviewContent(container, content) {
- const linkPattern = /ed2k:\/\/\|file\|[^|]+\|\d+\|[a-fA-F0-9]{32}\|\/|magnet:\?[^\s"'<>`,;\u4e00-\u9fa5,。?!()【】]+|https?:\/\/[^\s"'<>`,;\u4e00-\u9fa5,。?!()【】]+/g;
- let cursor = 0, match;
- while (match = linkPattern.exec(content)) {
- match.index > cursor && container.append(document.createTextNode(content.slice(cursor, match.index)));
- this.appendLinkControls(container, match[0]), cursor = match.index + match[0].length;
- }
- cursor < content.length && container.append(document.createTextNode(content.slice(cursor)));
- }
- appendLinkControls(container, value) {
- const isEd2k = value.startsWith("ed2k://"), isMagnet = value.startsWith("magnet:"), label = isEd2k ? "ED2K 链接" : isMagnet ? "Magnet 链接" : "打开链接";
- const isResource = isMagnet || isEd2k, wrapper = $(isResource ? '
' : '
');
- const main = $('
');
- const open = isEd2k ? $('
').text(label).on("click", (() => utils.copyToClipboard(label, value))) : $("
").addClass("jhs-review-link").attr({
- href: value,
- target: "_blank",
- rel: "noopener noreferrer"
- }).text(label);
- const copy = $('
').on("click", (() => utils.copyToClipboard(label, value)));
- main.append(open, copy), wrapper.append(main);
- if (isResource) {
- const actions = $('
');
- actions.append(`
`);
- wrapper.append(actions);
- }
- container.append(wrapper);
- }
- bindRightClickFilter() {
- $(document).off("contextmenu.jhsReviewFilter", ".review-content").on("contextmenu.jhsReviewFilter", ".review-content", (async (event) => {
- if (await storageManager.getSetting("enableTitleSelectFilter", _) !== _) return;
- const text = window.getSelection().toString();
- text && (event.preventDefault(), await utils.q(event, `是否将 '${text}' 加入评论区关键词?`, (async () => {
- await storageManager.saveReviewFilterKeyword(text), show.ok("操作成功, 刷新页面后生效");
- })));
- }));
- }
- };
- __name(_ReviewPlugin, "ReviewPlugin");
- var ReviewPlugin = _ReviewPlugin;
- var _FilterTitleKeywordPlugin = class _FilterTitleKeywordPlugin extends BasePlugin {
- getName() {
- return "FilterTitleKeywordPlugin";
- }
- async handle() {
- if (!isDetailPage) return;
- await this.bindDetailRoot(document);
- }
- async bindDetailRoot(root, { layerIndex = null } = {}) {
- if (await storageManager.getSetting("enableTitleSelectFilter", _) !== _) return;
- const host = $(root), selector = r ? ".title strong, .current-title" : l ? "h3" : ".current-title, .origin-title, .jhs-detail-title";
- host.off("contextmenu.jhsTitleFilter", selector).on("contextmenu.jhsTitleFilter", selector, ((e2) => {
- const t2 = window.getSelection().toString();
- if (t2) {
- e2.preventDefault();
- let n2 = {
- clientX: e2.clientX,
- clientY: e2.clientY + 80
- };
- utils.q(n2, `是否屏蔽标题关键词 ${t2}?`, (async () => {
- await storageManager.saveTitleFilterKeyword(t2), await jhsEventBus.emit("filter-rules-changed", { scope: "title-keyword" }), utils.closePage({ root: host, layerIndex });
- }));
- }
- }));
- }
- };
- __name(_FilterTitleKeywordPlugin, "FilterTitleKeywordPlugin");
- var FilterTitleKeywordPlugin = _FilterTitleKeywordPlugin;
- var _BlacklistPlugin = class _BlacklistPlugin extends BasePlugin {
- constructor() {
- super(...arguments), i(this, "blacklistSearchDebounced", null), i(this, "taskStatusUnsubscribe", null);
- }
- getName() {
- return "BlacklistPlugin";
- }
- async initCss() {
- return ``;
- }
- async addBlacklist(e2) {
- let t2 = {
- clientX: e2.clientX,
- clientY: e2.clientY + 80
- };
- const n2 = $("#addBlacklistBtn span").text().includes("已加入");
- let a2, i2;
- if (o.includes("/tags")) {
- const e3 = new URL(o);
- e3.searchParams.delete("page");
- const t3 = $("#jhs-check-tag").text().trim();
- a2 = {
- starId: "no-" + t3,
- name: "虚拟演员-" + t3,
- allName: ["虚拟演员"],
- role: "虚拟演员",
- movieType: t3,
- blacklistUrl: e3.toString()
- }, i2 = `是否将分类
${t3} 加入到黑名单中?`, n2 && (i2 = `分类
${t3} 已在黑名单中, 是否从当前页开始追加屏蔽?`);
- } else a2 = this.getActressPageInfo(), i2 = `是否将该演员
${a2.name} 加入到黑名单中?`, n2 && (i2 = `演员
${a2.name} 已在黑名单中, 是否从当前页开始追加屏蔽?`);
- const { starId: s2, name: r2, allName: c2, role: d2, movieType: h2, blacklistUrl: g2 } = a2;
- if (o.includes("page") && !o.includes("page=1") && (i2 += "
注意: 当前页面非第一页, 屏蔽数据将从此页面开始"), l) {
- const e3 = o.split("/star/")[1].split("/");
- if (e3.length > 1) {
- parseInt(e3[1]) > 1 && (i2 += "
注意: 当前页面非第一页, 屏蔽数据将从此页面开始");
- }
- }
- utils.q(t2, i2, (async () => {
- const e3 = this.getBean("TaskPlugin");
- navigator.locks.request(e3.singleTaskKey, {
- ifAvailable: true
- }, (async (e4) => {
- if (clog.debug("获取锁", e4), e4) {
- this.loadObj = loading();
- try {
- await storageManager.addBlacklistItem({
- starId: s2,
- name: r2,
- allName: c2,
- role: d2,
- movieType: h2,
- url: g2
- }), await this.filterActorVideo(r2, s2, null, l ? I : T);
- const e5 = show.ok(`屏蔽结束,是否跳转到最后一页: ${this.lastPageLink}`, {
- duration: -1,
- close: true,
- onClick: /* @__PURE__ */ __name(() => {
- e5.closeShow(), window.location.href = this.lastPageLink;
- }, "onClick")
- });
- } catch (t3) {
- clog.error(t3);
- const e5 = show.error("发生错误, 是否填转到解析失败的那一页? (点击并跳转)", {
- duration: -1,
- close: true,
- onClick: /* @__PURE__ */ __name(() => {
- e5.closeShow(), window.location.href = this.nextPageLink;
- }, "onClick")
- });
- } finally {
- this.loadObj.close();
- }
- } else show.error("当前有定时任务在后台执行中, 无法发起此操作");
- })).catch(((e4) => {
- clog.error("锁任务出现错误:", e4);
- }));
- }));
- }
- async resetBtnTip() {
- const e2 = this.getBean("TaskPlugin"), t2 = localStorage.getItem(e2.lastCheckBlacklistTimeKey) || "无", n2 = await storageManager.getSetting("checkBlacklist_intervalTime", 12);
- this.checkBlacklist_ruleTime = await storageManager.getSetting("checkBlacklist_ruleTime", 8760), $("#checkBlacklistBtn").attr("data-tip", `上次整批检测: ${t2}; 检测间隔时间: ${n2}小时`);
- }
- async openBlacklistDialog() {
- const e2 = this.getBean("TaskPlugin"), t2 = await storageManager.getSetting();
- let n2 = `
-
-
-
-
-
-
-
-
-
-
-
-
+ `,area:utils.isMobileMode()?utils.getResponsiveArea():["40%","80%"],success:d(async t=>{this.initEventListeners(),e&&e()},"success"),end:d(()=>{$(document).off("paste.searchImg")},"end")})}initEventListeners(){let e=$("#upload-area"),t=$("#image-file"),s=$("#select-image-btn"),a=$("#preview-area"),i=$("#preview-image"),r=$("#action-btns"),n=$("#handle-btn"),l=$("#cancel-btn"),c=$("#url-input-container"),h=$("#image-url"),u=$("#search-results"),p=$("#search-img-site-btns-container");e.on("dragover",(g=>{g.preventDefault(),e.addClass("highlight")})).on("dragleave",(()=>{e.removeClass("highlight")})).on("drop",(g=>{g.preventDefault(),e.removeClass("highlight"),g.originalEvent.dataTransfer.files&&g.originalEvent.dataTransfer.files[0]&&(this.handleImageFile(g.originalEvent.dataTransfer.files[0]),this.resetSearchUI())})),s.on("click",(()=>{t.trigger("click")})),t.on("change",(g=>{let m=g.target;m.files&&m.files[0]&&(this.handleImageFile(m.files[0]),this.resetSearchUI())})),$(document).on("paste.searchImg",(async g=>{let m=g.originalEvent.clipboardData.items;for(let v=0;v
{let g=i.attr("src");if(g){if(!this.isUploading){this.isUploading=!0;try{let m=await this.searchByImage(g);if(!m)return;r.hide(),u.show(),p.empty();let f=this.getRuntimeService("storage"),v="jhs_selectedSites",b=JSON.parse(f.getLocal(v)||"{}");m.targets.forEach((w=>{let S=b[w.name]!==!1,j=$('').attr({href:w.url,title:w.name}),k=$('').attr("data-site-name",w.name).prop("checked",S);j.append(k,$("
").attr({src:w.iconUrl,alt:w.name}),$("").text(w.name)),p.append(j)})),p.on("change",".site-checkbox",(w=>{let S=$(w.currentTarget),j=S.data("site-name");b[j]=S.is(":checked"),f.setLocal(v,JSON.stringify(b))})),p.show()}finally{this.isUploading=!1}}}else show.info("请粘贴或上传图片")})),l.on("click",(()=>{a.hide(),c.hide(),t.val(""),h.val("")})),h.on("change",(()=>{utils.isUrl(h.val())&&(i.attr("src",h.val()),a.show())})),$("#openAll").on("click",(()=>{$(".search-img-site-btn").each(((g,m)=>{$(m).find(".site-checkbox").is(":checked")&&window.open($(m).attr("href"))}))}))}resetSearchUI(){$("#action-btns").show(),$("#search-results").hide(),$("#search-img-site-btns-container").hide().empty()}handleImageFile(e){let t=document.getElementById("preview-image"),s=document.getElementById("preview-area"),a=document.getElementById("url-input-container");if(!t||!s||!a)return;if(!e.type.match("image.*"))return void show.info("请选择图片文件");let i=new FileReader;i.onload=r=>{t.src=String(r.target?.result||""),s.style.display="block",a.style.display="none",$("#handle-btn")[0]?.click()},i.readAsDataURL(e)}async searchByImage(e){let t=loading();try{e.startsWith("data:")&&show.info("开始上传图片...");let s=await this.getRuntimeService("scope")();return await this.getRuntimeService("imageSearch").resolve(e,{scope:s})}catch(s){show.error(`搜索失败: ${s instanceof Error?s.message:String(s)}`),clog.error("搜索失败:",s)}finally{t.close()}}};d(ar,"SearchByImagePlugin");var Is=ar;async function $e(o,e=4,t){let s=new Array(o.length),a=0,i=d(async()=>{for(;a=t&&a<=s?a:e}d(ae,"parseNumberSetting");function _e(o){if(o instanceof Date)return Number.isFinite(o.getTime())?o.getTime():null;if(typeof o=="number")return Number.isFinite(o)&&o>=0?o:null;if(typeof o!="string")return null;let e=o.trim();if(!e)return null;if(/^\d{13,16}$/.test(e)){let a=Number(e);return Number.isFinite(a)?a:null}let t=e.match(/^(\d{4})-(\d{2})-(\d{2})[ T](\d{2}):(\d{2}):(\d{2})$/);if(t){let a=t.slice(1).map(Number),i=new Date(a[0],a[1]-1,a[2],a[3],a[4],a[5]);return i.getFullYear()===a[0]&&i.getMonth()===a[1]-1&&i.getDate()===a[2]&&i.getHours()===a[3]&&i.getMinutes()===a[4]&&i.getSeconds()===a[5]?i.getTime():null}let s=new Date(e).getTime();return Number.isFinite(s)?s:null}d(_e,"parseTaskTimestamp");function pt(o,e,t=Date.now()){let s=ae(e,0,{min:0}),a=_e(o),i=_e(t);return s>0&&a!=null&&i!=null&&i>=a&&i-a>=36e5*s}d(pt,"shouldSkipStopped");function Kt(o){let e=null,t=-1/0;for(let s of o){let a=_e(s);a!=null&&a>t&&(t=a,e=s)}return e}d(Kt,"selectLatestPublishTime");function gt(o,e=window.location.href){if(!o)return null;try{let t=new URL(String(o),e);return["http:","https:"].includes(t.protocol)?t.href:null}catch(t){return clog.debug("外部 URL 无效,已忽略",t),null}}d(gt,"normalizeHttpUrl");function ir(o,e=window.location.href){return gt(o,e)?.replace(/^https:\/\/[^/]+\/rhe951l4q(?=\/)/i,"https://c0.jdbstatic.com")??null}d(ir,"normalizeJavdbMediaUrl");function yl(o){let e=String(o||"").trim();return/^(?:[a-f\d]{40}|[a-z2-7]{32})$/i.test(e)?e.toUpperCase():null}d(yl,"normalizeBtihHash");function jl(o){let e=String(o||"").split(/[\s,,;;]+/).map((s=>T(s))).filter((s=>typeof s=="string")),t=e.filter((s=>/^(?:FC2-)?[A-Z\d]+(?:-[A-Z\d]+)+$/i.test(s)));return{recognized:e.length,values:[...new Set(t.map((s=>s.toUpperCase())))],invalid:e.filter((s=>!t.includes(s)))}}d(jl,"parseCarNumberText");function Sl(o,e="https://javdb.com"){return`${e}/search?q=${encodeURIComponent(String(o??""))}`}d(Sl,"buildFallbackCarUrl");async function de(o,{context:e="视频",notify:t=!1,message:s="当前视频源无法播放"}={}){if(!o||typeof o.play!="function")return clog.warn(`${e}播放失败:媒体元素不可用`),t&&show.error(s),!1;try{return await o.play(),!0}catch(a){clog.warn(`${e}播放失败`,a);let i=a&&typeof a=="object"&&"name"in a?String(a.name):"";return t&&!["NotAllowedError","AbortError"].includes(i)&&show.error(s),!1}}d(de,"safePlay");var Re=Object.freeze({all:"全部",waitCheck:"待鉴定",favorite:"收藏",hasDown:"下载",hasWatch:"已看",blockedItems:"屏蔽项",favoriteUndownloaded:"收藏未下载",favoriteUnwatched:"收藏未观看",downloadedUnwatched:"下载未观看",recent7d:"最近 7 天"}),mt=Object.freeze(["all","waitCheck","favorite","hasDown","hasWatch"]),Gt=Object.freeze(["blockedItems","favoriteUndownloaded","favoriteUnwatched","downloadedUnwatched","recent7d"]),mh=new Set([...mt,...Gt]);function Te(o){return o==="filter"?"blockedItems":typeof o=="string"&&mh.has(o)?o:"waitCheck"}d(Te,"normalizeQuickFilterKey");function ft(o,e={}){return!!(o.blocked||e.keyword||e.actorBlacklist||e.actressBlacklist)}d(ft,"isHardHidden");function rr(o,e,{visibilityReasons:t={},recent:s=!1}={}){let a=Te(o),i=ft(e,t);return a==="all"?!0:a==="blockedItems"?i:i?!1:a==="waitCheck"?!oe(e):a==="favorite"?!!e.favorite:a==="hasDown"?!!e.downloaded:a==="hasWatch"?!!e.watched:a==="favoriteUndownloaded"?!!e.favorite&&!e.downloaded:a==="favoriteUnwatched"?!!e.favorite&&!e.watched:a==="downloadedUnwatched"?!!e.downloaded&&!e.watched:a==="recent7d"&&s}d(rr,"matchesQuickFilter");function xl({filter:o,flags:e,visibilityReasons:t,recent:s}){return rr(o,e,{visibilityReasons:t,recent:s})}d(xl,"shouldShowItem");var kl=Object.freeze(["native-javdb","native-javbus","u9a9","u3c3","sukebei","btsow"]);function nr(o,e){return!o||!String(o.magnet||"").startsWith("magnet:")?null:{title:String(o.title||""),magnet:o.magnet,size:o.size||"",date:o.date||"",seeders:Number(o.seeders)||0,leechers:Number(o.leechers)||0,source:e,files:Array.isArray(o.files)?o.files:[]}}d(nr,"normalizeMagnetResult");function Cl(o){let e=new URL(o).searchParams.get("xt")?.match(/^urn:btih:([a-z2-7]{32}|[a-f\d]{40})$/i)?.[1];return e?e.toUpperCase():null}d(Cl,"extractInfoHash");function Es(o){let e=new Map;return o.forEach((t=>{let s=Cl(t.magnet)||`${t.source}:${t.magnet}`,a=e.get(s);a?a.sources=[...new Set([...a.sources||[a.source],t.source])]:e.set(s,{...t,sources:[t.source]})})),[...e.values()]}d(Es,"deduplicateMagnetResults");var cr=class cr{constructor(e=[]){this.sources=new Map,e.forEach((t=>this.register(t)))}register(e){if(!e?.id||!e.name||typeof e.search!="function"||typeof e.targetUrl!="function")throw new TypeError("Invalid magnet provider");return this.sources.set(e.id,{enabled:!0,priority:100,...e}),this}get(e){return this.sources.get(e)||null}getEnabledSources(){return[...this.sources.values()].filter((e=>e.enabled)).sort(((e,t)=>e.priority-t.priority))}};d(cr,"MagnetSourceRegistry");var Qt=cr;function Rs(o){let e=new URL(o);if(e.protocol!=="https:")throw new TypeError("Source URL must use https");return e.origin}d(Rs,"validateHttpsBaseUrl");function bt(o){let e=["id","name","enabled","priority","searchUrlTemplate","targetUrlTemplate","parserType","rowSelector","titleSelector","magnetSelector","sizeSelector","dateSelector","seedersSelector","leechersSelector","resultsPath","titlePath","hashPath","magnetPath","sizePath","datePath","seedersPath"];if(Object.keys(o).some((t=>!e.includes(t))))throw new TypeError("Unsupported custom source field");if(!["torrent-table","magnet-links","json"].includes(o.parserType))throw new TypeError("Unsupported parser type");if(!String(o.name||"").trim())throw new TypeError("Source name is required");if(Rs(o.searchUrlTemplate.replace("{keyword}","test")),Rs((o.targetUrlTemplate||o.searchUrlTemplate).replace("{keyword}","test")),o.parserType==="torrent-table"&&(!o.rowSelector?.trim()||!o.magnetSelector?.trim()))throw new TypeError("表格来源必须填写结果行选择器和磁力选择器");if(o.parserType==="json"&&(!o.resultsPath?.trim()||!o.magnetPath?.trim()&&!o.hashPath?.trim()))throw new TypeError("JSON 来源必须填写结果数组路径,并填写磁力路径或哈希路径");return{...o,name:o.name.trim(),targetUrlTemplate:o.targetUrlTemplate||o.searchUrlTemplate}}d(bt,"validateCustomMagnetSource");function _l(o,e=[],t=[],s=[]){let a=`${o.title||""} ${(o.files||[]).join(" ")}`,i=d((u,p)=>u.type==="regex"?new RegExp(u.pattern,"i").test(p):p.toLowerCase().includes(u.pattern.toLowerCase()),"matches"),r=e.filter((u=>u.enabled&&i(u,a))),n=!1,l=0,c=[];return[...t.map((u=>({...u,value:o.title||""}))),...s.map((u=>({...u,value:(o.files||[]).join(" ")})))].filter((u=>u.enabled)).forEach((u=>{i(u,u.value)&&(c.push(u.id||u.pattern),u.action==="hide"?n=!0:l+=Number(u.penalty)||0)})),{...o,tags:r.map((u=>u.name)),customTagWeight:r.reduce(((u,p)=>u+(Number(p.weight)||0)),0),hidden:n,filterPenalty:l,filteredReasons:c}}d(_l,"applyMagnetRules");function or(o,e){let t=new Map;return $(o).find('a[href^="magnet:"],[data-clipboard-text^="magnet:"]').each(((s,a)=>{let i=$(a),r=i.attr("href")||i.attr("data-clipboard-text");if(!r)return;let n=i.closest(".item, .magnet-name, tr, .panel-block"),l=n.find(".name, .magnet-name, .title").first().text().trim()||i.text().trim()||"本站磁力",c=nr({title:l,magnet:r,size:n.find(".meta, .size").first().text().trim()},e);c&&t.set(Cl(r)||r,c)})),[...t.values()]}d(or,"parseNativeMagnets");function vt(o,e){return String(e||"").split(".").filter(Boolean).reduce(((t,s)=>t?.[s]),o)}d(vt,"readJsonPath");function lr(o,e,t){if(o=bt(o),o.parserType==="json")return(vt(e,o.resultsPath)||[]).map((i=>nr({title:vt(i,o.titlePath),magnet:o.magnetPath?vt(i,o.magnetPath):`magnet:?xt=urn:btih:${vt(i,o.hashPath)}`,size:vt(i,o.sizePath),date:vt(i,o.datePath),seeders:vt(i,o.seedersPath)},`custom:${t}`))).filter(Boolean);let s=utils.htmlTo$dom(e);return(o.parserType==="magnet-links"?s.find('a[href^="magnet:"]'):s.find(o.rowSelector)).map(((i,r)=>{let n=$(r),l=o.parserType==="magnet-links"?n:n.find(o.magnetSelector).first();return nr({title:o.parserType==="magnet-links"?n.text().trim():n.find(o.titleSelector).first().text().trim(),magnet:l.attr("href")||l.attr("data-magnet"),size:n.find(o.sizeSelector).text().trim(),date:n.find(o.dateSelector).text().trim(),seeders:n.find(o.seedersSelector).text().trim(),leechers:n.find(o.leechersSelector).text().trim()},`custom:${t}`)})).get().filter(Boolean)}d(lr,"parseCustomMagnetResponse");var Ns=Object.freeze([{id:"native-javdb",name:"JavDB 本站",type:"本站资源",domain:"javdb.com",priority:10,enabled:!0},{id:"native-javbus",name:"JavBus 本站",type:"本站资源",domain:"javbus.com",priority:11,enabled:!0}]);function It(o){if(!o.name?.trim()||!o.pattern?.trim())throw new TypeError("规则名称和匹配内容不能为空");if(o.type==="regex")try{new RegExp(o.pattern)}catch{throw new TypeError("正则表达式无效")}return{...o,name:o.name.trim(),pattern:o.pattern.trim()}}d(It,"validateRule");function $l(o,e=null){let t=o.parserType||"magnet-links",s={id:e?.id||`source-${Date.now()}`,name:String(o.name||"").trim(),enabled:!!o.enabled,priority:Number(o.priority)||100,searchUrlTemplate:String(o.searchUrlTemplate||"").trim(),targetUrlTemplate:String(o.targetUrlTemplate||o.searchUrlTemplate||"").trim(),parserType:t};if(!s.name)throw new TypeError("来源名称不能为空");return t==="torrent-table"&&Object.assign(s,{rowSelector:o.rowSelector,titleSelector:o.titleSelector,magnetSelector:o.magnetSelector,sizeSelector:o.sizeSelector,dateSelector:o.dateSelector,seedersSelector:o.seedersSelector,leechersSelector:o.leechersSelector}),t==="json"&&Object.assign(s,{resultsPath:o.resultsPath,titlePath:o.titlePath,magnetPath:o.magnetPath,hashPath:o.hashPath,sizePath:o.sizePath,datePath:o.datePath,seedersPath:o.seedersPath}),bt(s)}d($l,"buildCustomMagnetSource");function Yt(o,e=!1){return be(o,e)}d(Yt,"parseBooleanSetting");function Ds(o,e,t={}){let s=Number(o);if(!Number.isFinite(s))return e;let a=Math.round(s);return t.min!=null&&at.max?t.max:a}d(Ds,"parseFiniteNumberSetting");function Ye(o,e){if(["enable123Offline","enable115Offline","enable115Match","enable115LoginRedirect"].includes(o))return Yt(e,!1);if(o==="offlineProviderMode")return["ask","123","115"].includes(String(e))?String(e):"ask";if(o==="oneOneFiveConcurrency")return Ds(e,4,{min:1,max:10});if(o==="oneOneFiveCacheMinutes")return Ds(e,60,{min:1,max:1440});throw new TypeError(`未知云盘设置: ${o}`)}d(Ye,"normalizeCloudSetting");var dr=class dr{constructor(e=storageManager){this.storage=e}async getArray(e){let t=await this.storage.getSetting(e,"[]");if(Array.isArray(t))return t;try{let s=JSON.parse(t||"[]");return Array.isArray(s)?s:[]}catch{return[]}}async saveArray(e,t){if(!Array.isArray(t))throw new TypeError("配置必须是数组");return await this.storage.saveSettingItem(e,JSON.stringify(t)),t}async updateArray(e,t){if(typeof this.storage.updateSetting=="function"){let i;return await this.storage.updateSetting(r=>{let n=r[e],l;if(Array.isArray(n))l=n;else try{let c=JSON.parse(n||"[]");l=Array.isArray(c)?c:[]}catch{l=[]}if(i=t(l),!Array.isArray(i))throw new TypeError("配置必须是数组");r[e]=JSON.stringify(i)}),i}let s=await this.getArray(e),a=t(s);return await this.saveArray(e,a),a}getMagnetSources(){return this.getArray("customMagnetSources")}saveMagnetSources(e){return e.forEach(bt),this.saveArray("customMagnetSources",e)}getMagnetTagRules(){return this.getArray("magnetTagRules")}saveMagnetTagRules(e){return e.forEach(It),this.saveArray("magnetTagRules",e)}getMagnetFilterRules(){return this.getArray("magnetFilterRules")}saveMagnetFilterRules(e){return e.forEach(It),this.saveArray("magnetFilterRules",e)}async getBuiltInSources(){return await this.getArray("magnetBuiltInSources")}saveBuiltInSources(e){return e.forEach((t=>{if(!kl.includes(t.id))throw new TypeError("未知的内置磁力源");if(t.baseUrl&&Rs(t.baseUrl),t.priority!=null&&(!Number.isFinite(Number(t.priority))||Number(t.priority)<1))throw new TypeError("来源优先级无效")})),this.saveArray("magnetBuiltInSources",e)}async getScreenshotSettings(){return{mode:await this.storage.getSetting("screenshotMode","auto"),providers:await this.getArray("screenshotProviders")}}async saveScreenshotMode(e){return await this.storage.saveSettingItem("screenshotMode",e),e}async saveScreenshotProviders(e){return this.saveArray("screenshotProviders",e)}async saveScreenshotSettings(e){return typeof this.storage.patch=="function"?this.storage.patch({screenshotMode:e.mode,screenshotProviders:JSON.stringify(e.providers)}):(await this.saveScreenshotMode(e.mode),await this.saveScreenshotProviders(e.providers),e)}async getCloudSettings(){return{enable123Offline:Yt(await this.storage.getSetting("enable123Offline",!0),!0),enable115Offline:Yt(await this.storage.getSetting("enable115Offline",!1),!1),enable115Match:Yt(await this.storage.getSetting("enable115Match",!1),!1),enable115LoginRedirect:Yt(await this.storage.getSetting("enable115LoginRedirect",!1),!1),providerMode:Ye("offlineProviderMode",await this.storage.getSetting("offlineProviderMode","ask")),concurrency:Ds(await this.storage.getSetting("oneOneFiveConcurrency",4),4,{min:1,max:10}),cacheMinutes:Ds(await this.storage.getSetting("oneOneFiveCacheMinutes",60),60,{min:1,max:1440})}}async saveCloudSetting(e,t){let a={enable123Offline:"enable123Offline",enable115Offline:"enable115Offline",enable115Match:"enable115Match",enable115LoginRedirect:"enable115LoginRedirect",offlineProviderMode:"offlineProviderMode",oneOneFiveConcurrency:"oneOneFiveConcurrency",oneOneFiveCacheMinutes:"oneOneFiveCacheMinutes"}[e];if(!a)throw new TypeError(`未知云盘设置: ${e}`);let i=Ye(e,t);return await this.storage.saveSettingItem(a,i),i}async saveCloudSettings(e){let t={enable123Offline:Ye("enable123Offline",e.enable123Offline),enable115Offline:Ye("enable115Offline",e.enable115Offline),enable115Match:Ye("enable115Match",e.enable115Match),enable115LoginRedirect:Ye("enable115LoginRedirect",e.enable115LoginRedirect),offlineProviderMode:Ye("offlineProviderMode",e.providerMode||"ask"),oneOneFiveConcurrency:Ye("oneOneFiveConcurrency",e.concurrency),oneOneFiveCacheMinutes:Ye("oneOneFiveCacheMinutes",e.cacheMinutes)};if(typeof this.storage.patch=="function")return this.storage.patch(t);for(let[s,a]of Object.entries(t))await this.storage.saveSettingItem(s,a);return t}async exportConfig(){return{customMagnetSources:await this.getMagnetSources(),magnetTagRules:await this.getMagnetTagRules(),magnetFilterRules:await this.getMagnetFilterRules(),magnetBuiltInSources:await this.getBuiltInSources(),screenshot:await this.getScreenshotSettings()}}async importConfig(e){let t;try{t=JSON.parse(e)}catch(a){throw new TypeError(`配置格式错误:${a instanceof Error?a.message:String(a)}`)}if(!t||typeof t!="object"||Array.isArray(t))throw new TypeError("配置格式错误:根节点必须是对象");let s=[];if(Object.hasOwn(t,"customMagnetSources")){if(!Array.isArray(t.customMagnetSources))throw new TypeError("自定义磁力源必须是数组");t.customMagnetSources.forEach(bt),s.push(()=>this.saveMagnetSources(t.customMagnetSources))}if(Object.hasOwn(t,"magnetTagRules")){if(!Array.isArray(t.magnetTagRules))throw new TypeError("标签规则必须是数组");t.magnetTagRules.forEach(It),s.push(()=>this.saveMagnetTagRules(t.magnetTagRules))}if(Object.hasOwn(t,"magnetFilterRules")){if(!Array.isArray(t.magnetFilterRules))throw new TypeError("过滤规则必须是数组");t.magnetFilterRules.forEach(It),s.push(()=>this.saveMagnetFilterRules(t.magnetFilterRules))}if(Object.hasOwn(t,"magnetBuiltInSources")){if(!Array.isArray(t.magnetBuiltInSources))throw new TypeError("内置磁力源配置必须是数组");s.push(()=>this.saveBuiltInSources(t.magnetBuiltInSources))}if(Object.hasOwn(t,"screenshot")){if(!t.screenshot||!["auto","manual"].includes(t.screenshot.mode)||!Array.isArray(t.screenshot.providers))throw new TypeError("截图配置无效");s.push(()=>this.saveScreenshotSettings(t.screenshot))}for(let a of s)await a();return t}};d(dr,"ResourceSettingsService");var rt=dr;var Ls=Object.freeze([Object.freeze({id:"javstore",name:"JavStore",domain:"javstore.net",priority:10,enabled:!0}),Object.freeze({id:"projectjav",name:"ProjectJav",domain:"projectjav.com",priority:20,enabled:!1,implemented:!1}),Object.freeze({id:"18av",name:"18AV",domain:"18av.mm-cg.com",priority:30,enabled:!1,implemented:!1})]);var hr="x7k9p3",Tl="jhs_credential_install_secret",Al="AES:",Pl="";function ur(){let o=globalThis.localStorage?.getItem?.(Tl)||Pl;if(o)return o;let e=crypto.getRandomValues(new Uint8Array(32));return o=Rl(e.buffer),Pl=o,globalThis.localStorage?.setItem?.(Tl,o),o}d(ur,"getInstallationSecret");async function Il(o){let e=new TextEncoder,t=await crypto.subtle.importKey("raw",e.encode(`${o}.jhs.v1`),{name:"PBKDF2"},!1,["deriveKey"]);return crypto.subtle.deriveKey({name:"PBKDF2",salt:e.encode("jhs-backup"),iterations:1e5,hash:"SHA-256"},t,{name:"AES-GCM",length:256},!1,["encrypt","decrypt"])}d(Il,"getEncryptionKey");function Rl(o){let e=new Uint8Array(o),t=32768,s="";for(let a=0;anew TextDecoder().decode(await crypto.subtle.decrypt({name:"AES-GCM",iv:a},await Il(n),i)),"decrypt");try{return await r(e)}catch(n){if(!t||e===hr)throw n;return r(hr)}}d(Ze,"decryptData");async function Xt(o){return Ze(o)}d(Xt,"decryptPortableBackup");async function Dl(o,e=ur()){if(!o||!o.startsWith(Al))return o;try{return await Ze(o.slice(Al.length),e)}catch{return console?.warn?.("[凭证] 凭证解密失败,按原文返回"),o}}d(Dl,"decryptCredential");var vh="zh-cn",bh=Object.freeze({"zh-cn":Object.freeze({pagination:Object.freeze({first:"首页",first_title:"首页",last:"尾页",last_title:"尾页",prev:"上一页",prev_title:"上一页",next:"下一页",next_title:"下一页",all:"全部",page_size:"每页行数"})})});function ze(o,e,t){if(typeof o!="function")throw new TypeError("Tabulator runtime is required");(typeof e=="string"?globalThis.document?.querySelector(e):e)?.classList?.add("jhs-table");let a=(t.paginationSizeSelector??[20,50,100,1e3]).filter(i=>Number.isInteger(i)&&i>0);return new o(e,{layout:"fitColumns",responsiveLayout:"collapse",pagination:!0,paginationMode:"local",height:"100%",paginationSize:a[0]??20,...t,paginationSizeSelector:a,locale:t.locale??vh,langs:t.langs??bh})}d(ze,"createJhsTable");var Ue=d(o=>o instanceof Error?o.message:String(o),"errorMessage");async function Nl(o){if(typeof o?.getProfile=="function")return o.getProfile();let e=await storageManager.getSetting();return{url:e.webDavUrl,username:e.webDavUsername,password:await Dl(e.webDavPassword||"")}}d(Nl,"resolveWebDavProfile");async function Ll(o){try{let e=document.createElement("input");e.type="file",e.accept=".json";let t=d(()=>e.remove(),"cleanup");e.onchange=async s=>{let a=s.currentTarget.files?.[0];if(!a)return void t();let i=new FileReader;i.onload=async r=>{t();try{let n=String(r.currentTarget.result||""),l=JSON.parse(n);if(!l||typeof l!="object"||Array.isArray(l))throw new Error("文件内容不是有效的数据对象");let c=loading();try{let h=await storageManager.exportData(),u=await storageManager.diffData(h,l);c.close(),o(u,l,null)}catch(h){c.close(),clog.error(h),show.error("差异分析失败: "+Ue(h))}}catch(n){clog.error(n),show.error("导入失败:文件内容不是有效的JSON格式 "+Ue(n))}},i.onerror=()=>{t(),show.error("读取文件时出错")},i.readAsText(a)},document.body.appendChild(e),e.click(),setTimeout(t,3e5)}catch(e){clog.error(e),show.error("导入数据时出错: "+Ue(e))}}d(Ll,"importSettingData");async function Bl(o,e){let t=await Nl(e),s=t.url;if(!s)return void show.error("请填写webDav服务地址并保存后, 再试此功能");let a=t.username;if(!a)return void show.error("请填写webDav用户名并保存后, 再试此功能");if(!t.password)return void show.error("请填写webDav密码并保存后, 再试此功能");let i=loading();try{let r=t.password,n=utils.getNowStr("_","_")+".json",l=JSON.stringify(await storageManager.exportData());l=await El(l),await e.createClient({url:s,username:a,password:r}).backup(o,n,l),show.ok("备份完成")}catch(r){clog.error(r),show.error(Ue(r))}finally{i.close()}}d(Bl,"backupDataByWebDav");async function Ml(o,e,t){let s=await Nl(t),a=s.url;if(!a)return void show.error("请填写webDav服务地址并保存后, 再试此功能");let i=s.username;if(!i)return void show.error("请填写webDav用户名并保存后, 再试此功能");if(!s.password)return void show.error("请填写webDav密码并保存后, 再试此功能");let r=loading();try{let n=s.password,l=t.createClient({url:a,username:i,password:n}),c=await l.getBackupList(o);e(c,l,"WebDav")}catch(n){clog.error(n),show.error(`发生错误: ${Ue(n)}`)}finally{r.close()}}d(Ml,"backupListBtnByWebDav");function wh(o,e,t,s,a,i){let r=d(c=>{let h=["B","KB","MB","GB","TB","PB"],u=0,p=c;for(;p>=1024&&u!c||c.length===0?'暂无数据
':c.map((h,u)=>`
+
+
${C(h.name)}
+
${r(h.size)} · ${utils.getNowStr("-",":",h.createTime)}
+
+
+
+
-
-
-
-
- `;
- layer.open({
- type: 1,
- title: "演员黑名单",
- content: n2,
- scrollbar: false,
- area: utils.getDialogArea("xl"),
- anim: -1,
- success: /* @__PURE__ */ __name(async (t3) => {
- const dialog = $(t3).find(".layui-layer-content > div").first().addClass("jhs-blacklist-layout").removeAttr("style"), toolbar = dialog.children("div").first().addClass("jhs-blacklist-toolbar").removeAttr("style");
- toolbar.children("div").addClass("jhs-blacklist-toolbar__group").removeAttr("style"), toolbar.find("select,input,a").removeAttr("style"), dialog.find("#table-container").removeAttr("style");
- dialog.find("#table-container").before('
'), JhsSelect.enhance(t3);
- this.renderTaskStatus(), this.taskStatusUnsubscribe?.(), this.taskStatusUnsubscribe = jhsEventBus.on("task-status-changed", (() => this.renderTaskStatus()));
- await this.loadTableData();
- const content = $(t3).find(".layui-layer-content"), search = content.find("#searchValue");
- this.blacklistSearchDebounced = utils.debounce((() => void this.reloadTable()), 200), content.on("click", "#cleanQueryBtn", (async () => {
- search.val(""), JhsSelect.setValue("#dataType", "", false), JhsSelect.setValue("#statusType", "", false), JhsSelect.setValue("#urlType", "", false), await this.reloadTable();
- })).on("input", "#searchValue", this.blacklistSearchDebounced).on("change", "#dataType,#statusType,#urlType", (async () => {
- await this.reloadTable();
- })).on("click", "#toSetting", (() => {
- this.getBean("SettingPlugin").openSettingDialog("task-panel", (() => {
- $("#setting-blacklist").css({
- border: "1px solid var(--jhs-status-filter)"
- });
- }));
- })).on("click", ".open-url", ((e3) => {
- e3.preventDefault();
- const t4 = $(e3.currentTarget), n3 = t4.attr("data-url"), a2 = t4.attr("data-name");
- utils.openPage(n3, a2, true, e3);
- })).on("click", "#checkBlacklistBtn", ((event) => {
- const button = $(event.currentTarget), label = button.find("span").last(), previous = label.text();
- if (button.attr("aria-busy") === "true") return;
- button.attr("aria-busy", "true").prop("disabled", true), label.text("检测中…"), navigator.locks.request(e2.singleTaskKey, { ifAvailable: true }, (async (lock) => {
- lock ? await e2.checkBlacklist(true) : show.error("后台任务正在运行,请稍后再试");
- })).catch(((error) => {
- clog.error("锁任务出现错误:", error), e2.isNetworkBlocked(error) && show.error(error.message || "任务执行失败");
- })).finally((() => {
- button.removeAttr("aria-busy").prop("disabled", false), label.text(previous);
- }));
- }));
- }, "success"),
- end: /* @__PURE__ */ __name(async () => {
- this.blacklistSearchDebounced?.cancel?.(), this.blacklistSearchDebounced = null, this.taskStatusUnsubscribe?.(), this.taskStatusUnsubscribe = null, this.tableObj && (this.tableObj.destroy(), this.tableObj = null), await jhsEventBus.emit("blacklist-rules-changed");
- }, "end")
- });
- }
- renderTaskStatus() {
- const container = $("#blacklist-task-status");
- if (!container.length) return;
- const snapshot = this.getBean("TaskPlugin").getTaskStatusSnapshot("blacklist"), labels = { idle: "正常", running: "运行中", pending: "等待下一次任务检查", due: "待运行" }, format = /* @__PURE__ */ __name((value) => value ? new Date(value).toLocaleString() : "无", "format");
- container.empty().append($('
').text(`黑名单:${labels[snapshot.state]}`), $('
').text(`上次完成 ${format(snapshot.completedAt)};下次检查 ${snapshot.nextAt ? format(snapshot.nextAt) : "立即"}`));
- }
- async reloadTable() {
- if (!this.tableObj) return;
- const e2 = await this.getTableData();
- this.tableObj.setData(e2);
- }
- async getTableData() {
- const e2 = this.getBean("TaskPlugin"), t2 = await storageManager.getBlacklist(), n2 = await storageManager.getBlacklistCarList(), a2 = String($("#searchValue").val() || "").trim().toLocaleLowerCase(), i2 = $("#statusType").val(), s2 = $("#dataType"), o2 = s2.val(), r2 = $("#urlType").val(), l2 = t2.length;
- let c2 = 0, d2 = 0;
- const h2 = t2.map(((t3) => {
- t3.role === B ? c2++ : t3.role === P && d2++;
- let n3 = false;
- return n3 = shouldSkipStopped(t3.lastPublishTime, this.checkBlacklist_ruleTime), {
- ...t3,
- isUnCheck: n3
- };
- })).filter(((item) => {
- const aliases = Array.isArray(item.allName) ? item.allName.join(" ") : item.allName || "", searchable = `${item.name || ""} ${aliases} ${item.starId || ""}`.toLocaleLowerCase(), searchMatch = !a2 || searchable.includes(a2), statusMatch = !i2 || "normal" === i2 && !item.isUnCheck || "stop" === i2 && item.isUnCheck, roleMatch = !o2 || item.role === o2, hasCategory = item.url.includes("t="), urlMatch = !r2 || "hasT" === r2 && hasCategory || "noT" === r2 && !hasCategory;
- return searchMatch && statusMatch && roleMatch && urlMatch;
- }));
- s2.html(`
-
-
-
- `), JhsSelect.setValue(s2, o2, false);
- const g2 = /* @__PURE__ */ new Map();
- for (const m2 of n2) {
- const e3 = m2.starId;
- g2.has(e3) || g2.set(e3, []), g2.get(e3).push(m2);
- }
- const p2 = h2.map(((e3) => {
- const t3 = e3.starId, n3 = g2.get(t3) || [];
- return {
- ...e3,
- carList: n3,
- count: n3.length
- };
- }));
- return this.currentCarCount = p2.reduce(((e3, t3) => e3 + (t3.count || 0)), 0), p2;
- }
- async loadTableData() {
- this.checkBlacklist_ruleTime = parseNumberSetting(await storageManager.getSetting("checkBlacklist_ruleTime"), 8760, { min: 0 });
- const e2 = await this.getTableData(), placeholder = document.createElement("div");
- renderStateView(placeholder, { type: "empty", title: "没有符合当前筛选条件的黑名单记录" });
- this.tableObj = new Tabulator("#table-container", {
- layout: "fitColumns",
- placeholder,
- virtualDom: true,
- data: e2,
- pagination: true,
- paginationMode: "local",
- paginationSize: 20,
- paginationSizeSelector: [20, 50, 100, 1e3, true],
- paginationCounter: /* @__PURE__ */ __name((e3, t2, n2, a2, i2) => `演员: ${a2} 番号总数: ${this.currentCarCount}
`, "paginationCounter"),
- responsiveLayout: "collapse",
- responsiveLayoutCollapse: true,
- columnDefaults: {
- headerHozAlign: "center",
- hozAlign: "center"
- },
- index: "starId",
- columns: [{
- title: "演员",
- field: "name",
- sorter: "string",
- minWidth: 100,
- responsive: 0,
- headerSort: false,
- formatter: /* @__PURE__ */ __name((e3, t2, n2) => {
- const a2 = e3.getData(), url = normalizeHttpUrl(a2.url), link = document.createElement("a");
- link.className = "open-url", link.textContent = String(a2.name || ""), link.dataset.name = String(a2.name || "");
- return url ? (link.href = url, link.dataset.url = url, link.target = "_blank", link.rel = "noopener noreferrer") : (link.href = "#", link.setAttribute("aria-disabled", "true")), link;
- }, "formatter")
- }, {
- title: "性别角色",
- field: "role",
- sorter: "string",
- width: 120,
- responsive: 5,
- formatter: /* @__PURE__ */ __name((e3, t2, n2) => {
- const a2 = e3.getData().role;
- let i2 = a2;
- return a2 === B ? i2 = "男演员" : a2 === P && (i2 = "女演员"), i2;
- }, "formatter")
- }, {
- title: "影视类别",
- field: "movieType",
- sorter: "string",
- width: 120,
- responsive: 5,
- formatter: /* @__PURE__ */ __name((e3, t2, n2) => {
- const a2 = e3.getData().movieType;
- let i2 = a2;
- return a2 === D ? i2 = "有码" : a2 === A && (i2 = "无码"), i2;
- }, "formatter")
- }, {
- title: "屏蔽类型",
- field: "url",
- sorter: "string",
- minWidth: 120,
- responsive: 4,
- visible: r,
- formatter: /* @__PURE__ */ __name((e3, t2, n2) => {
- let a2 = e3.getData().url.includes("t=");
- return `
${a2 ? "按所选分类屏蔽" : "未筛选分类"}`;
- }, "formatter")
- }, {
- title: "番号数量",
- field: "count",
- sorter: "number",
- width: 170,
- responsive: 1
- }, {
- title: "创建时间",
- field: "createTime",
- sorter: "string",
- width: 170,
- responsive: 5
- }, {
- title: "最后发行时间",
- field: "lastPublishTime",
- sorter: "string",
- width: 170,
- responsive: 1
- }, {
- title: "状态",
- field: "isUnCheck",
- sorter: "string",
- width: 120,
- responsive: 1,
- formatter: /* @__PURE__ */ __name((e3, t2, n2) => {
- let a2 = "", i2 = "继续检测";
- return e3.getData().isUnCheck && (a2 = `停更${this.checkBlacklist_ruleTime / 24 / 365}年以上, 下轮任务不再进行检测`, i2 = "停更跳过"), `
${i2}`;
- }, "formatter")
- }, {
- title: "操作",
- sorter: "string",
- cssClass: "action-cell-dropdown",
- minWidth: 150,
- responsive: 0,
- headerSort: false,
- formatter: /* @__PURE__ */ __name((e3, t2, n2) => {
- const a2 = e3.getData();
- return n2((() => {
- var t3, n3;
- null == (t3 = e3.getElement().querySelector(".delete-btn")) || t3.addEventListener("click", ((e4) => {
- const t4 = a2.name, n4 = a2.starId;
- t4 ? n4 ? utils.q(e4, `是否移除对 ${t4} 的屏蔽?`, (async () => {
- await storageManager.removeBlacklistCarList(n4), await storageManager.deleteBlacklistItem(n4), show.info("操作成功"), await this.reloadTable();
- })) : show.error("获取starId失败") : show.error("获取名称失败");
- })), null == (n3 = e3.getElement().querySelector(".keyword-btn")) || n3.addEventListener("click", ((e4) => {
- const t4 = a2.carList.reduce(((e5, t5) => {
- const n5 = t5.carNum.split("-")[0] + "-";
- return e5[n5] = (e5[n5] || 0) + 1, e5;
- }), {}), n4 = Object.entries(t4).map((([e5, t5]) => ({
- prefix: e5,
- count: t5
- }))).sort(((e5, t5) => t5.count - e5.count));
- clog.debug(n4);
- }));
- })), '
';
- }, "formatter")
- }],
- initialSort: [{
- column: "createTime",
- dir: "desc"
- }],
- locale: "zh-cn",
- langs: {
- "zh-cn": {
- pagination: {
- first: "首页",
- first_title: "首页",
- last: "尾页",
- last_title: "尾页",
- prev: "上一页",
- prev_title: "上一页",
- next: "下一页",
- next_title: "下一页",
- all: "全部",
- page_size: "每页行数"
- }
- }
- }
- });
- }
- async filterAllVideo(e2, t2, page = 1, processed = 0) {
- let n2, a2;
- if (t2 ? (l && t2.find(".avatar-box").length > 0 && t2.find(".avatar-box").parent().remove(), n2 = t2.find(this.getSelector().requestDomItemSelector), a2 = t2.find(this.getSelector().nextPageSelector).attr("href")) : (n2 = $(this.getSelector().itemSelector), a2 = $(this.getSelector().nextPageSelector).attr("href")), a2 && 0 === n2.length) throw show.error("解析列表失败"), new Error("解析列表失败");
- for (const s2 of n2) {
- const t3 = $(s2), { carNum: n3, url: a3, publishTime: o2 } = this.getBean("ListPagePlugin").findCarNumAndHref(t3);
- if (a3 && n3) try {
- await stateService.patch(n3, { blocked: true }, { type: "actor-page-block", record: { carNum: n3, url: a3, names: e2, publishTime: o2 } }), clog.log("屏蔽演员番号", e2, n3);
- } catch (i2) {
- clog.error(`保存失败 [${n3}]:`, i2);
- }
- }
- processed += n2.length, $("#checkBlacklistMsg").text(`正在处理第 ${page} 页 · 已扫描 ${processed} 个番号`);
- if (a2) {
- clog.log("正在请求下一页内容:", a2), await new Promise(((e3) => setTimeout(e3, 500)));
- const t3 = await gmHttp.get(a2), n3 = new DOMParser(), i2 = $(n3.parseFromString(t3, "text/html"));
- await this.filterAllVideo(e2, i2, page + 1, processed);
- } else $("#checkBlacklistMsg").text(`处理完成 · ${page} 页 · 共扫描 ${processed} 个番号`);
- }
- async batchSaveAllVideos(e2, t2, pageDom = null, page = 1, processed = 0) {
- let n2, a2;
- pageDom ? (n2 = pageDom.find(this.getSelector().requestDomItemSelector), a2 = pageDom.find(this.getSelector().nextPageSelector).attr("href")) : (n2 = $(this.getSelector().itemSelector), a2 = $(this.getSelector().nextPageSelector).attr("href"));
- if (a2 && 0 === n2.length) throw show.error("解析列表失败"), new Error("解析列表失败");
- for (const i2 of n2) {
- const n3 = $(i2), { carNum: a3, url: o2, publishTime: r2 } = this.getBean("ListPagePlugin").findCarNumAndHref(n3);
- if (o2 && a3) try {
- const flag = legacyActionToFlag(t2);
- flag && await stateService.patch(a3, { [flag]: true }, { type: "actor-page-batch-state", record: { carNum: a3, url: o2, names: e2, publishTime: r2 } }), clog.log("批量操作", e2, a3, t2);
- } catch (s2) {
- clog.error(`保存失败 [${a3}]:`, s2);
- }
- }
- processed += n2.length, $("#checkBlacklistMsg").text(`正在处理第 ${page} 页 · 已扫描 ${processed} 个番号`);
- if (a2) {
- clog.log("正在请求下一页内容:", a2), await new Promise(((e3) => setTimeout(e3, 500)));
- const i2 = await gmHttp.get(a2), s2 = new DOMParser(), o2 = $(s2.parseFromString(i2, "text/html"));
- await this.batchSaveAllVideos(e2, t2, o2, page + 1, processed);
- } else $("#checkBlacklistMsg").text(`处理完成 · ${page} 页 · 共扫描 ${processed} 个番号`);
- }
- async filterActorVideo(e2, t2, n2, site = l ? I : T, page = 1, processed = 0) {
- let { nextPageLink: a2, recordCount } = await this.parseAndSaveFilterInfo(n2, e2, t2, site);
- processed += recordCount, $("#checkBlacklistMsg").text(`正在处理第 ${page} 页 · 已屏蔽 ${processed} 个番号`);
- if (this.nextPageLink = a2, a2) {
- let n3;
- this.lastPageLink = a2;
- clog.log("正在请求下一页内容:", a2);
- const i2 = await gmHttp.get(a2);
- n3 = utils.htmlTo$dom(i2);
- await this.filterActorVideo(e2, t2, n3, site, page + 1, processed);
- } else $("#checkBlacklistMsg").text(`处理完成 · ${page} 页 · 新增 ${processed} 个番号`);
- }
- async parseAndSaveFilterInfo(e2, t2, n2, site) {
- if (![T, I].includes(site)) throw new Error(`未知黑名单来源站点: ${site}`);
- const page = e2 || $(document), selector = this.getSelector(site);
- site === I && page.find(".avatar-box").length > 0 && page.find(".avatar-box").parent().remove();
- const pageState = parseDetailPage(page, {
- boxSelector: site === I ? `${selector.boxSelector}, #waterfall` : selector.boxSelector,
- requestDomItemSelector: e2 ? selector.requestDomItemSelector : selector.itemSelector
- }), nextPageLink = page.find(selector.nextPageSelector).attr("href");
- if ("valid" !== pageState.state) throw new Error(`黑名单作品页面无效: ${pageState.state}`);
- if (pageState.isEmpty && nextPageLink) throw new Error("黑名单作品空页面包含下一页");
- const records = [], publishTimes = [];
- for (const item of pageState.items) {
- const element = $(item), { carNum, url, publishTime } = this.getBean("ListPagePlugin").findCarNumAndHref(element);
- publishTime && publishTimes.push(publishTime), url && carNum && records.push({
- carNum,
- url,
- names: t2,
- actionType: d,
- starId: n2,
- publishTime
- });
- }
- await storageManager.batchSaveBlacklistCarList(records);
- return {
- nextPageLink,
- lastPublishTime: selectLatestPublishTime(publishTimes),
- recordCount: records.length
- };
- }
- };
- __name(_BlacklistPlugin, "BlacklistPlugin");
- var BlacklistPlugin = _BlacklistPlugin;
- var _ListPageButtonPlugin = class _ListPageButtonPlugin extends BasePlugin {
- getName() {
- return "ListPageButtonPlugin";
- }
- async handle() {
- if (!window.isListPage) return;
- await this.createMenuBtn(), this.bindEvent();
- const e2 = await storageManager.getSetting("autoPage"), t2 = this.supportsLiveSorting();
- $("#sort-toggle-btn").prop("disabled", e2 === _ && !t2).attr("title", e2 === _ && !t2 ? "瀑布流模式仅支持默认排序" : "选择列表排序方式"), (e2 !== _ || t2) && await this.sortItems();
- }
- async createMenuBtn() {
- if (r) {
- const e2 = o.includes("/actors/");
- let t2 = $(".main-tabs, .tabs"), n2 = "加入黑名单", a2 = "jhs-btn--filter", s2 = null;
- if (e2) {
- t2 = $(".toolbar, .section-addition").filter(":last");
- const e3 = await storageManager.getBlacklist(), i2 = this.getActressPageInfo();
- e3.find(((e4) => e4.starId === i2.starId)) && (n2 = "已加入黑名单", a2 = "jhs-btn--muted");
- } else o.includes("/tags") && utils.loopDetector((() => $("#jhs-check-tag").text().trim()), (async () => {
- const e3 = $("#addBlacklistBtn");
- e3.attr("data-tip", "将当前分类标签加入到黑名单, 后续有作品更新也会纳入屏蔽中");
- const t3 = $("#jhs-check-tag").text().trim();
- if (!t3) return;
- const n3 = "no-" + t3, a3 = await storageManager.getBlacklist();
- s2 = a3.find(((e4) => e4.starId === n3)), s2 && (e3.addClass("jhs-btn--muted").removeClass("jhs-btn--filter"), $("#addBlacklistBtn span").text("已加入黑名单"));
- }));
- const r2 = o.includes("advanced_search");
- r2 && (t2 = $("h2.section-title"));
- const l2 = localStorage.getItem("jhs_sortMethod"), d2 = "当前排序方式: " + ("rateCount" === l2 ? "评价人数" : "date" === l2 ? "时间" : "默认");
- t2.append(`
-
-
- ${e2 ? `
-
-
-
-
- ` : ""}
- ${o.includes("/tags") ? `
-
- ` : ""}
-
-
-
- ${c ? "" : this.sortMenuHtml(l2 || "default", d2)}
-
- `);
- }
- if (l) {
- const e2 = o.includes("/star/");
- let t2 = "加入黑名单", n2 = "jhs-btn--filter";
- if (e2) {
- const e3 = await storageManager.getBlacklist(), a3 = this.getActressPageInfo();
- e3.find(((e4) => e4.starId === a3.starId)) && (t2 = "已加入黑名单", n2 = "jhs-btn--muted");
- }
- const a2 = localStorage.getItem("jhs_sortMethod") || "default";
- $(".masonry").parent().prepend(`
-
-
- ${e2 ? `
-
-
-
-
- ` : ''}
- ${this.sortMenuHtml(a2)}
-
- `);
- }
- $("#waitCheckBtn > span").text("开始鉴定");
- const newVideoCount = $("#newVideoCount").detach(), newVideoLabel = $("#newVideoBtn > span");
- newVideoLabel.length && newVideoLabel.empty().append(document.createTextNode("新作品 ("), newVideoCount, document.createTextNode(")"));
- }
- /** 构建与原排序值兼容的 JHS 菜单。 */
- sortMenuHtml(method, title = "选择列表排序方式") {
- const labels = { default: "默认", rateCount: "评价人数", date: "时间" }, current = labels[method] || labels.default;
- return `
`;
- }
- bindEvent() {
- $("#waitCheckBtn").on("click", ((e3) => {
- void this.openWaitCheck(e3).catch(((error) => clog.error("待鉴定列表打开失败", error)));
- })), $("#newVideoBtn").on("click", ((e3) => {
- this.getBean("NewVideoPlugin").openDialog();
- })), $("#blacklistBtn").on("click", ((e3) => {
- this.getBean("BlacklistPlugin").openBlacklistDialog();
- })), this.bindSortMenu();
- const e2 = this.getBean("BlacklistPlugin");
- $("#addBlacklistBtn").on("click", (async (t2) => {
- await e2.addBlacklist(t2);
- })), $("#filterAllVideo").on("click", (async (t2) => {
- let n2 = {
- clientX: t2.clientX,
- clientY: t2.clientY + 80
- }, a2 = r ? $(".actor-section-name") : $(".avatar-box .photo-info .pb10");
- if (0 === a2.length) return void show.error("获取演员名称失败");
- let i2 = a2.text().trim().split(",")[0];
- utils.q(n2, "一键屏蔽视频列表?", (async () => {
- this.loadObj = loading();
- try {
- await e2.filterAllVideo(i2);
- } catch (t3) {
- clog.error(t3);
- } finally {
- this.loadObj.close();
- }
- }));
- })), $("#favoriteAllVideo").on("click", (async (t2) => {
- let n2 = { clientX: t2.clientX, clientY: t2.clientY + 80 }, a2 = r ? $(".actor-section-name") : $(".avatar-box .photo-info .pb10");
- if (0 === a2.length) return void show.error("获取演员名称失败");
- let i2 = a2.text().trim().split(",")[0];
- utils.q(n2, "一键收藏所有可见作品?", (async () => {
- this.loadObj = loading();
- try {
- await e2.batchSaveAllVideos(i2, h);
- } catch (t3) {
- clog.error(t3);
- } finally {
- this.loadObj.close();
- }
- }));
- })), $("#hasDownAllVideo").on("click", (async (t2) => {
- let n2 = { clientX: t2.clientX, clientY: t2.clientY + 80 }, a2 = r ? $(".actor-section-name") : $(".avatar-box .photo-info .pb10");
- if (0 === a2.length) return void show.error("获取演员名称失败");
- let i2 = a2.text().trim().split(",")[0];
- utils.q(n2, "一键已下载所有可见作品?", (async () => {
- this.loadObj = loading();
- try {
- await e2.batchSaveAllVideos(i2, g);
- } catch (t3) {
- clog.error(t3);
- } finally {
- this.loadObj.close();
- }
- }));
- }));
- }
- /** 绑定排序 popover 的选择与键盘交互。 */
- bindSortMenu() {
- const control = $(".jhs-sort-control"), toggle = control.find("#sort-toggle-btn"), menu = control.find(".jhs-sort-menu"), close = /* @__PURE__ */ __name((focus = false) => {
- menu.removeClass("is-open"), toggle.attr("aria-expanded", "false"), focus && toggle.trigger("focus");
- }, "close");
- toggle.on("click", ((event) => {
- event.preventDefault(), event.stopPropagation();
- const open = !menu.hasClass("is-open");
- menu.toggleClass("is-open", open), toggle.attr("aria-expanded", String(open)), open && menu.find('[aria-checked="true"]').trigger("focus");
- }));
- menu.on("click", ".jhs-sort-option", ((event) => {
- const item = $(event.currentTarget), method = item.data("sort-method");
- localStorage.setItem("jhs_sortMethod", method), menu.find(".jhs-sort-option").attr("aria-checked", "false"), item.attr("aria-checked", "true"), $("#jhs-sort-current").text(item.text()), close(true), void this.sortItems().catch(((error) => clog.error("列表排序失败", error)));
- })).on("keydown", ".jhs-sort-option", ((event) => {
- const items = menu.find(".jhs-sort-option"), index = items.index(event.currentTarget);
- if ("Escape" === event.key) return event.preventDefault(), close(true);
- if (!["ArrowDown", "ArrowUp", "Home", "End"].includes(event.key)) return;
- event.preventDefault();
- const next = "Home" === event.key ? 0 : "End" === event.key ? items.length - 1 : "ArrowDown" === event.key ? (index + 1) % items.length : (index - 1 + items.length) % items.length;
- items.eq(next).trigger("focus");
- }));
- $(document).off("click.jhsSortMenu").on("click.jhsSortMenu", ((event) => {
- $(event.target).closest(control).length || close();
- }));
- }
- async sortItems() {
- const e2 = this.supportsLiveSorting();
- if (!e2 && (o.includes("handle") || o.includes("advanced_search"))) return;
- const s2 = await storageManager.getSetting("autoPage");
- if (c || s2 === _ && !e2) return;
- const t2 = localStorage.getItem("jhs_sortMethod");
- if (!t2) return;
- const i2 = this.getSelector(), d2 = $(i2.boxSelector), h2 = $(i2.itemSelector);
- h2.each((function(e3) {
- $(this).attr("data-original-index") || $(this).attr("data-original-index", e3);
- }));
- const items = h2.get().map(((element, index) => {
- const card = $(element), originalIndex = Number(card.attr("data-original-index")) || 0;
- if ("default" === t2) return { element, key: originalIndex, originalIndex, index };
- if ("rateCount" === t2) {
- const explicit = Number(card.attr("data-jhs-rate-count")), match = card.find(".score").text().replaceAll(",", "").match(/(?:由\s*)?(\d+)\s*人(?:评价)?/);
- return { element, key: Number.isFinite(explicit) ? explicit : match ? Number(match[1]) : 0, originalIndex, index };
- }
- const value = card.attr("data-jhs-publish-time") || card.find(".meta").text().trim() || card.find("date").filter((function() {
- return /^\d{4}-\d{1,2}-\d{1,2}$/.test($(this).text().trim());
- })).first().text().trim(), timestamp = Date.parse(value);
- return { element, key: Number.isFinite(timestamp) ? timestamp : 0, originalIndex, index };
- }));
- items.sort(((e3, n2) => "default" === t2 ? e3.key - n2.key : n2.key - e3.key || e3.originalIndex - n2.originalIndex || e3.index - n2.index));
- const sortedElements = items.map(((item) => item.element));
- "default" === t2 ? $(sortedElements).appendTo(d2) : d2.empty().append(sortedElements);
- }
- isHitShowPage() {
- return isHitShowPage(window.location);
- }
- isFc2ListPage() {
- return r && "/advanced_search" === window.location.pathname && "3" === new URLSearchParams(window.location.search).get("type");
- }
- supportsLiveSorting() {
- return this.isHitShowPage() || this.isFc2ListPage();
- }
- async openWaitCheck() {
- let e2 = this.getSelector();
- const t2 = await storageManager.getSetting("waitCheckCount", 5);
- let a2 = 0;
- const listPage = this.getBean("ListPagePlugin");
- for (const element of $(e2.itemSelector).toArray()) {
- if (a2 >= t2) break;
- const item = $(element), flags = normalizeStateFlags(JSON.parse(item.attr("data-jhs-flags") || "{}")), visibilityReasons = JSON.parse(item.attr("data-jhs-visibility") || "{}");
- if (hasAnyState(flags) || isHardHidden(flags, visibilityReasons)) continue;
- await listPage.openMovieDetail(item, { autoplay: true, newTab: false }), a2++;
- }
- 0 === a2 && show.info("没有需鉴定的视频");
- }
- };
- __name(_ListPageButtonPlugin, "ListPageButtonPlugin");
- var ListPageButtonPlugin = _ListPageButtonPlugin;
- var _e = /* @__PURE__ */ __name(async (e2, t2 = "ja", n2 = "zh-CN") => {
- if (!e2) throw new Error("翻译文本不能为空");
- const a2 = "https://translate-pa.googleapis.com/v1/translate?" + new URLSearchParams({
- "params.client": "gtx",
- dataTypes: "TRANSLATION",
- key: "AIzaSyDLEeFI5OtFBwYBIoK_jj5m32rZK5CkCXA",
- "query.sourceLanguage": t2,
- "query.targetLanguage": n2,
- "query.text": e2
- }), i2 = await fetch(a2);
- if (!i2.ok) throw new Error(`${i2.status} ${i2.statusText}`);
- return (await i2.json()).translation;
- }, "_e");
- var Te = {
- IS_FILTERED: {
- text: u,
- color: "var(--jhs-status-filter)",
- on: "var(--jhs-status-filter-on)",
- reasonType: "单番号屏蔽",
- isCounted: true,
- countKey: "currentPageFilterCount"
- },
- IS_FAVORITE: {
- text: b,
- color: "var(--jhs-status-fav)",
- on: "var(--jhs-status-fav-on)",
- reasonType: "",
- isCounted: true,
- countKey: "currentPageFavoriteCount"
- },
- IS_HAS_DOWN: {
- text: y,
- color: "var(--jhs-status-down)",
- on: "var(--jhs-status-down-on)",
- reasonType: "",
- isCounted: true,
- countKey: "currentPageHasDownCount"
- },
- IS_HAS_WATCH: {
- text: k,
- color: "var(--jhs-status-watch)",
- on: "var(--jhs-status-watch-on)",
- reasonType: "",
- isCounted: true,
- countKey: "currentPageHasWatchCount"
- },
- IS_KEYWORD_FILTER: {
- text: "关键词屏蔽",
- color: "var(--jhs-status-filter)",
- on: "var(--jhs-status-filter-on)",
- reasonType: "",
- isCounted: true,
- countKey: "currentPageKeywordFilterCount"
- },
- IS_ACTOR_FILTER: {
- text: "男演员屏蔽",
- color: "var(--jhs-status-filter)",
- on: "var(--jhs-status-filter-on)",
- reasonType: "",
- isCounted: true,
- countKey: "currentPageActorFilterCount"
- },
- IS_ACTRESS_FILTER: {
- text: "女演员屏蔽",
- color: "var(--jhs-status-filter)",
- on: "var(--jhs-status-filter-on)",
- reasonType: "",
- isCounted: true,
- countKey: "currentPageActorFilterCount"
- },
- IS_WAIT_CHECK: {
- text: "",
- color: "",
- on: "",
- reasonType: "",
- isCounted: true,
- countKey: "currentPageWaitCheckCount"
- }
- };
- var QUICK_FILTER_LABELS = Object.freeze({
- all: "全部",
- waitCheck: "待鉴定",
- favorite: "收藏",
- hasDown: "下载",
- hasWatch: "已看",
- blockedItems: "屏蔽项",
- favoriteUndownloaded: "收藏未下载",
- favoriteUnwatched: "收藏未观看",
- downloadedUnwatched: "下载未观看",
- recent7d: "最近 7 天"
- });
- var PRIMARY_QUICK_FILTERS = Object.freeze(["all", "waitCheck", "favorite", "hasDown", "hasWatch"]);
- var SECONDARY_QUICK_FILTERS = Object.freeze(["blockedItems", "favoriteUndownloaded", "favoriteUnwatched", "downloadedUnwatched", "recent7d"]);
- var VALID_QUICK_FILTERS = /* @__PURE__ */ new Set([...PRIMARY_QUICK_FILTERS, ...SECONDARY_QUICK_FILTERS]);
- function normalizeQuickFilterKey(value) {
- if ("filter" === value) return "blockedItems";
- return VALID_QUICK_FILTERS.has(value) ? value : "waitCheck";
- }
- __name(normalizeQuickFilterKey, "normalizeQuickFilterKey");
- function isHardHidden(flags, visibilityReasons = {}) {
- return Boolean(flags.blocked || visibilityReasons.keyword || visibilityReasons.actorBlacklist || visibilityReasons.actressBlacklist);
- }
- __name(isHardHidden, "isHardHidden");
- function matchesQuickFilter(filter, flags, { visibilityReasons = {}, recent = false } = {}) {
- const normalizedFilter = normalizeQuickFilterKey(filter), hardHidden = isHardHidden(flags, visibilityReasons);
- if ("blockedItems" === normalizedFilter) return hardHidden;
- if (hardHidden) return false;
- if ("all" === normalizedFilter) return true;
- if ("waitCheck" === normalizedFilter) return !hasAnyState(flags);
- if ("favorite" === normalizedFilter) return !!flags.favorite;
- if ("hasDown" === normalizedFilter) return !!flags.downloaded;
- if ("hasWatch" === normalizedFilter) return !!flags.watched;
- if ("favoriteUndownloaded" === normalizedFilter) return !!flags.favorite && !flags.downloaded;
- if ("favoriteUnwatched" === normalizedFilter) return !!flags.favorite && !flags.watched;
- if ("downloadedUnwatched" === normalizedFilter) return !!flags.downloaded && !flags.watched;
- return "recent7d" === normalizedFilter && recent;
- }
- __name(matchesQuickFilter, "matchesQuickFilter");
- function shouldShowItem({ filter, flags, visibilityReasons, recent }) {
- return matchesQuickFilter(filter, flags, { visibilityReasons, recent });
- }
- __name(shouldShowItem, "shouldShowItem");
- var _ListPagePlugin = class _ListPagePlugin extends BasePlugin {
- async initCss() {
- return ``;
- }
- constructor() {
- super(...arguments), i(this, "currentPageFilterCount", 0), i(this, "currentPageFavoriteCount", 0), i(this, "currentPageHasDownCount", 0), i(this, "currentPageHasWatchCount", 0), i(this, "currentPageKeywordFilterCount", 0), i(this, "currentPageActorFilterCount", 0), i(this, "currentPageWaitCheckCount", 0), i(this, "currentPageTotalCount", 0), i(this, "cache", null), i(this, "translationPending", /* @__PURE__ */ new Map()), i(this, "filterContext", null), i(this, "pendingItems", /* @__PURE__ */ new Set()), i(this, "processTimer", null), i(this, "hdImageObserver", null), i(this, "hdEagerRemaining", 12), i(this, "writeQueue", Promise.resolve()), i(this, "_debouncedTranslateWrite", null), i(this, "itemIndex", /* @__PURE__ */ new Map()), i(this, "recountFrame", null);
- }
- getName() {
- return "ListPagePlugin";
- }
- async handle() {
- if (!window.isListPage || isHitShowPage()) return;
- const refreshAll = /* @__PURE__ */ __name(async () => {
- this.filterContext = null, storageManager._invalidateCache(storageManager.car_list_key), await this.doFilter(), this.applyVisibility();
- const e2 = this.getBean("HistoryPlugin");
- e2.tableObj && e2.tableObj.setData();
- }, "refreshAll");
- jhsEventBus.on("legacy-refresh", refreshAll), jhsEventBus.on("blacklist-rules-changed", refreshAll), jhsEventBus.on("filter-rules-changed", refreshAll), jhsEventBus.on("settings-changed", refreshAll), jhsEventBus.on("car-state-changed", (async (payload) => {
- this.filterContext = null, storageManager._invalidateCache(storageManager.car_list_key);
- const items = this.getIndexedItems(payload.carNums || []);
- items.length && (await this.doFilterItems(items), this.applyVisibility(items));
- const history = this.getBean("HistoryPlugin");
- history.tableObj && history.tableObj.setData();
- })), this.cleanRepeatId(), this.replaceHdImg(), this.addJumpPageControl(), this.fixBusTitleBox(), await this.doFilter(), await this.createQuickFilter(), this.applyVisibility(), await this.bindClick(), this.rememberTagExpand(), $(this.getSelector().itemSelector).attr("data-jhs-processed", "true"), this.rebuildItemIndex(), await jhsEventBus.emit("list-items-added", { items: $(this.getSelector().itemSelector).toArray() }, { broadcast: false }), this.checkDom();
- }
- async createQuickFilter() {
- if ($("#jhs-quick-filter").length) return;
- const e2 = this.getSelector(), primaryHtml = PRIMARY_QUICK_FILTERS.map(((filter) => `
`)).join(""), secondaryHtml = SECONDARY_QUICK_FILTERS.map(((filter, index) => `${1 === index ? '' : ""}
`)).join(""), t2 = `
-
${primaryHtml}
-
-
-
${secondaryHtml}
+ `).join(""),"renderCards"),l="jhs-backup-card-list";i.open({type:1,title:t+"备份文件",content:`
${n(o)}
`,area:utils.getResponsiveArea(["800px","70%"]),anim:-1,success:d(c=>{let h=$(c).find(`#${l}`);h.on("click",".jhs-backup-btn",async u=>{let p=$(u.currentTarget),g=p.data("action"),m=p.data("idx"),f=o[m];if(f){if(g==="delete")i.confirm(`是否删除 ${f.name} ?`,{icon:3,title:"提示",btn:["确定","取消"]},async v=>{i.close(v);let b=loading();try{await e.deleteFile(f.fileId),o=await e.getBackupList(s),h.html(n(o)),i.alert("删除成功")}catch(w){clog.error(w),show.error(`发生错误: ${Ue(w)}`)}finally{b.close()}});else if(g==="download"){let v=loading();try{let b=await Xt(await e.getFileContent(f.fileId));utils.download(b,f.name)}catch(b){clog.error(b),show.error("下载失败: "+b)}finally{v.close()}}else if(g==="import"){let v=loading();try{let b=await e.getFileContent(f.fileId);b=await Xt(b);let w=JSON.parse(b),S=await storageManager.exportData(),j=await storageManager.diffData(S,w);v.close(),a(j,null,w)}catch(b){v.close(),clog.error(b),show.error("预览失败: "+Ue(b))}}}})},"success")})}d(wh,"openFileListDialogMobile");function Ol(o,e,t,s,a,i){if(utils.isMobileMode()){wh(o,e,t,s,a,i);return}i.open({type:1,title:t+"备份文件",content:`
+
-
`;
- r ? $(e2.boxSelector).before(t2) : l && $(".masonry").before(t2);
- const root = $("#jhs-quick-filter"), toggle = root.find(".jhs-quick-filter__toggle"), menu = root.find(".jhs-quick-filter__menu"), closeMenu = /* @__PURE__ */ __name((restoreFocus = false) => {
- menu.removeClass("is-open"), toggle.attr("aria-expanded", "false"), restoreFocus && toggle.trigger("focus");
- }, "closeMenu");
- root.on("click", ".jhs-segmented__item", ((event) => this.setQuickFilter($(event.currentTarget).data("jhs-filter")))).on("keydown", ".jhs-segmented__item", ((event) => {
- if (!["ArrowLeft", "ArrowRight", "Home", "End"].includes(event.key)) return;
- event.preventDefault();
- const tabs = root.find(".jhs-segmented__item"), index = tabs.index(event.currentTarget), next = "Home" === event.key ? 0 : "End" === event.key ? tabs.length - 1 : "ArrowRight" === event.key ? (index + 1) % tabs.length : (index - 1 + tabs.length) % tabs.length;
- tabs.eq(next).trigger("click").trigger("focus");
- })).on("click", ".jhs-filter-option", ((event) => {
- this.setQuickFilter($(event.currentTarget).data("jhs-filter")), closeMenu(true);
- })).on("keydown", ".jhs-filter-option", ((event) => {
- const items = menu.find(".jhs-filter-option"), index = items.index(event.currentTarget);
- if ("Escape" === event.key) return event.preventDefault(), closeMenu(true);
- if (!["ArrowDown", "ArrowUp", "Home", "End"].includes(event.key)) return;
- event.preventDefault();
- const next = "Home" === event.key ? 0 : "End" === event.key ? items.length - 1 : "ArrowDown" === event.key ? (index + 1) % items.length : (index - 1 + items.length) % items.length;
- items.eq(next).trigger("focus");
- }));
- toggle.on("click", ((event) => {
- event.preventDefault(), event.stopPropagation();
- const open = !menu.hasClass("is-open");
- menu.toggleClass("is-open", open), toggle.attr("aria-expanded", String(open)), open && (menu.find('[aria-checked="true"]').first().length ? menu.find('[aria-checked="true"]').first() : menu.find(".jhs-filter-option").first()).trigger("focus");
- })), $(document).off("click.jhsQuickFilter").on("click.jhsQuickFilter", ((event) => {
- $(event.target).closest(root).length || closeMenu();
- }));
- this.setQuickFilter(await storageManager.getSetting("defaultQuickFilterTab", "waitCheck"));
- }
- applyVisibility(items = null) {
- const e2 = this.activeQuickFilter || "waitCheck", t2 = this.getSelector().itemSelector;
- (items ? $(items) : $(t2)).each((function() {
- const t3 = $(this), flags = normalizeStateFlags(JSON.parse(t3.attr("data-jhs-flags") || "{}")), visibilityReasons = JSON.parse(t3.attr("data-jhs-visibility") || "{}"), recent = "yes" === t3.attr("data-jhs-recent");
- shouldShowItem({ filter: e2, flags, visibilityReasons, recent }) ? t3.show() : t3.hide();
- }));
- }
- setQuickFilter(filter, { syncUi = true } = {}) {
- this.activeQuickFilter = normalizeQuickFilterKey(filter), this.applyVisibility(), syncUi && this.syncQuickFilterUi();
- }
- syncQuickFilterUi() {
- const filter = normalizeQuickFilterKey(this.activeQuickFilter), isPrimary = PRIMARY_QUICK_FILTERS.includes(filter), root = $("#jhs-quick-filter"), tabs = root.find(".jhs-segmented__item"), options = root.find(".jhs-filter-option");
- tabs.removeClass("active").attr({ "aria-selected": "false", tabindex: "-1" });
- isPrimary ? tabs.filter(`[data-jhs-filter="${filter}"]`).addClass("active").attr({ "aria-selected": "true", tabindex: "0" }) : tabs.first().attr("tabindex", "0");
- options.attr("aria-checked", "false").filter(`[data-jhs-filter="${filter}"]`).attr("aria-checked", "true");
- root.find(".jhs-quick-filter__label").text(isPrimary ? "更多筛选" : `筛选:${QUICK_FILTER_LABELS[filter]}`);
- $(".jhs-mobile-filter-label").text(`筛选:${QUICK_FILTER_LABELS[filter]}`), $(".jhs-mobile-filter-option").attr("aria-checked", "false").filter(`[data-jhs-filter="${filter}"]`).attr("aria-checked", "true");
- }
- rememberTagExpand() {
- if (!window.location.href.includes("actors")) return;
- const e2 = $(".tag-expand");
- if (0 === e2.length) return;
- const t2 = "jhs_tag_expand", n2 = "true" === localStorage.getItem(t2), a2 = $(".actor-tags .content");
- n2 && a2.hasClass("collapse") && e2[0].click(), e2.on("click", (function() {
- const e3 = !$(".actor-tags .content").hasClass("collapse");
- clog.debug("触发"), localStorage.setItem(t2, e3.toString());
- }));
- }
- checkDom() {
- if (!window.isListPage || isHitShowPage()) return;
- const e2 = this.getSelector(), t2 = document.querySelector(e2.boxSelector);
- if (!t2) return void clog.error("没有找到容器节点!");
- const a2 = new MutationObserver(((records) => {
- for (const record of records) {
- this.removeIndexedItems(record.removedNodes);
- for (const node of record.addedNodes) {
- if (node.nodeType !== Node.ELEMENT_NODE) continue;
- node.matches?.(e2.itemSelector) && "true" !== node.dataset.jhsProcessed && this.pendingItems.add(node), node.querySelectorAll?.(e2.itemSelector).forEach(((item) => {
- "true" !== item.dataset.jhsProcessed && this.pendingItems.add(item);
- }));
- }
- }
- this.pendingItems.size && (this.processTimer && clearTimeout(this.processTimer), this.processTimer = setTimeout((() => {
- const items = [...this.pendingItems].filter(((item) => item.isConnected && "true" !== item.dataset.jhsProcessed));
- this.pendingItems.clear(), this.processTimer = null, items.length && void this.processAddedItems(items).catch(((error) => clog.error("列表增量处理失败", error)));
- }), 100));
- }));
- a2.observe(t2, {
- childList: true,
- subtree: false
- });
- }
- async processAddedItems(items) {
- const selector = this.getSelector(), covers = items.flatMap(((item) => [...item.querySelectorAll(selector.coverImgSelector)]));
- this.replaceHdImg(covers), this.addJumpPageControl(), this.fixBusTitleBox(items), await this.doFilterItems(items), this.applyVisibility(items), await this.getBean("ListPageButtonPlugin").sortItems(), await this.getBean("CoverButtonPlugin").addSvgBtn(items), items.forEach(((item) => item.dataset.jhsProcessed = "true")), this.indexItems(items), await jhsEventBus.emit("list-items-added", { items }, { broadcast: false }), this.getBean("AutoPagePlugin").checkLoad();
- }
- rebuildItemIndex() {
- this.itemIndex.clear(), this.indexItems($(this.getSelector().itemSelector).toArray());
- }
- indexItems(items) {
- items.forEach(((item) => {
- try {
- const key = normalizeCarNum(this.findCarNumAndHref($(item)).carNum);
- if (!key) return;
- const indexed = this.itemIndex.get(key) || /* @__PURE__ */ new Set();
- indexed.add(item), this.itemIndex.set(key, indexed);
- } catch (error) {
- clog.debug("列表项索引跳过无效卡片", error);
- }
- }));
- }
- removeIndexedItems(nodes) {
- const removed = /* @__PURE__ */ new Set();
- Array.from(nodes || []).forEach(((node) => {
- node.nodeType === Node.ELEMENT_NODE && (removed.add(node), node.querySelectorAll?.(this.getSelector().itemSelector).forEach(((item) => removed.add(item))));
- }));
- if (!removed.size) return;
- this.itemIndex.forEach(((items, key) => {
- items.forEach(((item) => {
- removed.has(item) && items.delete(item);
- })), items.size || this.itemIndex.delete(key);
- }));
- }
- getIndexedItems(carNums) {
- const result = /* @__PURE__ */ new Set();
- carNums.map(normalizeCarNum).forEach(((key) => {
- const items = this.itemIndex.get(key);
- items?.forEach(((item) => item.isConnected ? result.add(item) : items.delete(item))), items && !items.size && this.itemIndex.delete(key);
- }));
- return [...result];
- }
- fixBusTitleBox(items = null) {
- if (!l) return;
- (items ? $(items).toArray() : $(this.getSelector().itemSelector).toArray()).forEach(((e2) => {
- var t2;
- let n2 = $(e2);
- if (n2.find(".avatar-box").length > 0) return;
- const a2 = (null == (t2 = n2.find("img").attr("title")) ? void 0 : t2.trim()) || "";
- n2.find(".photo-info span:first").contents().first().wrap(`
${a2}`), n2.find("br").remove();
- }));
- }
- cleanRepeatId() {
- if (!l) return;
- $("#waterfall_h").removeAttr("id").attr("id", "no-page");
- const e2 = $('[id="waterfall"]');
- 0 !== e2.length && e2.each((function() {
- const e3 = $(this);
- if (!e3.hasClass("masonry")) {
- e3.children().insertAfter(e3), e3.remove();
- }
- }));
- }
- async doFilter() {
- return this.doFilterItems();
- }
- async doFilterItems(items = null) {
- if (!window.isListPage) return;
- let e2 = items ? $(items).toArray() : $(this.getSelector().itemSelector).toArray();
- e2.length && (await this.filterMovieList(e2), l && setTimeout((() => {
- this.getBean("BusImgPlugin").logImageHeightsByRow().catch(((e3) => clog.error("JavBus图片高度修正失败", e3)));
- })));
- }
- async yieldListFrame() {
- await new Promise(((e2) => {
- window.requestAnimationFrame ? window.requestAnimationFrame((() => setTimeout(e2))) : setTimeout(e2);
- }));
- }
- findMatchedTitleKeyword(e2, t2, n2) {
- for (const a2 of e2) if (t2.includes(a2) || n2.startsWith(a2)) return a2;
- return null;
- }
- async getFilterContext() {
- if (this.filterContext) return this.filterContext;
- const [titleKeywords, blacklistMap, blacklistCars, settings, carMap, activity] = await Promise.all([storageManager.getTitleFilterKeyword(), storageManager.getBlacklistMap(), storageManager.getBlacklistCarList(), storageManager.getSetting(), storageManager.getCarMap(), stateService.getActivityLog()]), actorCarNumToNameMap = /* @__PURE__ */ new Map(), actressCarNumToNameMap = /* @__PURE__ */ new Map(), recentCarNums = /* @__PURE__ */ new Set();
- const cutoff = Date.now() - 7 * 864e5;
- activity.entries.filter(((entry) => "committed" === entry.commitState && Date.parse(entry.createdAt) >= cutoff)).forEach(((entry) => entry.changes.filter(((change) => "reverted" !== change.undoState && change.fields?.some(((field) => field.startsWith("stateFlags."))))).forEach(((change) => recentCarNums.add(change.carNum)))));
- for (const item of blacklistCars) {
- const role = blacklistMap.get(item.starId)?.role;
- if (!role) {
- clog.error("黑名单数据源丢失演员信息", item);
- continue;
- }
- const target = role === B ? actorCarNumToNameMap : actressCarNumToNameMap, carNum = normalizeCarNum(item.carNum);
- target.has(carNum) || target.set(carNum, item.names);
- }
- return this.filterContext = { titleKeywords, settings, carMap, recentCarNums, actorCarNumToNameMap, actressCarNumToNameMap };
- }
- collectCurrentPageSummary() {
- const summary = { total: 0, pending: 0, blockedItems: 0, favorite: 0, downloaded: 0, watched: 0, debug: { manualBlocked: 0, keywordBlocked: 0, actorBlocked: 0, actressBlocked: 0 } };
- $(this.getSelector().itemSelector).each(((e2, item) => {
- const card = $(item);
- if (l && card.find(".avatar-box").length > 0) return;
- const flags = normalizeStateFlags(JSON.parse(card.attr("data-jhs-flags") || "{}")), reasons = JSON.parse(card.attr("data-jhs-visibility") || "{}"), hardHidden = isHardHidden(flags, reasons);
- summary.total++, flags.favorite && summary.favorite++, flags.downloaded && summary.downloaded++, flags.watched && summary.watched++, hardHidden && summary.blockedItems++, !hasAnyState(flags) && !hardHidden && summary.pending++, flags.blocked && summary.debug.manualBlocked++, reasons.keyword && summary.debug.keywordBlocked++, reasons.actorBlacklist && summary.debug.actorBlocked++, reasons.actressBlacklist && summary.debug.actressBlocked++;
- }));
- return summary;
- }
- getCurrentPageSummary() {
- return this.collectCurrentPageSummary();
- }
- recountStatuses() {
- const summary = this.collectCurrentPageSummary();
- this.currentPageFilterCount = summary.debug.manualBlocked, this.currentPageFavoriteCount = summary.favorite, this.currentPageHasDownCount = summary.downloaded, this.currentPageHasWatchCount = summary.watched, this.currentPageKeywordFilterCount = summary.debug.keywordBlocked, this.currentPageActorFilterCount = summary.debug.actorBlocked + summary.debug.actressBlocked, this.currentPageWaitCheckCount = summary.pending, this.currentPageTotalCount = summary.total;
- return summary;
- }
- scheduleRecount() {
- if (this.recountFrame) return;
- const schedule = window.requestAnimationFrame || ((callback) => setTimeout(callback, 0));
- this.recountFrame = schedule((() => {
- this.recountFrame = null, this.recountStatuses();
- }));
- }
- async translateListItems(e2) {
- if (await storageManager.getSetting("translateTitle", _) !== _) return;
- await mapLimit(e2, 3, (async (item, index) => {
- try {
- index > 0 && index % 8 == 0 && await this.yieldListFrame(), await this.translate(item);
- } catch (error) {
- clog.error("列表标题翻译失败", error);
- }
- }));
- }
- async filterMovieList(e2) {
- utils.time("累计耗费时间"), utils.time("读取数据耗时");
- const { titleKeywords: n2, settings: s2, carMap: m2, recentCarNums: recent, actorCarNumToNameMap: f, actressCarNumToNameMap: v2 } = await this.getFilterContext(), o2 = utils.time("读取数据耗时");
- utils.time("组装数据耗时");
- const b2 = utils.time("组装数据耗时"), P2 = (null == s2 ? void 0 : s2.tagPosition) || "rightTop";
- const O2 = n2.filter(((e3) => e3));
- this.currentPageFilterCount = 0, this.currentPageFavoriteCount = 0, this.currentPageHasDownCount = 0, this.currentPageHasWatchCount = 0, this.currentPageKeywordFilterCount = 0, this.currentPageActorFilterCount = 0, this.currentPageWaitCheckCount = 0, this.currentPageTotalCount = 0, utils.time("处理页面耗时");
- const R2 = [];
- for (let n3 = 0; n3 < e2.length; n3++) {
- n3 > 0 && n3 % 12 == 0 && await this.yieldListFrame();
- let t2 = $(e2[n3]);
- if (l && t2.find(".avatar-box").length > 0) continue;
- const { carNum: a2, title: i2 } = this.findCarNumAndHref(t2), record = m2.get(a2), flags = normalizeStateFlags(record?.stateFlags), actorFiltered = f.has(a2), actressFiltered = v2.has(a2), keyword = this.findMatchedTitleKeyword(O2, i2, a2), visibilityReasons = { keyword: !!keyword, actorBlacklist: actorFiltered, actressBlacklist: actressFiltered };
- const hardHidden = isHardHidden(flags, visibilityReasons);
- t2.attr("data-jhs-flags", JSON.stringify(flags)).attr("data-jhs-visibility", JSON.stringify(visibilityReasons)).attr("data-jhs-recent", recent.has(a2) ? _ : C).attr("data-jhs-tag-position", P2);
- const signature = JSON.stringify({ flags, visibilityReasons, P: P2 });
- if (t2.attr("data-jhs-state-signature") !== signature) {
- t2.attr("data-jhs-state-signature", signature), t2.find(".jhs-status-tags").remove();
- const badgeDefs = [
- [flags.blocked, Te.IS_FILTERED, "单番号屏蔽"],
- [flags.favorite, Te.IS_FAVORITE, ""],
- [flags.downloaded, Te.IS_HAS_DOWN, ""],
- [flags.watched, Te.IS_HAS_WATCH, ""],
- [visibilityReasons.keyword, Te.IS_KEYWORD_FILTER, keyword || "未知"],
- [visibilityReasons.actorBlacklist, Te.IS_ACTOR_FILTER, f.get(a2) || ""],
- [visibilityReasons.actressBlacklist, Te.IS_ACTRESS_FILTER, v2.get(a2) || ""]
- ].filter(((item) => item[0]));
- if (badgeDefs.length) {
- const box = $(`
`);
- badgeDefs.forEach((([, definition, tip]) => {
- const badge = $(`
${escapeHtml(definition.text)}`);
- badge.css({ color: definition.on, backgroundColor: definition.color }), box.append(badge);
- }));
- if (r) t2.find(".tags").append(box);
- else if (l) {
- const host = t2.find(".item-tag");
- host.length ? host.append(box) : t2.find(".photo-info > span > div").append(box);
- }
- }
- }
- hardHidden || R2.push(t2);
- }
- this.scheduleRecount(), void this.translateListItems(R2).catch(((e3) => clog.error("列表页翻译任务失败", e3)));
- const D2 = utils.time("处理页面耗时"), A2 = utils.time("累计耗费时间");
- clog.log(`
-
-
- | ${o2} |
- ${b2} |
-
-
-
- | ${D2} |
- ${A2} |
-
-
- | 项目 |
- 数量 |
- 项目 |
- 数量 |
-
-
-
- | 屏蔽单番号 |
- ${this.currentPageFilterCount} |
- 收藏 |
- ${this.currentPageFavoriteCount} |
-
-
-
- | 屏蔽演员 |
- ${this.currentPageActorFilterCount} |
- 已下载 |
- ${this.currentPageHasDownCount} |
-
-
-
- | 屏蔽关键词 |
- ${this.currentPageKeywordFilterCount} |
- 已观看 |
- ${this.currentPageHasWatchCount} |
-
-
-
- | 待鉴定 |
- ${this.currentPageWaitCheckCount} |
- |
- |
-
-
-
- | 总数 |
- ${this.currentPageTotalCount} |
-
-
- `);
- }
- async bindClick() {
- let e2 = this.getSelector();
- this.bindMovieDetailNavigation(e2.boxSelector), $(e2.boxSelector).on("click", ".item video", (async (e3) => {
- const t2 = e3.currentTarget;
- t2.paused ? await safePlay(t2, {
- context: "列表视频",
- notify: true
- }) : t2.pause(), e3.preventDefault(), e3.stopPropagation();
- })), $(e2.boxSelector).on("contextmenu", ".item img, .item video", (async (e3) => {
- try {
- e3.preventDefault();
- const t2 = $(e3.target).closest(".item"), { carNum: n2, url: a2, publishTime: i2, fc2Source } = this.findCarNumAndHref(t2);
- let s2 = r ? $(".actor-section-name") : $(".avatar-box .photo-info .pb10"), o2 = "";
- s2.length && (o2 = s2.text().trim().split(",")[0].replace("(無碼)", "")), utils.q(e3, `是否屏蔽番号 ${n2}?`, (async () => {
- try {
- o2 || (o2 = await this.parseActressName(a2)), await stateService.patch(n2, { blocked: true }, { record: { carNum: n2, url: a2, names: o2, publishTime: i2, fc2Source } }), show.ok("操作成功");
- } catch (s3) {
- clog.error("屏蔽操作失败:", s3), show.error("操作失败");
- }
- }));
- } catch (t2) {
- clog.error("右键菜单处理失败:", t2);
- }
- }));
- }
- /** 从任意列表卡片进入统一详情导航。 */
- async openMovieDetail(item, { event = null, autoplay = false, newTab = false } = {}) {
- const card = item?.jquery ? item : $(item), { carNum, aHref, fc2Source } = this.findCarNumAndHref(card);
- if (!carNum || !aHref) return;
- const shouldOpenTab = newTab || !!event && (event.ctrlKey || event.metaKey || 1 === event.button);
- if (carNum.includes("FC2-")) {
- const movieId = this.parseMovieId(aHref);
- return shouldOpenTab ? this.getBean("Fc2Plugin").openFc2Page(movieId, carNum, aHref, { event, newTab: true }, { source: fc2Source }) : this.getBean("Fc2Plugin").openFc2Dialog(movieId, carNum, aHref, { source: fc2Source });
- }
- const destination = new URL(aHref, window.location.origin);
- autoplay && destination.searchParams.set("autoPlay", "1"), utils.openPage(destination.href, carNum, true, { event, newTab: shouldOpenTab }), this.$currentImage = null;
- }
- /** 为宿主与合成列表统一绑定左键、修饰键和中键导航。 */
- bindMovieDetailNavigation(container) {
- const root = $(container), selector = ".item img, .item .video-title";
- root.off("click.jhsMovieDetail auxclick.jhsMovieDetail", selector).on("click.jhsMovieDetail auxclick.jhsMovieDetail", selector, ((event) => {
- if ("auxclick" === event.type && 1 !== event.button || "click" === event.type && event.button && 0 !== event.button) return;
- if (event.shiftKey || event.altKey || $(event.target).closest("div.meta-buttons,[class^='jhs-match-']").length) return;
- event.preventDefault(), event.stopPropagation();
- void this.openMovieDetail($(event.currentTarget).closest(".item"), { event }).catch(((error) => clog.error("打开影片详情失败", error)));
- }));
- }
- async parseActressName(e2) {
- let t2 = null;
- if (await storageManager.getSetting("enableSaveActressCarInfo", C) === _) {
- clog.debug("鉴定补录演员信息-已启用, 开始解析详情页"), clog.debug("开始解析演员详情页", e2);
- const n2 = await gmHttp.get(e2), a2 = utils.htmlTo$dom(n2);
- r ? t2 = a2.find(".female").prev().map(((e3, t3) => $(t3).text())).get().join(" ") : l && (t2 = a2.find('span[onmouseover*="star_"] a').map(((e3, t3) => $(t3).text())).get().join(" ")), clog.debug("解析到名称:", t2);
- }
- return t2;
- }
- findCarNumAndHref(e2) {
- var t2, n2;
- let a2, i2, s2, o2 = e2.find("a"), r2 = o2.attr("href"), l2 = e2.find(".video-title");
- if (l2.length > 0) {
- let t3 = l2.find("strong");
- t3.length > 0 && (a2 = t3.text().trim()), i2 = o2.attr("title") ? o2.attr("title").trim() : a2 ? l2.text().replace(a2, "").trim() : l2.text().trim(), s2 = e2.find(".meta").text().trim();
- }
- if (!a2) {
- let o3 = e2.find("img");
- r2 && o3.length > 0 && (i2 = (null == (t2 = o3.attr("title")) ? void 0 : t2.trim()) || (null == (n2 = o3.attr("data-title")) ? void 0 : n2.trim()));
- const l3 = /* @__PURE__ */ __name((e3) => /^\d{4}-\d{1,2}-\d{1,2}$/.test(e3), "l");
- s2 = e2.find("date").map(((e3, t3) => $(t3).text().trim())).get().find(l3), a2 = e2.find("date").map(((e3, t3) => $(t3).text().trim())).get().find(((e3) => !l3(e3)));
- }
- if (!a2) {
- const e3 = "提取番号信息失败";
- throw show.error(e3), new Error(e3);
- }
- return {
- carNum: normalizeCarNum(a2),
- aHref: r2,
- url: r2,
- title: i2,
- publishTime: s2,
- fc2Source: ["fc2", "123av"].includes(e2.attr("data-jhs-fc2-source")) ? e2.attr("data-jhs-fc2-source") : "fc2"
- };
- }
- showCarNumBox(e2) {
- const t2 = $(".movie-list .item").toArray().find(((t3) => $(t3).find(".video-title strong").text() === e2));
- if (t2) {
- const n2 = $(t2);
- n2.attr("data-hide") === "yes" && (n2.show(), n2.removeAttr("data-hide"));
- }
- }
- _replaceSingleHdImg(e2) {
- if ("true" === e2.dataset.hdReplaced) return;
- if (r) {
- const isJavdbCdn = /jdbstatic\.com|javdb\.com/i.test(e2.src);
- if (isJavdbCdn) {
- const originalSrc = e2.src;
- e2.src = e2.src.replace("thumbs", "covers");
- e2.dataset.hdReplaced = "true";
- e2.title = "";
- e2.onerror = function() {
- if (this.src !== originalSrc) {
- this.src = originalSrc;
- this.onerror = null;
- }
- };
- }
- } else if (l) {
- const t2 = /\/(imgs|pics)\/(thumb|thumbs)\//, n2 = /(\.jpg|\.jpeg|\.png)$/i;
- t2.test(e2.src) ? (e2.src = e2.src.replace(t2, "/$1/cover/").replace(n2, "_b$1"), e2.dataset.hdReplaced = "true", e2.dataset.title = e2.title, e2.title = "") : /ps(\.jpg|\.jpeg|\.png)$/i.test(e2.src) && (e2.src = e2.src.replace(/ps(\.jpg|\.jpeg|\.png)$/i, "pl$1"), e2.dataset.hdReplaced = "true", e2.dataset.title = e2.title, e2.title = "");
- }
- }
- replaceHdImg(e2) {
- if (e2 && "string" == typeof e2.jquery && (e2 = e2.toArray()), e2 || (e2 = document.querySelectorAll(this.getSelector().coverImgSelector)), !e2.length) return;
- const t2 = Array.from(e2).filter(((e3) => "true" !== e3.dataset.hdReplaced && "true" !== e3.dataset.jhsHdObserved));
- if ("IntersectionObserver" in window && !this.hdImageObserver) this.hdImageObserver = new IntersectionObserver(((entries) => {
- entries.forEach(((entry) => {
- entry.isIntersecting && (this.hdImageObserver.unobserve(entry.target), delete entry.target.dataset.jhsHdObserved, this._replaceSingleHdImg(entry.target));
- }));
- }), { rootMargin: "200px" });
- for (const image of t2) this.hdEagerRemaining > 0 ? (this.hdEagerRemaining--, this._replaceSingleHdImg(image)) : this.hdImageObserver ? (image.dataset.jhsHdObserved = "true", this.hdImageObserver.observe(image)) : this._replaceSingleHdImg(image);
- storageManager.getSetting("hoverBigImg", C).then(((e3) => {
- e3 === _ && (window.imageHoverPreviewObj ? window.imageHoverPreviewObj.bindEvents() : window.imageHoverPreviewObj = new ImageHoverPreview({
- selector: this.getSelector().coverImgSelector
- }));
- }));
- }
- getTranslationCache() {
- if (this.cache && "object" == typeof this.cache && !Array.isArray(this.cache)) return this.cache;
- try {
- this.cache = JSON.parse(localStorage.getItem("jhs_translate") || "{}");
- } catch (error) {
- clog.warn("列表翻译缓存无法解析,已忽略旧缓存", error), this.cache = {};
- }
- return this.cache && "object" == typeof this.cache && !Array.isArray(this.cache) ? this.cache : this.cache = {};
- }
- scheduleTranslationWrite() {
- this._debouncedTranslateWrite && clearTimeout(this._debouncedTranslateWrite), this._debouncedTranslateWrite = setTimeout((() => {
- localStorage.setItem("jhs_translate", JSON.stringify(this.getTranslationCache()));
- }), 500);
- }
- applyTranslatedTitle(e2, t2, n2) {
- const a2 = e2.find(".video-title");
- r ? (a2.contents().each((function() {
- 3 !== this.nodeType || "" === this.textContent.trim() || this.textContent.includes(n2) || (this.textContent = " " + t2 + " ");
- })), a2.attr("title", t2)) : a2.text(t2), e2.attr("data-jhs-translation-key", n2);
- }
- async translate(e2) {
- let t2, n2, a2 = e2.find(".video-title");
- if (r ? (t2 = a2.contents().filter(((e3, t3) => 3 === t3.nodeType && "" !== t3.textContent.trim())).text().trim(), n2 = e2.find(".video-title strong").text().trim()) : (t2 = (e2.find("img").attr("data-title") || "").trim(), n2 = (e2.find("a").attr("href") || "").split("/").filter(Boolean).pop()?.trim()), !t2 || !n2) return;
- const cache = this.getTranslationCache();
- if (cache[n2]) return void this.applyTranslatedTitle(e2, cache[n2], n2);
- let pending = this.translationPending.get(n2);
- if (!pending) {
- pending = _e(t2).then(((translated) => (cache[n2] = translated, this.scheduleTranslationWrite(), translated))).finally((() => this.translationPending.delete(n2))), this.translationPending.set(n2, pending);
- }
- this.applyTranslatedTitle(e2, await pending, n2);
- }
- async revertTranslation() {
- $(this.getSelector().itemSelector).toArray().forEach(((e2) => {
- let t2 = $(e2);
- const n2 = t2.find(".box").attr("title") || t2.find(".video-title").attr("title") || t2.find("img").attr("data-title");
- let a2;
- r && (a2 = t2.find(".video-title strong").text().trim());
- const i2 = t2.find(".video-title");
- i2.contents().each((function() {
- 3 !== this.nodeType || "" === this.textContent.trim() || this.textContent.includes(a2) || (this.textContent = " " + n2 + " ");
- })), i2.removeAttr("title");
- }));
- }
- addJumpPageControl() {
- const e2 = "gemini-jump-page-control";
- if ($("#" + e2).length > 0) return;
- if (0 === $(".pagination-link.is-current").length) return;
- const t2 = utils.getUrlParam(o, "page") || 1, n2 = $('
', {
- id: "jumpPageInput",
- placeholder: "页码",
- min: "1",
- value: t2 + 1
- }), a2 = $('