import { BasePlugin } from '../core/base-plugin.js'; import { __publicField, isJavBus, isJavDb, currentHref, Status_FILTER } from '../core/constants.js'; class BlacklistPlugin extends BasePlugin { getName() { return "BlacklistPlugin"; } async addBlacklist(event) { let tempEvent = { clientX: event.clientX, clientY: event.clientY + 80 }; const hasAddBlacklist = $("#addBlacklistBtn span").text().includes("已加入"); let dataInfo, title; if (currentHref.includes("/tags")) { const urlObj = new URL(currentHref); urlObj.searchParams.delete("page"); const checkTag = $("#jhs-check-tag").text().trim(); dataInfo = { starId: "no-" + checkTag, name: "虚拟演员-" + checkTag, allName: [ "虚拟演员" ], role: "虚拟演员", movieType: "虚拟演员", blacklistUrl: urlObj.toString() }; title = `是否将分类 ${checkTag} 加入到黑名单中?`; hasAddBlacklist && (title = `分类 ${checkTag} 已在黑名单中, 是否从当前页开始追加屏蔽?`); } else { dataInfo = this.getActressPageInfo(); title = `是否将该演员 ${dataInfo.name} 加入到黑名单中?`; hasAddBlacklist && (title = `演员 ${dataInfo.name} 已在黑名单中, 是否从当前页开始追加屏蔽?`); } const {starId: starId, name: name2, allName: allName, role: role, movieType: movieType, blacklistUrl: blacklistUrl} = dataInfo; currentHref.includes("page") && !currentHref.includes("page=1") && (title += "
注意: 当前页面非第一页, 屏蔽数据将从此页面开始"); if (isJavBus) { const afterStarParts = currentHref.split("/star/")[1].split("/"); if (afterStarParts.length > 1) { parseInt(afterStarParts[1]) > 1 && (title += "
注意: 当前页面非第一页, 屏蔽数据将从此页面开始"); } } utils.q(tempEvent, title, (async () => { const taskPlugin = this.getBean("TaskPlugin"); navigator.locks.request(taskPlugin.singleTaskKey, { ifAvailable: !0 }, (async lock => { if (lock) { this.loadObj = loading(); try { await storageManager.addBlacklistItem({ starId: starId, name: name2, allName: allName, role: role, movieType: movieType, url: blacklistUrl }); await this.filterActorVideo(name2, starId); const toast = show.ok(`屏蔽结束,是否跳转到最后一页: ${this.lastPageLink || "无"}`, { duration: -1, close: !0, onClick: () => { toast.closeShow(); this.lastPageLink && (window.location.href = this.lastPageLink); } }); } catch (e) { clog.error(e); const toast = show.error("发生错误, 是否填转到解析失败的那一页? (点击并跳转)", { duration: -1, close: !0, onClick: () => { toast.closeShow(); window.location.href = this.nextPageLink; } }); } finally { this.loadObj.close(); } } else show.error("当前有定时任务在后台执行中, 无法发起此操作"); })).catch((error => { console.error("锁任务出现错误:", error); clog.error("锁任务出现错误:", error); })); })); } async resetBtnTip() { const taskPlugin = this.getBean("TaskPlugin"), lastCheckBlacklistTimeStr = localStorage.getItem(taskPlugin.lastCheckBlacklistTimeKey) || "无", checkBlacklist_intervalTime = await storageManager.getSetting("checkBlacklist_intervalTime", 12); this.checkBlacklist_ruleTime = await storageManager.getSetting("checkBlacklist_ruleTime", 8760); $("#checkBlacklistBtn").attr("data-tip", `上次检测时间: ${lastCheckBlacklistTimeStr}; 检测间隔时间: ${checkBlacklist_intervalTime}小时`); } async openBlacklistDialog() { const taskPlugin = this.getBean("TaskPlugin"), settingObj = await storageManager.getSetting(); let html = `\n
\n
\n
\n ${this.blacklistSvg}  手动检测黑名单\n ${this.removeSvg}    清理数据\n ${this.settingSvg}    配置\n
\n
\n \n \n \n \n 重置\n
\n\n
\n
\n
\n `; layer.open({ type: 1, title: "演员黑名单", content: html, scrollbar: !1, area: utils.getResponsiveArea([ "80%", "90%" ]), anim: -1, success: async layero => { await this.loadTableData(); $(".layui-layer-content").on("click", "#cleanQueryBtn", (async event => { $("#searchValue").val(""); $("#dataType").val(""); $("#statusType").val(""); await this.reloadTable(); })).on("focusout keydown", "#searchValue", (async event => { if ("focusout" === event.type || "Enter" === event.key) { "Enter" === event.key && event.preventDefault(); if ("keydown" === event.type && "Enter" !== event.key) return; $("#dataType").val(""); await this.reloadTable(); } })).on("change", "#dataType", (async () => { $("#searchValue").val(""); await this.reloadTable(); })).on("change", "#statusType", (async () => { await this.reloadTable(); })).on("change", "#urlType", (async () => { await this.reloadTable(); })).on("click", "#toSetting", (() => { this.getBean("SettingPlugin").openSettingDialog("task-panel", (() => { $("#setting-blacklist").css({ border: "1px solid #f40" }); })); })).on("click", "#clearKeywordBlacklist", (async () => { await this.clearKeywordBlacklist(); })).on("click", ".open-url", (event => { event.preventDefault(); const $el = $(event.currentTarget), url = $el.attr("data-url"), name2 = $el.attr("data-name"); utils.openPage(url, name2, !0, event); })).on("click", "#checkBlacklistBtn", (event => { utils.q({ clientX: event.clientX, clientY: event.clientY + 20 }, "是否手动检测黑名单?", (() => { navigator.locks.request(taskPlugin.singleTaskKey, { ifAvailable: !0 }, (async lock => { if (lock) { await taskPlugin.loadConfig(); await taskPlugin.checkBlacklist(!0); } else show.error("当前有定时任务在后台执行中, 无法发起手动任务"); })).catch((error => { console.error("锁任务出现错误:", error); clog.error("锁任务出现错误:", error); })); })); })); }, end: () => { if (this.tableObj) { this.tableObj.destroy(); this.tableObj = null; } window.refresh(); } }); } async reloadTable() { if (!this.tableObj) return; const tableData = await this.getTableData(); this.tableObj.setData(tableData); } async getTableData() { const taskPlugin = this.getBean("TaskPlugin"), blacklist = await storageManager.getBlacklist(), blacklistCarList = await storageManager.getBlacklistCarList(), searchValue = $("#searchValue").val(), statusType = $("#statusType").val(), $dataType = $("#dataType"), dataType = $dataType.val(), urlType = $("#urlType").val(), blacklistCount = blacklist.length; let actorCount = 0, actressCount = 0, noStarCount = 0; const filteredBlacklist = blacklist.map((item => { "actor" === item.role ? actorCount++ : "actress" === item.role ? actressCount++ : "虚拟演员" === item.role && noStarCount++; let isUnCheck = !1; item.lastPublishTime && (isUnCheck = !taskPlugin.isUnnecessaryCheck(item.lastPublishTime, this.checkBlacklist_ruleTime)); return { ...item, isUnCheck: isUnCheck }; })).filter((item => !(searchValue && !item.name.includes(searchValue)) && (("normal" !== statusType || !item.isUnCheck) && (!("stop" === statusType && !item.isUnCheck) && (dataType ? item.role === dataType : !("hasT" === urlType && !item.url.includes("t=") && !item.url.includes("/tags")) && ("noT" !== urlType || !item.url.includes("t=") && !item.url.includes("/tags"))))))); $dataType.html(`\n \n \n \n \n `); $dataType.val(dataType); const carListMap = new Map; for (const car of blacklistCarList) { const starId = car.starId; carListMap.has(starId) || carListMap.set(starId, []); carListMap.get(starId).push(car); } const finalResult = filteredBlacklist.map((item => { const starId = item.starId, carList = carListMap.get(starId) || []; return { ...item, carList: carList, count: carList.length }; })); this.currentCarCount = finalResult.reduce(((accumulator, currentObject) => accumulator + (currentObject.count || 0)), 0); return finalResult; } async loadTableData() { this.checkBlacklist_ruleTime = await storageManager.getSetting("checkBlacklist_ruleTime") || 8760; const tableData = await this.getTableData(); this.tableObj = new Tabulator("#table-container", { layout: "fitColumns", placeholder: "暂无数据", virtualDom: !0, data: tableData, pagination: !0, paginationMode: "local", paginationSize: 20, paginationSizeSelector: [ 20, 50, 100, 1e3, 99999 ], paginationCounter: (pageSize, currentRow, currentPage, totalRows, totalPages) => `演员: ${totalRows}    番号总数: ${this.currentCarCount} `, responsiveLayout: "collapse", responsiveLayoutCollapse: !0, columnDefaults: { headerHozAlign: "center", hozAlign: "center" }, index: "starId", columns: [ { title: "演员", field: "name", sorter: "string", minWidth: 100, responsive: 0, headerSort: !1, formatter: (cell, formatterParams, onRendered) => { const item = cell.getData(); return `${item.name}`; } }, { title: "性别角色", field: "role", sorter: "string", width: 120, responsive: 5, formatter: (cell, formatterParams, onRendered) => { const role = cell.getData().role; let content = role; "actor" === role ? content = "男演员" : "actress" === role && (content = "女演员"); return content; } }, { title: "影视类别", field: "movieType", sorter: "string", width: 120, responsive: 5, formatter: (cell, formatterParams, onRendered) => { const movieType = cell.getData().movieType; let content = movieType; "censored" === movieType ? content = "有码" : "uncensored" === movieType && (content = "无码"); return content; } }, { title: "屏蔽类型", field: "url", sorter: "string", minWidth: 120, responsive: 4, visible: isJavDb, formatter: (cell, formatterParams, onRendered) => { const item = cell.getData(); if ("虚拟演员" === item.role) { const nameSplitList = item.name.split("-"); return `${nameSplitList.length > 0 ? nameSplitList[1] : item.name}`; } const tParam = utils.getUrlParam(item.url, "t"); if (!tParam) return "所有分类"; { const tParamList = tParam.toString().split(","); let selectTypeStr = ""; tParamList.forEach((i => { let value = categoryMap[i]; selectTypeStr += value ? value + " " : "未知类别:" + i + " "; })); selectTypeStr = selectTypeStr.trim(); if (selectTypeStr) return `${selectTypeStr}`; } } }, { 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: (cell, formatterParams, onRendered) => { let dataTip = "", content = "正常检测"; if (cell.getData().isUnCheck) { dataTip = `停更${this.checkBlacklist_ruleTime / 24 / 365}年以上, 下轮任务不再进行检测`; content = "停止检测"; } return `${content}`; } }, { title: "操作", sorter: "string", cssClass: "action-cell-dropdown", minWidth: 150, responsive: 0, headerSort: !1, formatter: (cell, formatterParams, onRendered) => { const item = cell.getData(); onRendered((() => { var _a2, _b; null == (_a2 = cell.getElement().querySelector(".delete-btn")) || _a2.addEventListener("click", (e => { const name2 = item.name, starId = item.starId; name2 ? starId ? utils.q(e, `是否移除对 ${name2} 的屏蔽?`, (async () => { await storageManager.removeBlacklistCarList(starId); await storageManager.deleteBlacklistItem(starId); show.info("操作成功"); this.reloadTable().then(); })) : show.error("获取starId失败") : show.error("获取名称失败"); })); null == (_b = cell.getElement().querySelector(".keyword-btn")) || _b.addEventListener("click", (e => { const prefixMap = item.carList.reduce(((dataMap, carItem) => { const prefix = carItem.carNum.split("-")[0] + "-"; dataMap[prefix] = (dataMap[prefix] || 0) + 1; return dataMap; }), {}), sortedPrefixList = Object.entries(prefixMap).map((([prefix, count]) => ({ prefix: prefix, count: count }))).sort(((a, b) => b.count - a.count)); console.log(sortedPrefixList); })); })); return '\n \x3c!-- 提取屏蔽词 --\x3e\n ✂️ 删除 \n '; } } ], 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: "每页行数" } } } }); } getCurrentStarUrl() { let urlWithoutSortType = window.location.href.replace(/([&?])sort_type=[^&]+(&|$)/, "$1"); urlWithoutSortType = urlWithoutSortType.replace(/[&?]$/, ""); urlWithoutSortType = urlWithoutSortType.replace(/\?&/, "?"); let urlWithoutPageNumber = urlWithoutSortType; urlWithoutPageNumber = urlWithoutPageNumber.replace(/([&?])page=\d+(&|$)/, "$1"); urlWithoutPageNumber = urlWithoutPageNumber.replace(/[&?]$/, ""); urlWithoutPageNumber = urlWithoutPageNumber.replace(/\?&/, "?"); urlWithoutPageNumber = urlWithoutPageNumber.replace(/\/(\d+)(?:\/(\d+))?(\?|$)/, ((match, id, page, suffix) => void 0 !== page ? `/${id}${suffix}` : match)); return urlWithoutPageNumber; } parseUrlId(url) { if (!url) throw new Error("url未传入"); return new URL(url).pathname.split("/").filter((segment => "" !== segment.trim())).pop(); } async filterAllVideo(actorName, $dom) { let movieList, nextPageLink; if ($dom) { isJavBus && $dom.find(".avatar-box").length > 0 && $dom.find(".avatar-box").parent().remove(); movieList = $dom.find(this.getSelector().requestDomItemSelector); nextPageLink = $dom.find(this.getSelector().nextPageSelector).attr("href"); } else { movieList = $(this.getSelector().itemSelector); nextPageLink = $(this.getSelector().nextPageSelector).attr("href"); } if (nextPageLink && 0 === movieList.length) { show.error("解析列表失败"); throw new Error("解析列表失败"); } for (const element of movieList) { const $item = $(element), {carNum: carNum2, url: url, publishTime: publishTime} = this.getBoxCarInfo($item); if (url && carNum2) try { if (await storageManager.getCar(carNum2)) continue; await storageManager.saveCar({ carNum: carNum2, url: url, names: actorName, actionType: Status_FILTER, publishTime: publishTime }); clog.log("屏蔽演员番号", actorName, carNum2); } catch (error) { console.error(`保存失败 [${carNum2}]:`, error); } } if (nextPageLink) { show.info("请不要关闭窗口, 正在解析下一页:" + nextPageLink); await new Promise((resolve => setTimeout(resolve, 500))); const html = await gmHttp.get(nextPageLink), parser = new DOMParser, next$dom = $(parser.parseFromString(html, "text/html")); await this.filterAllVideo(actorName, next$dom); } else { show.ok("执行结束!"); window.refresh(); } } async filterActorVideo(actorName, starId, $dom) { let {nextPageLink: nextPageLink} = await this.parseAndSaveFilterInfo($dom, actorName, starId); this.nextPageLink = nextPageLink; if (nextPageLink) { this.lastPageLink = nextPageLink; show.info("请不要关闭窗口, 正在解析下一页:" + nextPageLink); let next$dom; const pageNum = utils.getUrlParam(nextPageLink, "page") || 0, beyond60Plugin = this.getBean("Beyond60Plugin"); if (isJavDb && beyond60Plugin && pageNum > 60) { let {html: html, nextUrl: nextUrl, hasMore: hasMore} = await beyond60Plugin.handleBeyond60(nextPageLink), mergeHtml = `\n
${html}
\n ${nextUrl ? `` : ""}\n `; next$dom = utils.htmlTo$dom(mergeHtml); } else { clog.log("正在请求下一页内容:", nextPageLink); const html = await gmHttp.get(nextPageLink); next$dom = utils.htmlTo$dom(html); } await this.filterActorVideo(actorName, starId, next$dom); } else { show.ok("执行结束!"); window.refresh(); } } async parseAndSaveFilterInfo($dom, actorName, starId) { let movieList, nextPageLink; if ($dom) { let tempIsJavBus = !1, selectorType = "javdb"; if ($dom.text().includes("javbus")) { tempIsJavBus = !0; selectorType = "javbus"; } tempIsJavBus && $dom.find(".avatar-box").length > 0 && $dom.find(".avatar-box").parent().remove(); movieList = $dom.find(this.getSelector(selectorType).requestDomItemSelector); nextPageLink = $dom.find(this.getSelector(selectorType).nextPageSelector).attr("href"); } else { movieList = $(this.getSelector().itemSelector); nextPageLink = $(this.getSelector().nextPageSelector).attr("href"); } if (nextPageLink && 0 === movieList.length) return { nextPageLink: null, lastPublishTime: null }; const filterKeywordList = await storageManager.getTitleFilterKeyword(); let carDataList = [], lastPublishTime = null; for (const element of movieList) { const $item = $(element), {carNum: carNum2, url: url, publishTime: publishTime, title: title} = this.getBoxCarInfo($item); lastPublishTime || (lastPublishTime = publishTime); filterKeywordList.find((kw => title.includes(kw) || carNum2.startsWith(kw))) || url && carNum2 && carDataList.push({ carNum: carNum2, url: url, names: actorName, actionType: Status_FILTER, starId: starId, publishTime: publishTime }); } await storageManager.batchSaveBlacklistCarList(carDataList); return { nextPageLink: nextPageLink, lastPublishTime: lastPublishTime }; } async clearKeywordBlacklist() { let filterKeywordList = await storageManager.getTitleFilterKeyword(); const processedKeywordsSet = new Set; filterKeywordList.forEach((item => { /^[a-z]{2,}-/i.test(item) && processedKeywordsSet.add(item); })); if (0 === processedKeywordsSet.size) { show.info("没有需要清理的关键词数据"); return; } const carList = await storageManager.getCarList(), blacklistCarList = await storageManager.getBlacklistCarList(), carNumSet = new Set(carList.map((car => car.carNum))), keywords = Array.from(processedKeywordsSet), waitRemoveCarNumList = [], removableCarData = []; for (const car of blacklistCarList) { const carNum2 = car.carNum; let matched = !1; for (const keyword of keywords) if (carNum2.startsWith(keyword)) { removableCarData.push({ carNum: carNum2, names: car.names, matchedKeyword: keyword }); waitRemoveCarNumList.push(carNum2); matched = !0; break; } if (!matched && carNumSet.has(carNum2)) { removableCarData.push({ carNum: carNum2, names: car.names, matchedKeyword: "已在鉴定记录中" }); waitRemoveCarNumList.push(carNum2); } } const carNumCount = waitRemoveCarNumList.length; 0 !== carNumCount ? layer.open({ type: 1, title: `确认清理黑名单数据 (共 ${carNumCount} 个番号)`, area: [ "80%", "70%" ], anim: -1, content: `\n
\n
\n

以下匹配到 ${carNumCount} 个黑名单番号存在于屏蔽关键词中, 是否移除?

\n
\n
\n
\n `, btn: [ "确定清理", "取消" ], success: function(layero, index) { new Tabulator("#wait-remove-table", { data: removableCarData, virtualDom: !0, layout: "fitColumns", pagination: !0, paginationMode: "local", paginationSize: 50, paginationSizeSelector: [ 50, 100, 1e3, 99999 ], columns: [ { title: "演员", field: "names" }, { title: "番号", field: "carNum" }, { title: "匹配关键词", field: "matchedKeyword" } ], locale: "zh-cn", initialSort: [ { column: "names", dir: "asc" } ], langs: { "zh-cn": { pagination: { first: "首页", first_title: "首页", last: "尾页", last_title: "尾页", prev: "上一页", prev_title: "上一页", next: "下一页", next_title: "下一页", all: "所有", page_size: "每页行数" } } } }); }, yes: async index => { const removedCount = await storageManager.batchRemoveBlacklistCars(waitRemoveCarNumList); if (0 === removedCount) show.error("移除失败"); else { show.ok(`🎉 清理完成!已移除${removedCount}个相关黑名单番号数据`); await this.reloadTable(); layer.close(index); } }, btn2: function(index) { layer.close(index); } }) : show.info("没有需要清理的黑名单番号数据"); } } export { BlacklistPlugin };