From 0885b5e0fee2cfd1f2437f8122a96a4a61e6d5ac Mon Sep 17 00:00:00 2001 From: "lzyme.dev@gmail.com" Date: Sat, 29 Aug 2026 22:02:02 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=A0=87=E8=AE=B0=E5=88=A0?= =?UTF-8?q?=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dist/jhs-enhance.user.js | 249 +++++++++++++++++++++++++++++++++++- src/core/base-plugin.js | 1 + src/plugins/cover-button.js | 46 ++++++- src/plugins/setting.js | 129 ++++++++++++++++++- src/plugins/wangpan-115.js | 69 ++++++++++ 5 files changed, 482 insertions(+), 12 deletions(-) diff --git a/dist/jhs-enhance.user.js b/dist/jhs-enhance.user.js index 963b7e4..797d3b3 100644 --- a/dist/jhs-enhance.user.js +++ b/dist/jhs-enhance.user.js @@ -1809,6 +1809,7 @@ ${value}\r __publicField(this, "videoSvg", ''); __publicField(this, "screenSvg", ''); __publicField(this, "recoveryVideoSvg", ''); + __publicField(this, "markDeleteSvg", ''); } getName() { throw new Error(`${this.constructor.name} 未显示getName()`); @@ -2777,6 +2778,7 @@ ${value}\r
🚫 屏蔽配置
📋 定时任务
🌐 外部网站
+
🗑️ 标记删除
⌨️ 快捷键配置
🧹 清理缓存
💵 打赏作者
@@ -3210,6 +3212,25 @@ ${value}\r + +
+
+ 标记删除列表 + +
+
+ + +
+
+
加载中...
+
+
+

快捷键修改后, 刷新页面生效

@@ -3945,19 +3966,23 @@ ${value}\r isJavBus$1 && this.getBean("BusImgPlugin").logImageHeightsByRow(); } bindClick() { + const self = this; $(".side-menu-item").on("click", (function() { $(".side-menu-item").removeClass("active"); $(this).addClass("active"); $(".content-panel").hide(); const panelId = $(this).data("panel"); $("#" + panelId).show(); - if ("cache-panel" === panelId) { + if ("cache-panel" === panelId || "mark-delete-panel" === panelId) { $("#saveBtn").hide(); - $("#clean-all").show(); + "cache-panel" === panelId ? $("#clean-all").show() : $("#clean-all").hide(); } else { $("#saveBtn").show(); $("#clean-all").hide(); } + if ("mark-delete-panel" === panelId) { + self.loadMarkDeleteList(); + } })); $("#importBtn").on("click", ((event) => this.importData(event))); $("#exportBtn").on("click", ((event) => this.exportData(event))); @@ -4003,6 +4028,71 @@ ${value}\r } $widthInput.on("input", updateBorder); $colorPicker.on("input", updateBorder); + $("#markDeleteSelectAll").on("change", (function () { + const checked = $(this).prop("checked"); + $(".mark-delete-item-cb").prop("checked", checked); + })); + $("#batchDeleteBtn").on("click", (async () => { + const markedList = await storageManager.forage.getItem("markedDeleteList") || []; + if (markedList.length === 0) { + show.error("没有标记删除的作品"); + return; + } + const checkedItems = []; + $(".mark-delete-item-cb:checked").each((function () { + const index = parseInt($(this).data("index")); + checkedItems.push(markedList[index]); + })); + if (checkedItems.length === 0) { + show.error("请至少选择一个作品"); + return; + } + utils.q(null, `确认一键删除 ${checkedItems.length} 个作品?此操作不可撤销!`, (async () => { + let loadObj = loading(); + try { + const matchPlugin = this.getBean("WangPan115MatchPlugin"); + const result = await matchPlugin.batchDeleteMarked(checkedItems, ((current, total, item, status) => { + loadObj.close(); + loadObj = loading(); + $(`#markDeleteProgress`).text(`正在删除: ${current}/${total} - ${item.carNum} ${status}`); + })); + loadObj.close(); + const remaining = markedList.filter((item) => !checkedItems.some((ci) => ci.carNum === item.carNum)); + await storageManager.forage.setItem("markedDeleteList", remaining); + show.ok(`删除完成: 成功 ${result.deletedCount} 个,失败 ${result.failedCount} 个`); + this.loadMarkDeleteList(); + } catch (error) { + loadObj.close(); + console.error("批量删除失败:", error); + show.error(`批量删除失败: ${error.message || "网络错误"}`); + } + })); + })); + $(document).on("click", ".mark-delete-remove-btn", (async (event) => { + event.preventDefault(); + const $btn = $(event.currentTarget); + const index = parseInt($btn.data("index")); + const markedList = await storageManager.forage.getItem("markedDeleteList") || []; + if (index >= 0 && index < markedList.length) { + const item = markedList[index]; + utils.q(null, `确认从标记列表中移除 "${item.title}"?`, (async () => { + markedList.splice(index, 1); + await storageManager.forage.setItem("markedDeleteList", markedList); + show.ok(`已移除: ${item.title}`); + const self2 = this; + $(`.markDeleteSvg`).each((function () { + const $markBtn = $(this); + const $box2 = $markBtn.closest(".item"); + const {carNum: carNum2} = self2.getBoxCarInfo($box2); + if (carNum2 === item.carNum) { + $markBtn.removeClass("marked"); + $markBtn.attr("title", "标记删除"); + } + })); + this.loadMarkDeleteList(); + })); + } + })); } async saveForm() { let settingObj = await storageManager.getSetting(); @@ -4077,6 +4167,35 @@ ${value}\r this.getBean("BlacklistPlugin").resetBtnTip(); this.getBean("BlacklistPlugin").reloadTable(); } + + async loadMarkDeleteList() { + const markedList = await storageManager.forage.getItem("markedDeleteList") || []; + const $list = $("#markDeleteList"); + const $count = $("#markDeleteCount"); + $count.text(`共 ${markedList.length} 个作品`); + if (markedList.length === 0) { + $list.html('
暂无标记删除的作品
'); + return; + } + let html = ""; + for (let i = 0; i < markedList.length; i++) { + const item = markedList[i]; + const truncatedTitle = item.title && item.title.length > 30 ? item.title.substring(0, 30) + "..." : item.title; + const detailUrl = item.url ? item.url.startsWith("http") ? item.url : window.location.origin + item.url : "#"; + html += `
+ + +
${item.carNum}
+ ${truncatedTitle} +
${item.publishTime || "-"} | 评分: ${item.score || "-"}
+
+ ✕ +
`; + } + html += `
`; + $list.html(html); + $("#markDeleteSelectAll").prop("checked", true); + } addLabelTag(containerId, keyword) { const $tagBox = $(`${containerId} .tag-box`); let $label, color = "#333"; @@ -8732,20 +8851,84 @@ ${err.stack}` : ""); data-match='${JSON.stringify(matchList)}' title="点击查看匹配详情">匹配${matchList.length}个`); const $deleteBtn = $box2.find(".delete115Svg"); + const $markBtn = $box2.find(".markDeleteSvg"); if ($deleteBtn.length > 0 && matchList[0].dirId) { $deleteBtn.attr("data-match", JSON.stringify(matchList)); $deleteBtn.show(); } + if ($markBtn.length > 0) { + $markBtn.show(); + this.updateMarkBtnStyle($markBtn, carNum2); + } } else { $box2.find(".jhs-match-btn").replaceWith(`未匹配`); const $deleteBtn = $box2.find(".delete115Svg"); + const $markBtn = $box2.find(".markDeleteSvg"); if ($deleteBtn.length > 0) { $deleteBtn.removeAttr("data-match"); $deleteBtn.hide(); } + if ($markBtn.length > 0) { + $markBtn.hide(); + } } } + + async updateMarkBtnStyle($markBtn, carNum2) { + const markedList = await storageManager.forage.getItem("markedDeleteList") || []; + const isMarked = markedList.some((item) => item.carNum === carNum2); + isMarked ? $markBtn.addClass("marked") : $markBtn.removeClass("marked"); + $markBtn.attr("title", isMarked ? "取消标记删除" : "标记删除"); + } + + async batchDeleteMarked(selectedItems, onProgress) { + let deletedCount = 0; + let failedCount = 0; + const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); + for (let i = 0; i < selectedItems.length; i++) { + const item = selectedItems[i]; + try { + const matchList = await this.searchFiles(item.carNum); + if (matchList.length === 0) { + failedCount++; + onProgress && onProgress(i + 1, selectedItems.length, item, "未匹配到115文件"); + continue; + } + const upperCarNum = item.carNum.toUpperCase(); + const safeMatchList = matchList.filter((m) => m.name.toUpperCase().includes(upperCarNum)); + if (safeMatchList.length === 0) { + failedCount++; + onProgress && onProgress(i + 1, selectedItems.length, item, "无安全可删除目录"); + continue; + } + let itemDeleted = 0; + for (const m of safeMatchList) { + try { + const result = await gmHttp.postForm("https://webapi.115.com/rb/delete", { + pid: m.dirId, + "fid[0]": m.folderId + }); + if (result && result.state) { + itemDeleted++; + deletedCount++; + } + } catch (e) { + console.error(`删除失败: ${m.name}`, e); + } + } + onProgress && onProgress(i + 1, selectedItems.length, item, itemDeleted > 0 ? `已删除${itemDeleted}个` : "删除失败"); + if (i < selectedItems.length - 1) { + await delay(800); + } + } catch (error) { + failedCount++; + onProgress && onProgress(i + 1, selectedItems.length, item, error.message || "出错"); + console.error(`批量删除出错 [${item.carNum}]:`, error); + } + } + return {deletedCount, failedCount}; + } showDeleteDialog(carNum2, matchList, afterDelete) { const upperCarNum = carNum2.toUpperCase(); const safeMatchList = matchList.filter((m) => m.name.toUpperCase().includes(upperCarNum)); @@ -8758,7 +8941,7 @@ ${err.stack}` : ""); if (!bytes) return "-"; const units = ["B", "KB", "MB", "GB", "TB"]; let size = parseFloat(bytes), unit = 0; - for (; size >= 1024 && unit < units.length - 1;) size /= 1024, unit++; + for (; size >= 1024 && unit < units.length - 1;) size /= 1024, unit++; return size.toFixed(2) + " " + units[unit]; }; let html = '
'; @@ -8794,7 +8977,7 @@ ${err.stack}` : ""); show.error("请至少选择一个文件夹"); return; } - const selected = checked.map((function () { + const selected = checked.map((function () { return parseInt($(this).data("index")); })).get(); layer.close(index); @@ -8821,7 +9004,7 @@ ${err.stack}` : ""); } } }); - $(`#layui-layer${layerIndex}`).on("change", ".jhs-delete-select-all", (function () { + $(`#layui-layer${layerIndex}`).on("change", ".jhs-delete-select-all", (function () { const checked = $(this).prop("checked"); $(`#layui-layer${layerIndex} .jhs-delete-folder-cb`).prop("checked", checked); })); @@ -8885,10 +9068,15 @@ ${err.stack}` : ""); data-match='${JSON.stringify(matchList)}' title="${title}">匹配${matchList.length}个`); const $deleteBtn = $box2.find(".delete115Svg"); + const $markBtn = $box2.find(".markDeleteSvg"); if ($deleteBtn.length > 0 && matchList[0].dirId) { $deleteBtn.attr("data-match", JSON.stringify(matchList)); $deleteBtn.show(); } + if ($markBtn.length > 0) { + $markBtn.show(); + this.updateMarkBtnStyle($markBtn, carNum2); + } } else $box2.find(".video-title").prepend(`未匹配`); @@ -9873,7 +10061,7 @@ ${err.stack}` : ""); justify-content: space-between; } .tool-box span{ - opacity:.3 + opacity:.6 } .tool-box span:hover{ opacity:1 @@ -9885,6 +10073,24 @@ ${err.stack}` : ""); [data-theme="dark"] .tool-box svg path { fill: white; } + .tool-box .delete115Svg svg path { + fill: #e57373 !important; + } + [data-theme="dark"] .tool-box .delete115Svg svg path { + fill: #ef9a9a !important; + } + .tool-box .markDeleteSvg svg path { + fill: #81c784 !important; + } + [data-theme="dark"] .tool-box .markDeleteSvg svg path { + fill: #66bb6a !important; + } + .tool-box .markDeleteSvg.marked svg path { + fill: #d84315 !important; + } + [data-theme="dark"] .tool-box .markDeleteSvg.marked svg path { + fill: #e64a19 !important; + } /* 鼠标移入时的弹性动画 */ @@ -9965,6 +10171,7 @@ ${err.stack}` : ""); if (!($box2.find(".tool-box").length > 0)) { isJavDb$1 && $box2.find(".tags").append(`
+ ${this.screenSvg} @@ -10028,6 +10235,7 @@ ${err.stack}` : ""); if ($box2.find(".avatar-box").length > 0) return; $box2.find(".photo-info").append(`
+ ${this.screenSvg} @@ -10200,6 +10408,35 @@ ${err.stack}` : ""); $(event.currentTarget).hasClass("titleSvg") ? utils.copyToClipboard("标题", title) : $(event.currentTarget).hasClass("carNumSvg") ? utils.copyToClipboard("番号", carNum2) : $(event.currentTarget).hasClass("downSvg") && fetch($img.attr("src")).then(((response) => response.blob())).then(((blob) => { utils.download(blob, carNum2 + " " + title + ".jpg"); })); + })); + $(document).on("click", ".markDeleteSvg", (async (event) => { + event.preventDefault(); + event.stopPropagation(); + const $btn = $(event.currentTarget); + const $box2 = $btn.closest(".item"); + const {carNum: carNum2, title, publishTime, url} = this.getBoxCarInfo($box2); + const score = $box2.find(".score").text().trim() || "无评分"; + let markedList = await storageManager.forage.getItem("markedDeleteList") || []; + const existingIndex = markedList.findIndex((item) => item.carNum === carNum2); + if (existingIndex > -1) { + markedList.splice(existingIndex, 1); + $btn.removeClass("marked"); + $btn.attr("title", "标记删除"); + show.ok(`已取消标记: ${title}`); + } else { + markedList.push({ + carNum: carNum2, + title, + publishTime, + score, + url, + markedTime: (/* @__PURE__ */ new Date()).toISOString() + }); + $btn.addClass("marked"); + $btn.attr("title", "取消标记删除"); + show.ok(`已标记删除: ${title}`); + } + await storageManager.forage.setItem("markedDeleteList", markedList); })); $(document).on("click", ".delete115Svg", (async (event) => { event.preventDefault(); diff --git a/src/core/base-plugin.js b/src/core/base-plugin.js index ef10d79..311be43 100644 --- a/src/core/base-plugin.js +++ b/src/core/base-plugin.js @@ -21,6 +21,7 @@ class BasePlugin { __publicField(this, "videoSvg", ''); __publicField(this, "screenSvg", ''); __publicField(this, "recoveryVideoSvg", ''); + __publicField(this, "markDeleteSvg", ''); } getName() { throw new Error(`${this.constructor.name} 未显示getName()`); diff --git a/src/plugins/cover-button.js b/src/plugins/cover-button.js index a1952dd..805ddbf 100644 --- a/src/plugins/cover-button.js +++ b/src/plugins/cover-button.js @@ -7,7 +7,18 @@ class CoverButtonPlugin extends BasePlugin { return "CoverButtonPlugin"; } async initCss() { - return `\n \n `; + return `\n \n `; } handle() { if (window.isListPage) { @@ -19,10 +30,10 @@ class CoverButtonPlugin extends BasePlugin { $(this.getSelector().itemSelector).toArray().forEach((ele => { let $box2 = $(ele); if (!($box2.find(".tool-box").length > 0)) { - isJavDb && $box2.find(".tags").append(`\n
\n \n ${this.screenSvg}\n \n ${this.videoSvg}\n \n
\n
${this.handleSvg}
\n \n \n
\n \n
\n
${this.siteSvg}
\n \n \n
\n \n
\n
${this.copySvg}
\n \n
\n ${this.carNumSvg}\n ${this.titleSvg}\n ${this.downSvg}\n
\n
\n
\n `); + isJavDb && $box2.find(".tags").append(`\n
\n \n \n ${this.screenSvg}\n \n ${this.videoSvg}\n \n
\n
${this.handleSvg}
\n \n \n
\n \n
\n
${this.siteSvg}
\n \n \n
\n \n
\n
${this.copySvg}
\n \n
\n ${this.carNumSvg}\n ${this.titleSvg}\n ${this.downSvg}\n
\n
\n
\n `); if (isJavBus) { if ($box2.find(".avatar-box").length > 0) return; - $box2.find(".photo-info").append(`\n
\n \n ${this.screenSvg}\n\n ${this.videoSvg}\n \n
\n
${this.handleSvg}
\n \n \n
\n \n
\n
${this.siteSvg}
\n \n \n
\n \n
\n
${this.copySvg}
\n \n
\n ${this.carNumSvg}\n ${this.titleSvg}\n ${this.downSvg}\n
\n
\n
\n `); + $box2.find(".photo-info").append(`\n
\n \n \n ${this.screenSvg}\n\n ${this.videoSvg}\n \n
\n
${this.handleSvg}
\n \n \n
\n \n
\n
${this.siteSvg}
\n \n \n
\n \n
\n
${this.copySvg}
\n \n
\n ${this.carNumSvg}\n ${this.titleSvg}\n ${this.downSvg}\n
\n
\n
\n `); } } })); @@ -135,6 +146,35 @@ class CoverButtonPlugin extends BasePlugin { utils.download(blob, carNum2 + " " + title + ".jpg"); })); })); + $(document).on("click", ".markDeleteSvg", (async event => { + event.preventDefault(); + event.stopPropagation(); + const $btn = $(event.currentTarget); + const $box2 = $btn.closest(".item"); + const {carNum: carNum2, title: title, publishTime: publishTime, url: url} = this.getBoxCarInfo($box2); + const score = $box2.find(".score").text().trim() || "无评分"; + let markedList = await storageManager.forage.getItem("markedDeleteList") || []; + const existingIndex = markedList.findIndex(item => item.carNum === carNum2); + if (existingIndex > -1) { + markedList.splice(existingIndex, 1); + $btn.removeClass("marked"); + $btn.attr("title", "标记删除"); + show.ok(`已取消标记: ${title}`); + } else { + markedList.push({ + carNum: carNum2, + title: title, + publishTime: publishTime, + score: score, + url: url, + markedTime: new Date().toISOString() + }); + $btn.addClass("marked"); + $btn.attr("title", "取消标记删除"); + show.ok(`已标记删除: ${title}`); + } + await storageManager.forage.setItem("markedDeleteList", markedList); + })); $(document).on("click", ".delete115Svg", (async event => { event.preventDefault(); event.stopPropagation(); diff --git a/src/plugins/setting.js b/src/plugins/setting.js index ba84097..ca4e395 100644 --- a/src/plugins/setting.js +++ b/src/plugins/setting.js @@ -94,7 +94,28 @@ class SettingPlugin extends BasePlugin { option.canSelect && (videoQualityHtml += ``); })); const coverButtonPlugin = this.getBean("CoverButtonPlugin"); - let settingHtml = `\n
\n
\n
💾 数据备份
\n
⚙️ 基础配置
\n
🚫 屏蔽配置
\n
📋 定时任务
\n
🌐 外部网站
\n
⌨️ 快捷键配置
\n
🧹 清理缓存
\n
💵 打赏作者
\n
\n \n
\n
\n \n \x3c!-- 阿里云盘面板 --\x3e\n
\n \n \n
\n 阿里云盘备份\n \n
\n
\n refresh_token:\n
\n \n
\n
\n \n
\n \n
\n WebDav备份\n \n
\n
\n 服务地址:\n
\n \n
\n
\n
\n 用户名:\n
\n \n
\n
\n
\n 密码:\n
\n \n
\n
\n
\n \n \n \x3c!-- 基础设置面板 --\x3e\n
\n
\n 已鉴定标签展示位置:\n
\n \n
\n
\n \n
\n 已鉴定内容处理方式:\n
\n \n
\n
\n \n
\n \n 鉴定补录演员信息 ❓\n \n
\n \n
\n
\n \n
\n \n
\n \n 列表页功能按钮\n \n
\n \n
\n 按钮-打开待鉴定:\n
\n \n
\n
\n \n
\n 按钮-打开已收藏:\n
\n \n
\n
\n \n
\n 打开待鉴定|已收藏 窗口数:\n
\n \n
\n
\n \n
\n 随机打开已收藏:\n
\n \n
\n
\n \n \n
\n \n
\n \n 封面快捷按钮\n \n
\n \n
\n \n ${coverButtonPlugin.screenSvg}长缩略图:\n \n
\n \n
\n
\n \n
\n \n ${coverButtonPlugin.videoSvg}预览视频:\n \n
\n \n
\n
\n \n
\n \n ${coverButtonPlugin.handleSvg}鉴定按钮:\n \n
\n \n
\n
\n \n
\n \n ${coverButtonPlugin.siteSvg}第三方跳转:\n \n
\n \n
\n
\n \n
\n \n ${coverButtonPlugin.copySvg}复制按钮:\n \n
\n \n
\n
\n \n
\n\n
\n 预览视频默认画质:\n
\n \n
\n
\n \n
\n 评论区条数:\n
\n \n
\n
\n \n
\n \n 高亮已收藏演员 ❓\n \n
\n \n
\n
\n \n
\n \n 分类标签|高亮演员-边框样式:\n \n
\n \n \n
\n
\n\n
\n \n
\n 请求超时时间(毫秒):\n
\n \n
\n
\n \n
\n 请求失败重试次数:\n
\n \n
\n
\n \n
\n \n
\n \n 启用控制台日志:\n \n
\n \n
\n
\n\n
\n 日志最大行数:\n
\n \n
\n
\n
\n \n \x3c!-- 定时任务 --\x3e\n
\n \n
\n 请求并发数量:\n
\n \n
\n
\n
\n 请求间隔时间(毫秒):\n
\n \n
\n
\n \n
\n \n
\n 自动检测屏蔽黑名单演员\n
\n \n 任务开关: ❓ \n \n
\n \n
\n
\n
\n 任务间隔时间:\n
\n \n
\n
\n
\n 检测规则:\n
\n \n
\n
\n
\n \n
\n 自动同步已收藏的演员\n
\n \n 任务开关: ❓ \n \n
\n \n
\n
\n
\n 任务间隔时间:\n
\n \n
\n
\n
\n \n
\n 自动检测已收藏演员的最新作品\n
\n \n 任务开关: ❓ \n \n
\n \n
\n
\n
\n 任务间隔时间:\n
\n \n
\n
\n
\n 检测规则:\n
\n \n
\n
\n
\n
\n \n \x3c!-- 域名设置面板 --\x3e\n
\n
\n 域名 - MissAv:\n
\n \n
\n
\n
\n 域名 - Jable:\n
\n \n
\n
\n
\n 域名 - Avgle:\n
\n \n
\n
\n
\n 域名 - JavTrailer:\n
\n \n
\n
\n
\n 域名 - 123Av:\n
\n \n
\n
\n
\n 域名 - JavDb:\n
\n \n
\n
\n
\n 域名 - JavBus:\n
\n \n
\n
\n
\n 域名 - SupJav:\n
\n \n
\n
\n
\n \n \x3c!-- 快捷键 --\x3e\n
\n

快捷键修改后, 刷新页面生效

\n
\n
\n 🚫 屏蔽:\n
\n \n
\n
\n
\n ⭐ 收藏:\n
\n \n
\n
\n
\n 📥️ 已下载:\n
\n \n
\n
\n
\n 🔍 已观看:\n
\n \n
\n
\n \n
\n \n ❓ 对视频列表页启用快捷键:\n \n
\n \n
\n
\n
\n \n
\n
\n ⏩ 快进:\n
\n \n
\n
\n \n
\n ▲ 折叠:\n
\n \n
\n
\n \n
\n 💻 控制台:\n
\n \n
\n
\n
\n\n\n
\n 显示已鉴定内容\n
\n 屏蔽单番号:\n
\n \n
\n
\n
\n 屏蔽演员:\n
\n \n
\n
\n
\n 屏蔽关键词:\n
\n \n
\n
\n
\n 收藏:\n
\n \n
\n
\n
\n 已下载:\n
\n \n
\n
\n
\n 已观看:\n
\n \n
\n
\n
\n 显示所有:\n
\n \n
\n
\n
\n\n
\n \n \x3c!-- 屏蔽设置面板 --\x3e\n
\n
\n \n 启用划词屏蔽 ❓ \n \n
\n \n
\n
\n \n
\n \n
\n
\n 评论区屏蔽词:\n
\n \n \n
\n
\n
\n
\n \n
\n \n
\n
\n 视频标题屏蔽词:\n
\n \n \n
\n
\n
\n
\n
\n
\n

以下操作, 不会对核心数据造成影响

\n
\n
\n ${buttonsHTML}\n
\n \n
\n
\n

如果JAV-JHS给您带来了便捷和价值,请考虑给予一点支持,您的鼓励是我持续创作的最大动力!感谢您的慷慨支持!

\n
\n
\n
\n TRC20-USDT二维码\n

TRC20-USDT

\n \n

点击地址可复制

\n
\n
\n
\n
\n
\n \n
\n \n \n
\n
\n
\n `; + let settingHtml = `\n
\n
\n
💾 数据备份
\n
⚙️ 基础配置
\n
🚫 屏蔽配置
\n
📋 定时任务
\n
🌐 外部网站
+
🗑️ 标记删除
+
⌨️ 快捷键配置
\n
🧹 清理缓存
\n
💵 打赏作者
\n
\n \n
\n
\n \n \x3c!-- 阿里云盘面板 --\x3e\n
\n \n \n
\n 阿里云盘备份\n \n
\n
\n refresh_token:\n
\n \n
\n
\n \n
\n \n
\n WebDav备份\n \n
\n
\n 服务地址:\n
\n \n
\n
\n
\n 用户名:\n
\n \n
\n
\n
\n 密码:\n
\n \n
\n
\n
\n \n \n \x3c!-- 基础设置面板 --\x3e\n
\n
\n 已鉴定标签展示位置:\n
\n \n
\n
\n \n
\n 已鉴定内容处理方式:\n
\n \n
\n
\n \n
\n \n 鉴定补录演员信息 ❓\n \n
\n \n
\n
\n \n
\n \n
\n \n 列表页功能按钮\n \n
\n \n
\n 按钮-打开待鉴定:\n
\n \n
\n
\n \n
\n 按钮-打开已收藏:\n
\n \n
\n
\n \n
\n 打开待鉴定|已收藏 窗口数:\n
\n \n
\n
\n \n
\n 随机打开已收藏:\n
\n \n
\n
\n \n \n
\n \n
\n \n 封面快捷按钮\n \n
\n \n
\n \n ${coverButtonPlugin.screenSvg}长缩略图:\n \n
\n \n
\n
\n \n
\n \n ${coverButtonPlugin.videoSvg}预览视频:\n \n
\n \n
\n
\n \n
\n \n ${coverButtonPlugin.handleSvg}鉴定按钮:\n \n
\n \n
\n
\n \n
\n \n ${coverButtonPlugin.siteSvg}第三方跳转:\n \n
\n \n
\n
\n \n
\n \n ${coverButtonPlugin.copySvg}复制按钮:\n \n
\n \n
\n
\n \n
\n\n
\n 预览视频默认画质:\n
\n \n
\n
\n \n
\n 评论区条数:\n
\n \n
\n
\n \n
\n \n 高亮已收藏演员 ❓\n \n
\n \n
\n
\n \n
\n \n 分类标签|高亮演员-边框样式:\n \n
\n \n \n
\n
\n\n
\n \n
\n 请求超时时间(毫秒):\n
\n \n
\n
\n \n
\n 请求失败重试次数:\n
\n \n
\n
\n \n
\n \n
\n \n 启用控制台日志:\n \n
\n \n
\n
\n\n
\n 日志最大行数:\n
\n \n
\n
\n
\n \n \x3c!-- 定时任务 --\x3e\n
\n \n
\n 请求并发数量:\n
\n \n
\n
\n
\n 请求间隔时间(毫秒):\n
\n \n
\n
\n \n
\n \n
\n 自动检测屏蔽黑名单演员\n
\n \n 任务开关: ❓ \n \n
\n \n
\n
\n
\n 任务间隔时间:\n
\n \n
\n
\n
\n 检测规则:\n
\n \n
\n
\n
\n \n
\n 自动同步已收藏的演员\n
\n \n 任务开关: ❓ \n \n
\n \n
\n
\n
\n 任务间隔时间:\n
\n \n
\n
\n
\n \n
\n 自动检测已收藏演员的最新作品\n
\n \n 任务开关: ❓ \n \n
\n \n
\n
\n
\n 任务间隔时间:\n
\n \n
\n
\n
\n 检测规则:\n
\n \n
\n
\n
\n
\n \n \x3c!-- 域名设置面板 --\x3e\n
\n
\n 域名 - MissAv:\n
\n \n
\n
\n
\n 域名 - Jable:\n
\n \n
\n
\n
\n 域名 - Avgle:\n
\n \n
\n
\n
\n 域名 - JavTrailer:\n
\n \n
\n
\n
\n 域名 - 123Av:\n
\n \n
\n
\n
\n 域名 - JavDb:\n
\n \n
\n
\n
\n 域名 - JavBus:\n
\n \n
\n
\n
\n 域名 - SupJav:\n
\n \n
\n
\n
\n \n \x3c!-- 标记删除 --\x3e +
+
+ 标记删除列表 + +
+
+ + +
+
+
加载中...
+
+
+ + \x3c!-- 快捷键 --\x3e\n
\n

快捷键修改后, 刷新页面生效

\n
\n
\n 🚫 屏蔽:\n
\n \n
\n
\n
\n ⭐ 收藏:\n
\n \n
\n
\n
\n 📥️ 已下载:\n
\n \n
\n
\n
\n 🔍 已观看:\n
\n \n
\n
\n \n
\n \n ❓ 对视频列表页启用快捷键:\n \n
\n \n
\n
\n
\n \n
\n
\n ⏩ 快进:\n
\n \n
\n
\n \n
\n ▲ 折叠:\n
\n \n
\n
\n \n
\n 💻 控制台:\n
\n \n
\n
\n
\n\n\n
\n 显示已鉴定内容\n
\n 屏蔽单番号:\n
\n \n
\n
\n
\n 屏蔽演员:\n
\n \n
\n
\n
\n 屏蔽关键词:\n
\n \n
\n
\n
\n 收藏:\n
\n \n
\n
\n
\n 已下载:\n
\n \n
\n
\n
\n 已观看:\n
\n \n
\n
\n
\n 显示所有:\n
\n \n
\n
\n
\n\n
\n \n \x3c!-- 屏蔽设置面板 --\x3e\n
\n
\n \n 启用划词屏蔽 ❓ \n \n
\n \n
\n
\n \n
\n \n
\n
\n 评论区屏蔽词:\n
\n \n \n
\n
\n
\n
\n \n
\n \n
\n
\n 视频标题屏蔽词:\n
\n \n \n
\n
\n
\n
\n
\n
\n

以下操作, 不会对核心数据造成影响

\n
\n
\n ${buttonsHTML}\n
\n \n
\n
\n

如果JAV-JHS给您带来了便捷和价值,请考虑给予一点支持,您的鼓励是我持续创作的最大动力!感谢您的慷慨支持!

\n
\n
\n
\n TRC20-USDT二维码\n

TRC20-USDT

\n \n

点击地址可复制

\n
\n
\n
\n
\n
\n \n
\n \n \n
\n
\n
\n `; layer.open({ type: 1, title: "设置", @@ -406,19 +427,23 @@ class SettingPlugin extends BasePlugin { isJavBus && this.getBean("BusImgPlugin").logImageHeightsByRow(); } bindClick() { + const self = this; $(".side-menu-item").on("click", (function() { $(".side-menu-item").removeClass("active"); $(this).addClass("active"); $(".content-panel").hide(); const panelId = $(this).data("panel"); $("#" + panelId).show(); - if ("cache-panel" === panelId) { + if ("cache-panel" === panelId || "mark-delete-panel" === panelId) { $("#saveBtn").hide(); - $("#clean-all").show(); + "cache-panel" === panelId ? $("#clean-all").show() : $("#clean-all").hide(); } else { $("#saveBtn").show(); $("#clean-all").hide(); } + if ("mark-delete-panel" === panelId) { + self.loadMarkDeleteList(); + } })); $("#importBtn").on("click", (event => this.importData(event))); $("#exportBtn").on("click", (event => this.exportData(event))); @@ -463,6 +488,74 @@ class SettingPlugin extends BasePlugin { } $widthInput.on("input", updateBorder); $colorPicker.on("input", updateBorder); + + $("#markDeleteSelectAll").on("change", (function () { + const checked = $(this).prop("checked"); + $(".mark-delete-item-cb").prop("checked", checked); + })); + + $("#batchDeleteBtn").on("click", (async () => { + const markedList = await storageManager.forage.getItem("markedDeleteList") || []; + if (markedList.length === 0) { + show.error("没有标记删除的作品"); + return; + } + const checkedItems = []; + $(".mark-delete-item-cb:checked").each((function () { + const index = parseInt($(this).data("index")); + checkedItems.push(markedList[index]); + })); + if (checkedItems.length === 0) { + show.error("请至少选择一个作品"); + return; + } + utils.q(null, `确认一键删除 ${checkedItems.length} 个作品?此操作不可撤销!`, (async () => { + let loadObj = loading(); + try { + const matchPlugin = this.getBean("WangPan115MatchPlugin"); + const result = await matchPlugin.batchDeleteMarked(checkedItems, ((current, total, item, status) => { + loadObj.close(); + loadObj = loading(); + $(`#markDeleteProgress`).text(`正在删除: ${current}/${total} - ${item.carNum} ${status}`); + })); + loadObj.close(); + const remaining = markedList.filter(item => !checkedItems.some(ci => ci.carNum === item.carNum)); + await storageManager.forage.setItem("markedDeleteList", remaining); + show.ok(`删除完成: 成功 ${result.deletedCount} 个,失败 ${result.failedCount} 个`); + this.loadMarkDeleteList(); + } catch (error) { + loadObj.close(); + console.error("批量删除失败:", error); + show.error(`批量删除失败: ${error.message || "网络错误"}`); + } + })); + })); + + $(document).on("click", ".mark-delete-remove-btn", (async event => { + event.preventDefault(); + const $btn = $(event.currentTarget); + const index = parseInt($btn.data("index")); + const markedList = await storageManager.forage.getItem("markedDeleteList") || []; + if (index >= 0 && index < markedList.length) { + const item = markedList[index]; + utils.q(null, `确认从标记列表中移除 "${item.title}"?`, (async () => { + markedList.splice(index, 1); + await storageManager.forage.setItem("markedDeleteList", markedList); + show.ok(`已移除: ${item.title}`); + const self = this; + $(`.markDeleteSvg`).each((function () { + const $markBtn = $(this); + const $box2 = $markBtn.closest(".item"); + const {carNum: carNum2} = self.getBoxCarInfo($box2); + if (carNum2 === item.carNum) { + $markBtn.removeClass("marked"); + $markBtn.attr("title", "标记删除"); + } + })); + this.loadMarkDeleteList(); + })); + } + })); } async saveForm() { let settingObj = await storageManager.getSetting(); @@ -537,6 +630,36 @@ class SettingPlugin extends BasePlugin { this.getBean("BlacklistPlugin").resetBtnTip(); this.getBean("BlacklistPlugin").reloadTable(); } + + async loadMarkDeleteList() { + const markedList = await storageManager.forage.getItem("markedDeleteList") || []; + const $list = $("#markDeleteList"); + const $count = $("#markDeleteCount"); + $count.text(`共 ${markedList.length} 个作品`); + if (markedList.length === 0) { + $list.html('
暂无标记删除的作品
'); + return; + } + let html = ""; + for (let i = 0; i < markedList.length; i++) { + const item = markedList[i]; + const truncatedTitle = item.title && item.title.length > 30 ? item.title.substring(0, 30) + "..." : item.title; + const detailUrl = item.url ? (item.url.startsWith("http") ? item.url : window.location.origin + item.url) : "#"; + html += `
+ + +
${item.carNum}
+ ${truncatedTitle} +
${item.publishTime || "-"} | 评分: ${item.score || "-"}
+
+ ✕ +
`; + } + html += `
`; + $list.html(html); + $("#markDeleteSelectAll").prop("checked", true); + } + addLabelTag(containerId, keyword) { const $tagBox = $(`${containerId} .tag-box`); let $label, color = "#333"; diff --git a/src/plugins/wangpan-115.js b/src/plugins/wangpan-115.js index 16a1764..301d321 100644 --- a/src/plugins/wangpan-115.js +++ b/src/plugins/wangpan-115.js @@ -359,20 +359,84 @@ const _WangPan115MatchPlugin = class _WangPan115MatchPlugin extends BasePlugin { if (matchList.length > 0) { $box2.find(".jhs-match-btn").replaceWith(`匹配${matchList.length}个`); const $deleteBtn = $box2.find(".delete115Svg"); + const $markBtn = $box2.find(".markDeleteSvg"); if ($deleteBtn.length > 0 && matchList[0].dirId) { $deleteBtn.attr("data-match", JSON.stringify(matchList)); $deleteBtn.show(); } + if ($markBtn.length > 0) { + $markBtn.show(); + this.updateMarkBtnStyle($markBtn, carNum2); + } } else { $box2.find(".jhs-match-btn").replaceWith(`未匹配`); const $deleteBtn = $box2.find(".delete115Svg"); + const $markBtn = $box2.find(".markDeleteSvg"); if ($deleteBtn.length > 0) { $deleteBtn.removeAttr("data-match"); $deleteBtn.hide(); } + if ($markBtn.length > 0) { + $markBtn.hide(); + } } } + async updateMarkBtnStyle($markBtn, carNum2) { + const markedList = await storageManager.forage.getItem("markedDeleteList") || []; + const isMarked = markedList.some(item => item.carNum === carNum2); + isMarked ? $markBtn.addClass("marked") : $markBtn.removeClass("marked"); + $markBtn.attr("title", isMarked ? "取消标记删除" : "标记删除"); + } + + async batchDeleteMarked(selectedItems, onProgress) { + let deletedCount = 0; + let failedCount = 0; + const delay = ms => new Promise(resolve => setTimeout(resolve, ms)); + for (let i = 0; i < selectedItems.length; i++) { + const item = selectedItems[i]; + try { + const matchList = await this.searchFiles(item.carNum); + if (matchList.length === 0) { + failedCount++; + onProgress && onProgress(i + 1, selectedItems.length, item, "未匹配到115文件"); + continue; + } + const upperCarNum = item.carNum.toUpperCase(); + const safeMatchList = matchList.filter(m => m.name.toUpperCase().includes(upperCarNum)); + if (safeMatchList.length === 0) { + failedCount++; + onProgress && onProgress(i + 1, selectedItems.length, item, "无安全可删除目录"); + continue; + } + let itemDeleted = 0; + for (const m of safeMatchList) { + try { + const result = await gmHttp.postForm("https://webapi.115.com/rb/delete", { + pid: m.dirId, + "fid[0]": m.folderId + }); + if (result && result.state) { + itemDeleted++; + deletedCount++; + } + } catch (e) { + console.error(`删除失败: ${m.name}`, e); + } + } + onProgress && onProgress(i + 1, selectedItems.length, item, itemDeleted > 0 ? `已删除${itemDeleted}个` : "删除失败"); + if (i < selectedItems.length - 1) { + await delay(800); + } + } catch (error) { + failedCount++; + onProgress && onProgress(i + 1, selectedItems.length, item, error.message || "出错"); + console.error(`批量删除出错 [${item.carNum}]:`, error); + } + } + return {deletedCount, failedCount}; + } + showDeleteDialog(carNum2, matchList, afterDelete) { const upperCarNum = carNum2.toUpperCase(); const safeMatchList = matchList.filter(m => m.name.toUpperCase().includes(upperCarNum)); @@ -504,10 +568,15 @@ const _WangPan115MatchPlugin = class _WangPan115MatchPlugin extends BasePlugin { const title = 1 === matchList.length ? "点击直接播放" : `点击查看${matchList.length}个匹配结果`; $box2.find(".video-title").prepend(`匹配${matchList.length}个`); const $deleteBtn = $box2.find(".delete115Svg"); + const $markBtn = $box2.find(".markDeleteSvg"); if ($deleteBtn.length > 0 && matchList[0].dirId) { $deleteBtn.attr("data-match", JSON.stringify(matchList)); $deleteBtn.show(); } + if ($markBtn.length > 0) { + $markBtn.show(); + this.updateMarkBtnStyle($markBtn, carNum2); + } } else $box2.find(".video-title").prepend(`未匹配`); } formatSize(bytes) {