4 Commits
v3 ... v5

Author SHA1 Message Date
lzyme.dev@gmail.com 39f252fa0b 多目录可选删除,暂时无法删除下载记录 2026-08-29 18:44:16 +08:00
lzyme.dev@gmail.com d99af94e74 新增作品列表下删除作品 2026-08-29 18:10:41 +08:00
lzyme.dev@gmail.com 2383406ede 新增作品列表下删除作品 2026-08-29 18:04:57 +08:00
lzyme.dev@gmail.com 8da5c36bf2 更新文档 2026-08-29 16:59:27 +08:00
5 changed files with 310 additions and 25 deletions
+5 -19
View File
@@ -12,43 +12,29 @@
### 一键安装
[![Install](https://img.shields.io/badge/点击安装-JHS--enhance.user.js-brightgreen)](https://github.com/替换为你的用户名/JHS-enhance/releases/latest/download/jhs-enhance.user.js)
[![Install](https://img.shields.io/badge/点击安装-JHS--enhance.user.js-brightgreen)](https://github.com/fireinrain/JHS-enhance/releases/latest/download/jhs-enhance.user.js)
> 点击上方按钮,Tampermonkey 会自动弹出安装提示。安装后脚本会自动更新(Tampermonkey 默认每天检查一次更新)。
### 手动安装
1. 从 [Releases](https://github.com/替换为你的用户名/JHS-enhance/releases) 页面下载最新 `jhs-enhance.user.js`
1. 从 [Releases](https://github.com/fireinrain/JHS-enhance/releases) 页面下载最新 `jhs-enhance.user.js`
2. 打开 Tampermonkey 管理面板 → 拖入文件安装
---
## 自动部署配置
## 自动部署
本项目使用 GitHub Actions 在推送代码时自动构建并发布到 [GitHub Releases](https://github.com/替换为你的用户名/JHS-enhance/releases)。首次使用只需一步:
### 修改 README 中的安装链接
将 README.md 中所有 `替换为你的用户名` 替换为你的 GitHub 用户名:
```
https://github.com/你的用户名/JHS-enhance/releases/latest/download/jhs-enhance.user.js
```
推送代码后,GitHub Actions 自动构建并将脚本上传到 Release,无需额外配置。
### 工作原理
本项目使用 GitHub Actions 在推送代码时自动构建并发布到 [GitHub Releases](https://github.com/fireinrain/JHS-enhance/releases)。
```
git push → GitHub Actions 自动构建 → 发布 GitHub Release
↓
https://github.com/用户名/JHS-enhance/releases/latest/download/jhs-enhance.user.js
https://github.com/fireinrain/JHS-enhance/releases/latest/download/jhs-enhance.user.js
↓
用户点击安装链接 → Tampermonkey 弹出安装提示
```
> **为什么不用 GitHub Pages?** Release 方案更简单,不需要配置 Pages,不需要额外权限,`/releases/latest/download/` 是固定链接,每次发版自动指向最新脚本。
---
## 功能概览
+152 -3
View File
@@ -2,7 +2,7 @@
// @name JAV-JHS
// @namespace JAV-JHS
// @version 3.3.6-fix
// @author xie bro
// @author xie bro,fireinrain
// @description Jav-鉴黄师 收藏、屏蔽、标记已下载; 屏蔽标签、屏蔽演员、同步收藏演员、新作品检测; 免VIP查看热播、Top250排行榜、Fc2ppv、可查看所有评论信息、相关清单; 支持云盘备份; 以图识图; 字幕搜索; JavDb|JavBus
// @license MIT
// @icon https://www.google.com/s2/favicons?sz=64&domain=javdb.com
@@ -8082,6 +8082,47 @@ ${err.stack}` : "");
}
return fileId;
}
async deleteOfflineTasksByKeyword(keyword) {
try {
const singInfo = await (async () => {
const res = await gmHttp.get("https://115.com/?ct=offline&ac=space&_=" + (/* @__PURE__ */ new Date()).getTime());
return "object" == typeof res ? res : null;
})();
if (!singInfo) {
console.error("删除离线任务: 未登录115网盘");
return;
}
const {sign, time} = singInfo, uid = singInfo.uid || (() => {
const match = document.cookie.match(/UID=(\d+)_/);
return match ? match[1] : null;
})() || await this.getUserId();
const taskListRes = await gmHttp.postForm("https://115.com/web/lixian/?ct=lixian&ac=task_lists", {
page: 1,
uid,
sign,
time
});
const tasks = (null == taskListRes ? void 0 : taskListRes.tasks) || [];
const keyword2 = keyword.toLowerCase().replace("fc2-", "");
let deletedCount = 0;
for (const task of tasks) {
if (task.name && task.name.toLowerCase().includes(keyword2)) {
console.log(`删除离线任务: ${task.name} (hash: ${task.info_hash})`);
await gmHttp.postForm("https://115.com/web/lixian/?ct=lixian&ac=task_del", {
"hash[0]": task.info_hash,
flag: 1,
uid,
sign,
time
});
deletedCount++;
}
}
deletedCount > 0 && console.log(`共删除 ${deletedCount} 个离线任务`);
} catch (error) {
console.error("删除离线任务失败:", error);
}
}
}
class ScreenShotPlugin extends BasePlugin {
getName() {
@@ -8666,11 +8707,25 @@ ${err.stack}` : "");
}
}
updateMatchStatus($box2, carNum2, matchList) {
matchList.length > 0 ? $box2.find(".jhs-match-btn").replaceWith(`<a class='jhs-match-btn a-success'
if (matchList.length > 0) {
$box2.find(".jhs-match-btn").replaceWith(`<a class='jhs-match-btn a-success'
data-keyword="${carNum2}"
data-match='${JSON.stringify(matchList)}'
title="点击查看匹配详情">匹配${matchList.length}个</a>`) : $box2.find(".jhs-match-btn").replaceWith(`<a class='jhs-match-error-btn a-info' data-keyword="${carNum2}"
title="点击查看匹配详情">匹配${matchList.length}个</a>`);
const $deleteBtn = $box2.find(".delete115Svg");
if ($deleteBtn.length > 0 && matchList[0].dirId) {
$deleteBtn.attr("data-match", JSON.stringify(matchList));
$deleteBtn.show();
}
} else {
$box2.find(".jhs-match-btn").replaceWith(`<a class='jhs-match-error-btn a-info' data-keyword="${carNum2}"
title="点击重新尝试匹配">未匹配</a>`);
const $deleteBtn = $box2.find(".delete115Svg");
if ($deleteBtn.length > 0) {
$deleteBtn.removeAttr("data-match");
$deleteBtn.hide();
}
}
}
async handleLoginRedirect() {
window.open("https://115.com");
@@ -8680,6 +8735,7 @@ ${err.stack}` : "");
let searchKeyword = carNum2.toLowerCase().replace("fc2-", "");
return (null == (_a2 = (await searchFiles(searchKeyword)).data) ? void 0 : _a2.map(((data) => ({
folderId: data.fid,
dirId: data.cid,
videoId: data.pc,
name: data.n,
createTime: utils.formatDate(new Date(1e3 * data.te)),
@@ -8729,6 +8785,11 @@ ${err.stack}` : "");
data-keyword="${carNum2}"
data-match='${JSON.stringify(matchList)}'
title="${title}">匹配${matchList.length}个</a>`);
const $deleteBtn = $box2.find(".delete115Svg");
if ($deleteBtn.length > 0 && matchList[0].dirId) {
$deleteBtn.attr("data-match", JSON.stringify(matchList));
$deleteBtn.show();
}
} else $box2.find(".video-title").prepend(`<a class='jhs-match-error-btn a-info'
data-keyword="${carNum2}"
title="未匹配,点击重试">未匹配</a>`);
@@ -9805,6 +9866,7 @@ ${err.stack}` : "");
if (!($box2.find(".tool-box").length > 0)) {
isJavDb$1 && $box2.find(".tags").append(`
<div class="tool-box" style="margin-left: auto; display: flex; align-items: center">
<span class="delete115Svg" title="删除115作品" style="margin-right: 15px; display: none;">${this.removeSvg}</span>
<span class="screenSvg" title="长缩略图" style="margin-right: 15px;">${this.screenSvg}</span>
<span class="videoSvg" title="播放视频" style="margin-right: 15px;">${this.videoSvg}</span>
@@ -9867,6 +9929,7 @@ ${err.stack}` : "");
if ($box2.find(".avatar-box").length > 0) return;
$box2.find(".photo-info").append(`
<div class="tool-box" style="display: flex; align-items: center;justify-content: flex-end">
<span class="delete115Svg" title="删除115作品" style="margin-right: 15px; display: none;">${this.removeSvg}</span>
<span class="screenSvg" title="长缩略图" style="margin-right: 15px;">${this.screenSvg}</span>
<span class="videoSvg" title="播放视频" style="margin-right: 15px;">${this.videoSvg}</span>
@@ -10039,6 +10102,92 @@ ${err.stack}` : "");
utils.download(blob, carNum2 + " " + title + ".jpg");
}));
}));
$(document).on("click", ".delete115Svg", (async (event) => {
event.preventDefault();
event.stopPropagation();
const $btn = $(event.currentTarget);
const matchData = $btn.attr("data-match");
if (!matchData) {
show.error("未找到115匹配数据");
return;
}
const matchList = JSON.parse(matchData);
const $box2 = $btn.closest(".item");
const { carNum: carNum2 } = this.getBoxCarInfo($box2);
this.showDeleteDialog(carNum2, matchList, $box2);
}));
}
showDeleteDialog(carNum2, matchList, $box2) {
const formatSize = (bytes) => {
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++;
return size.toFixed(2) + " " + units[unit];
};
let html = '<div style="padding: 15px; max-height: 400px; overflow-y: auto;">';
html += `<div style="margin-bottom: 12px; font-size: 14px;">作品 <b>${carNum2}</b> 匹配到 <b>${matchList.length}</b> 个文件夹:</div>`;
html += '<div style="margin-bottom: 10px;"><label style="cursor: pointer;"><input type="checkbox" class="jhs-delete-select-all" checked> <b>全选</b></label></div>';
for (let i = 0; i < matchList.length; i++) {
const m = matchList[i];
html += `<div style="margin-bottom: 6px; padding: 8px 10px; border: 1px solid #e0e0e0; border-radius: 4px; background: #fafafa;">
<label style="display: flex; align-items: flex-start; cursor: pointer; width: 100%;">
<input type="checkbox" class="jhs-delete-folder-cb" data-index="${i}" checked style="margin-top: 3px; flex-shrink: 0;">
<span style="margin-left: 8px; flex: 1;">
<div style="word-break: break-all;">${m.name}</div>
<div style="font-size: 12px; color: #888; margin-top: 2px;">${formatSize(m.size)} | ${m.createTime}</div>
</span>
</label>
</div>`;
}
html += "</div>";
const layerIndex = layer.open({
type: 1,
title: `删除115作品文件夹`,
content: html,
area: ["520px", "auto"],
btn: ["确定删除", "取消"],
shadeClose: false,
yes: async (index) => {
const $layer = $(`#layui-layer${index}`);
const checked = $layer.find(".jhs-delete-folder-cb:checked");
if (0 === checked.length) {
show.error("请至少选择一个文件夹");
return;
}
const selected = checked.map((function () {
return parseInt($(this).data("index"));
})).get();
layer.close(index);
let loadObj = loading();
let deletedCount = 0;
try {
for (const i of selected) {
const m = matchList[i];
const result = await gmHttp.postForm("https://webapi.115.com/rb/delete", {
pid: m.dirId,
"fid[0]": m.folderId
});
result && result.state ? deletedCount++ : console.error(`删除失败: ${m.name}`, result);
}
loadObj.close();
show.ok(`作品 "${carNum2}" 已删除 ${deletedCount} 个文件夹`);
const matchPlugin = this.getBean("WangPan115MatchPlugin");
const newMatchList = await matchPlugin.searchFiles(carNum2);
matchPlugin.updateMatchStatus($box2, carNum2, newMatchList);
this.getBean("WangPan115TaskPlugin").deleteOfflineTasksByKeyword(carNum2);
} catch (error) {
loadObj.close();
console.error("删除115文件失败:", error);
show.error(`删除失败: ${error.message || "网络错误"}`);
}
}
});
$(`#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);
}));
}
showImg($svgElement, $img, carNum2) {
$svgElement.html(this.videoSvg).attr("title", "播放视频");
File diff suppressed because one or more lines are too long
+43
View File
@@ -145,6 +145,49 @@ class WangPan115TaskPlugin extends BasePlugin {
}
return fileId;
}
//TODO: 暂时没有删除离线任务
// async deleteOfflineTasksByKeyword(keyword) {
// try {
// const singInfo = await (async () => {
// const res = await gmHttp.get("https://115.com/?ct=offline&ac=space&_=" + (new Date).getTime());
// return "object" == typeof res ? res : null;
// })();
// if (!singInfo) {
// console.error("删除离线任务: 未登录115网盘");
// return;
// }
// const {sign: sign, time: time} = singInfo, uid = singInfo.uid || (() => {
// const match = document.cookie.match(/UID=(\d+)_/);
// return match ? match[1] : null;
// })() || await this.getUserId();
// const taskListRes = await gmHttp.postForm("https://115.com/web/lixian/?ct=lixian&ac=task_lists", {
// page: 1,
// uid: uid,
// sign: sign,
// time: time
// });
// const tasks = (null == taskListRes ? void 0 : taskListRes.tasks) || [];
// const keyword2 = keyword.toLowerCase().replace("fc2-", "");
// let deletedCount = 0;
// for (const task of tasks) {
// if (task.name && task.name.toLowerCase().includes(keyword2)) {
// console.log(`删除离线任务: ${task.name} (hash: ${task.info_hash})`);
// await gmHttp.postForm("https://115.com/web/lixian/?ct=lixian&ac=task_del", {
// "hash[0]": task.info_hash,
// flag: 1,
// uid: uid,
// sign: sign,
// time: time
// });
// deletedCount++;
// }
// }
// deletedCount > 0 && console.log(`共删除 ${deletedCount} 个离线任务`);
// } catch (error) {
// console.error("删除离线任务失败:", error);
// }
// }
}
+21 -1
View File
@@ -341,7 +341,21 @@ const _WangPan115MatchPlugin = class _WangPan115MatchPlugin extends BasePlugin {
}
}
updateMatchStatus($box2, carNum2, matchList) {
matchList.length > 0 ? $box2.find(".jhs-match-btn").replaceWith(`<a class='jhs-match-btn a-success' \n data-keyword="${carNum2}"\n data-match='${JSON.stringify(matchList)}'\n title="点击查看匹配详情">匹配${matchList.length}个</a>`) : $box2.find(".jhs-match-btn").replaceWith(`<a class='jhs-match-error-btn a-info' data-keyword="${carNum2}" \n title="点击重新尝试匹配">未匹配</a>`);
if (matchList.length > 0) {
$box2.find(".jhs-match-btn").replaceWith(`<a class='jhs-match-btn a-success' \n data-keyword="${carNum2}"\n data-match='${JSON.stringify(matchList)}'\n title="点击查看匹配详情">匹配${matchList.length}个</a>`);
const $deleteBtn = $box2.find(".delete115Svg");
if ($deleteBtn.length > 0 && matchList[0].dirId) {
$deleteBtn.attr("data-match", JSON.stringify(matchList));
$deleteBtn.show();
}
} else {
$box2.find(".jhs-match-btn").replaceWith(`<a class='jhs-match-error-btn a-info' data-keyword="${carNum2}" \n title="点击重新尝试匹配">未匹配</a>`);
const $deleteBtn = $box2.find(".delete115Svg");
if ($deleteBtn.length > 0) {
$deleteBtn.removeAttr("data-match");
$deleteBtn.hide();
}
}
}
async handleLoginRedirect() {
window.open("https://115.com");
@@ -351,6 +365,7 @@ const _WangPan115MatchPlugin = class _WangPan115MatchPlugin extends BasePlugin {
let searchKeyword = carNum2.toLowerCase().replace("fc2-", "");
return (null == (_a2 = (await searchFiles(searchKeyword)).data) ? void 0 : _a2.map((data => ({
folderId: data.fid,
dirId: data.cid,
videoId: data.pc,
name: data.n,
createTime: utils.formatDate(new Date(1e3 * data.te)),
@@ -392,6 +407,11 @@ const _WangPan115MatchPlugin = class _WangPan115MatchPlugin extends BasePlugin {
if (!($box2.find("[class^='jhs-match-']").length > 0)) if (this.loginStatus === _WangPan115MatchPlugin.LoginStatus.LOGGED_OUT) $box2.find(".video-title").prepend(`<a class='jhs-match-no-login-btn a-info' \n data-keyword="${carNum2}" \n title="未登录115网盘">未登录</a>`); else if (matchList.length > 0) {
const title = 1 === matchList.length ? "点击直接播放" : `点击查看${matchList.length}个匹配结果`;
$box2.find(".video-title").prepend(`<a class='jhs-match-btn a-success' \n data-keyword="${carNum2}"\n data-match='${JSON.stringify(matchList)}'\n title="${title}">匹配${matchList.length}个</a>`);
const $deleteBtn = $box2.find(".delete115Svg");
if ($deleteBtn.length > 0 && matchList[0].dirId) {
$deleteBtn.attr("data-match", JSON.stringify(matchList));
$deleteBtn.show();
}
} else $box2.find(".video-title").prepend(`<a class='jhs-match-error-btn a-info' \n data-keyword="${carNum2}" \n title="未匹配,点击重试">未匹配</a>`);
}
formatSize(bytes) {