// ==UserScript==
// @name 115Rename2025
// @namespace http://tampermonkey.net/
// @version 1.1
// @description 115网盘视频整理工具:1.根据番号查询并重命名文件 2.支持javbus/avmoo查询 3.演员归档自动分类 4.可设置归档根目录 5.支持中文字幕和无码标记
// @author db117 wusuowei111 Chunluren
// @include https://115.com/*
// @icon https://115.com/favicon.ico
// @domain javbus.com
// @domain avmoo.host
// @domain avsox.host
// @grant GM_notification
// @grant GM_xmlhttpRequest
// @grant GM_setValue
// @grant GM_getValue
// @license MIT
// ==/UserScript==
(function () {
// 添加页面内通知样式
const notificationStyle = `
<style>
.custom-notification {
position: fixed;
top: 20px;
right: 20px;
max-width: 300px;
background-color: rgba(0, 0, 0, 0.8);
color: white;
padding: 12px 20px;
border-radius: 4px;
z-index: 9999;
font-size: 14px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
transition: all 0.3s ease;
opacity: 0;
transform: translateY(-10px);
}
.custom-notification.success {
border-left: 4px solid #52c41a;
}
.custom-notification.error {
border-left: 4px solid #f5222d;
}
.custom-notification.info {
border-left: 4px solid #1890ff;
}
.custom-notification.show {
opacity: 1;
transform: translateY(0);
}
</style>`;
// 添加样式到页面
$('head').append(notificationStyle);
// 页面内通知函数
window.showPageNotification = function(message, type = 'info', duration = this.duration) {
const notificationId = 'custom-notification-' + Date.now();
const notificationHtml = `<div id="${notificationId}" class="custom-notification ${type}">${message}</div>`;
// 添加通知到页面
$('body').append(notificationHtml);
// 显示通知
setTimeout(() => {
$(`#${notificationId}`).addClass('show');
}, 10);
// 自动关闭
setTimeout(() => {
$(`#${notificationId}`).removeClass('show');
setTimeout(() => {
$(`#${notificationId}`).remove();
}, 300);
}, duration);
};
// 按钮
let rename_list = `
<li id="rename_list">
<a id="rename_all_javbus" class="mark" href="javascript:;">改名javbus</a>
<a id="rename_all_javbus_date" class="mark" href="javascript:;">改名javbus_时间</a>
<a id="rename_all_avmoo" class="mark" href="javascript:;">改名avmoo</a>
<a id="rename_all_avmoo_date" class="mark" href="javascript:;">改名avmoo_时间</a>
<a id="archive_to_folder" class="mark" href="javascript:;">归档至文件夹</a>
<a id="set_archive_root" class="mark" href="javascript:;">设置为归档根目录</a>
</li>
`;
/**
* 添加按钮的定时任务
*/
let interval = setInterval(buttonInterval, 1000);
// 默认使用根目录ID
const ROOT_DIR_CID = "0"; // 115网盘根目录的ID为"0"
// 尝试从存储中获取根目录ID,如果不存在则为null(表示未设置)
let archiveRootCid = GM_getValue("archiveRootCid", null);
let archiveRootName = GM_getValue("archiveRootName", null);
// javbus
let javbusBase = "https://www.javbus.com/";
// 直接访问番号页面 (使用番号直接访问)
let javbusDirectAccess = javbusBase;
// 无码页面基础URL
let javbusUncensoredBase = javbusBase + "uncensored/";
// avmoo
// 有码
let avmooSearch = "https://avmoo.host/cn/search/";
// 无码
let avmooUncensoredSearch = "https://avsox.host/cn/search/";
'use strict';
/**
* 添加按钮定时任务(检测到可以添加时添加按钮)
*/
function buttonInterval() {
let open_dir = $("div#js_float_content li[val='open_dir']");
if (open_dir.length !== 0 && $("li#rename_list").length === 0) {
open_dir.before(rename_list);
$("a#rename_all_javbus").click(
function () {
rename(rename_javbus, false);
});
$("a#rename_all_javbus_date").click(
function () {
rename(rename_javbus, true);
});
$("a#rename_all_avmoo").click(
function () {
rename(rename_avmoo, false);
});
$("a#rename_all_avmoo_date").click(
function () {
rename(rename_avmoo, true);
});
$("a#archive_to_folder").click(
function () {
archiveToActorFolder();
});
$("a#set_archive_root").click(
function () {
setArchiveRoot();
});
// 根据是否设置了根目录,显示不同的日志
if (archiveRootCid) {
console.log("添加按钮,当前归档根目录: " + archiveRootName + " (CID: " + archiveRootCid + ")");
} else {
console.log("添加按钮,未设置归档根目录,将使用115网盘根目录");
}
// 结束定时任务
clearInterval(interval);
}
}
/**
* 设置归档根目录
* 获取选中的文件夹信息并保存为归档根目录
*/
function setArchiveRoot() {
// 获取选中的文件夹
let selectedFolder = $("iframe[rel='wangpan']")
.contents()
.find("li.selected");
// 检查是否只选择了一个文件夹
if (selectedFolder.length !== 1) {
GM_notification(getDetails("请只选择一个文件夹", "设置失败"));
console.log("设置归档根目录失败:选择了 " + selectedFolder.length + " 个项目,请只选择一个文件夹");
return;
}
let $item = $(selectedFolder[0]);
// 检查是否是文件夹
let file_type = $item.attr("file_type");
if (file_type !== "0") {
GM_notification(getDetails("请选择文件夹类型", "设置失败"));
console.log("设置归档根目录失败:选中的不是文件夹");
return;
}
// 获取文件夹ID和名称
let cid = $item.attr("cate_id");
let name = $item.attr("title");
if (cid) {
// 保存到GM存储中
GM_setValue("archiveRootCid", cid);
GM_setValue("archiveRootName", name);
// 更新当前变量
archiveRootCid = cid;
archiveRootName = name;
GM_notification(getDetails(name, "归档根目录设置成功"));
console.log("归档根目录设置成功: " + name + " (CID: " + cid + ")");
} else {
GM_notification(getDetails("无法获取文件夹ID", "设置失败"));
console.log("设置归档根目录失败:无法获取文件夹ID");
}
}
/**
* 执行改名方法
* @param call 回调函数
* @param addDate 是否添加时间
*/
function rename(call, addDate) {
// 获取选中的文件数量
let selectedCount = $("iframe[rel='wangpan']").contents().find("li.selected").length;
showPageNotification(`开始处理 ${selectedCount} 个文件...`, 'info', 3000);
// 记录成功处理的数量
let successCount = 0;
// 获取所有已选择的文件
let list = $("iframe[rel='wangpan']")
.contents()
.find("li.selected")
.each(function (index, v) {
let $item = $(v);
// 原文件名称
let file_name = $item.attr("title");
// 文件类型
let file_type = $item.attr("file_type");
// 文件id
let fid;
// 后缀名
let suffix;
if (file_type === "0") {
// 文件夹
fid = $item.attr("cate_id");
} else {
// 文件
fid = $item.attr("file_id");
// 处理后缀
let lastIndexOf = file_name.lastIndexOf('.');
if (lastIndexOf !== -1) {
suffix = file_name.substr(lastIndexOf, file_name.length);
}
}
if (fid && file_name) {
let fh = getVideoCode(file_name);
if (fh) {
// 校验是否是中文字幕
let chineseCaptions = checkChineseCaptions(fh, file_name);
let Uncensored = checkUncensored (fh, file_name);
// 执行查询
call(fid, fh, suffix, chineseCaptions, Uncensored, addDate, function() {
// 成功回调
successCount++;
// 如果所有文件都处理完毕,显示总结通知
if (successCount === selectedCount) {
showPageNotification(`所有 ${successCount} 个文件处理完成`, 'success', 5000);
}
});
}
}
});
}
/**
* 通过javbus进行查询
*/
function rename_javbus(fid, fh, suffix, chineseCaptions, Uncensored, addDate, callback) {
requestJavbus(fid, fh, suffix, chineseCaptions, Uncensored, addDate, javbusDirectAccess, callback);
}
/**
* 请求javbus,并请求115进行改名
* @param fid 文件id
* @param fh 番号
* @param suffix 后缀
* @param chineseCaptions 是否有中文字幕
* @param url 请求地址
* @param addDate 是否添加时间
* @param callback 成功回调
*/
function requestJavbus(fid, fh, suffix, chineseCaptions, Uncensored, addDate, url, callback) {
GM_xmlhttpRequest({
method: "GET",
url: url + fh,
onload: xhr => {
// 匹配标题
let response = $(xhr.responseText);
// 尝试不同方式获取标题
let title = null;
// 方法1: 从详情页面的标题获取
let h3Title = response.find("h3");
if (h3Title.length > 0) {
title = h3Title.text().trim();
// 删除番号部分(如果有),保留后面的标题
if (title.toUpperCase().indexOf(fh.toUpperCase()) === 0) {
title = title.substring(fh.length).trim();
}
console.log("从h3获取标题: " + title);
}
// 方法2: 如果方法1没有获取到标题,尝试从img标签获取
if (!title || title.length === 0) {
title = response.find("div.photo-frame img").attr("title");
console.log("从img标签title属性获取标题: " + title);
}
// 方法3: 如果前两种方法都失败,尝试获取页面标题
if (!title || title.length === 0) {
title = response.find("title").text().trim();
// 清理标题中可能的额外信息
if (title.indexOf(" - JavBus") > 0) {
title = title.substring(0, title.indexOf(" - JavBus")).trim();
}
if (title.toUpperCase().indexOf(fh.toUpperCase()) === 0) {
title = title.substring(fh.length).trim();
}
console.log("从页面title获取标题: " + title);
}
// 时间
let date = response.find("div.photo-info date:last").html();
if (title && title.length > 0) {
console.log("最终使用标题: " + title);
// 构建新名称
let newName = buildNewName(fh, suffix, chineseCaptions, Uncensored, title);
// 添加时间
if (addDate && date) {
newName = date + "_" + newName;
}
if (newName) {
// 修改名称
send_115(fid, newName, fh, callback);
}
} else if (url !== javbusUncensoredBase) {
console.log("未找到标题,尝试无码页面");
// 进行无码重查询
requestJavbus(fid, fh, suffix, chineseCaptions, addDate, javbusUncensoredBase, callback);
} else {
console.log("无法获取标题: " + fh);
GM_notification(getDetails(fh, "无法获取标题"));
}
}
})
}
/**
* 通过avmoo进行查询
*/
function rename_avmoo(fid, fh, suffix, chineseCaptions, Uncensored, addDate, callback) {
requestAvmoo(fid, fh, suffix, chineseCaptions, Uncensored, addDate, avmooSearch, callback);
}
/**
* 请求avmoo,并请求115进行改名
*/
function requestAvmoo(fid, fh, suffix, chineseCaptions, Uncensored, addDate, url, callback) {
GM_xmlhttpRequest({
method: "GET",
url: url + fh,
onload: xhr => {
// 匹配标题
let response = $(xhr.responseText);
if (!(response.find("div.alert").length)) {
// 尝试不同方式获取标题
let title = null;
// 首先尝试从图片标题获取
title = response.find("div.photo-frame img").attr("title");
console.log("AVMOO从img标签title属性获取标题: " + title);
// 如果没有获取到,尝试从标题获取
if (!title || title.length === 0) {
title = response.find("h3").text().trim();
// 清理标题
if (title.toUpperCase().indexOf(fh.toUpperCase()) === 0) {
title = title.substring(fh.length).trim();
}
console.log("AVMOO从h3获取标题: " + title);
}
// 时间
let date = response.find("div.photo-info date:last").html();
if (title && title.length > 0) {
console.log("AVMOO最终使用标题: " + title);
// 构建新名称
let newName = buildNewName(fh, suffix, chineseCaptions, Uncensored, title);
// 添加时间
if (addDate && date) {
newName = date + "_" + newName;
}
if (newName) {
// 修改名称
send_115(fid, newName, fh, callback);
}
} else if (url !== avmooUncensoredSearch) {
console.log("AVMOO未找到标题,尝试无码页面");
// 进行无码查询
requestAvmoo(fid, fh, suffix, chineseCaptions, addDate, avmooUncensoredSearch, callback);
} else {
console.log("AVMOO无法获取标题: " + fh);
GM_notification(getDetails(fh, "无法获取标题"));
}
} else if (url !== avmooUncensoredSearch) {
// 进行无码查询
requestAvmoo(fid, fh, suffix, chineseCaptions, addDate, avmooUncensoredSearch, callback);
}
}
})
}
/**
* 构建新名称
* @param fh 番号
* @param suffix 后缀
* @param chineseCaptions 是否有中文字幕
* @param title 番号标题
* @returns {string} 新名称
*/
function buildNewName(fh, suffix, chineseCaptions, Uncensored, title) {
if (title) {
let newName = String(fh);
// 有中文字幕
if (chineseCaptions) {
newName = newName + "【中文字幕】";
}
// 有无码
if (Uncensored) {
newName = newName + "【无码】";
}
// 拼接标题
newName = newName + " " + title;
if (suffix) {
// 文件保存后缀名
newName = newName + suffix;
}
return newName;
}
}
/**
* 请求115接口
* @param id 文件id
* @param name 要修改的名称
* @param fh 番号
* @param callback 成功回调
*/
function send_115(id, name, fh, callback) {
let file_name = stringStandard(name);
$.post("https://webapi.115.com/files/edit", {
fid: id,
file_name: file_name
},
function (data, status) {
let result = JSON.parse(data);
if (!result.state) {
GM_notification(getDetails(fh, "修改失败"));
showPageNotification(`${fh} 修改失败: ${result.error}`, 'error', 3000);
console.log("请求115接口异常: " + unescape(result.error
.replace(/\\(u[0-9a-fA-F]{4})/gm, '%$1')));
} else {
GM_notification(getDetails(fh, "修改成功"));
showPageNotification(`${fh} 修改成功`, 'success', 2000);
console.log("修改文件名称,fh:" + fh, "name:" + file_name);
if (typeof callback === 'function') {
callback();
}
}
}
);
}
/**
* 通知参数
* @param text 内容
* @param title 标题
* @returns {{text: *, title: *, timeout: number}}
*/
function getDetails(text, title) {
return {
text: text,
title: title,
timeout: 1000
};
}
/**
* 115名称不接受(\/:*?\"<>|)
* @param name
*/
function stringStandard(name) {
return name.replace(/\\/g, "")
.replace(/\//g, " ")
.replace(/:/g, " ")
.replace(/\?/g, " ")
.replace(/"/g, " ")
.replace(/</g, " ")
.replace(/>/g, " ")
.replace(/\|/g, "")
.replace(/\*/g, " ");
}
/**
* 校验是否为中文字幕
* @param fh 番号
* @param title 标题
*/
function checkChineseCaptions(fh, title) {
if (title.indexOf("中文") !== -1) {
return true;
}
let regExp = new RegExp(fh + "[_-](UC|C)");
let match = title.toUpperCase().match(regExp);
if (match) {
return true;
}
}
/**
* 校验是否为无码
* @param fh 番号
* @param title 标题
*/
function checkUncensored(fh, title) {
if (title.indexOf("无码") !== -1) {
return true;
}
let regExp = new RegExp(fh + "[_-](UC|U)");
let match = title.toUpperCase().match(regExp);
if (match) {
return true; // 如果标题中包含 "-U" 或 "_U",则返回 true,表示为无码
}
return false; // 默认情况下,返回 false,表示不是无码
}
/**
* 获取番号
* @param title 源标题
* @returns {string} 提取的番号
*/
function getVideoCode(title) {
title = title.toUpperCase().replace("SIS001", "")
.replace("1080P", "")
.replace("720P", "");
let t = title.match(/T28[\-_]\d{3,4}/);
// 一本道
if (!t) {
t = title.match(/1PONDO[\-_]\d{6}[\-_]\d{2,4}/);
if (t) {
t = t.toString().replace("1PONDO_", "")
.replace("1PONDO-", "");
}
}
if (!t) {
t = title.match(/HEYZO[\-_]?\d{4}/);
}
if (!t) {
// 加勒比
t = title.match(/CARIB[\-_]\d{6}[\-_]\d{3}/);
if (t) {
t = t.toString().replace("CARIB-", "")
.replace("CARIB_", "");
}
}
if (!t) {
// 东京热
t = title.match(/N[-_]\d{4}/);
}
if (!t) {
// Jukujo-Club | 熟女俱乐部
t = title.match(/JUKUJO[-_]\d{4}/);
}
// 通用
if (!t) {
t = title.match(/[A-Z]{2,5}[-_]\d{3,5}/);
}
if (!t) {
t = title.match(/\d{6}[\-_]\d{2,4}/);
}
if (!t) {
t = title.match(/[A-Z]+\d{3,5}/);
}
if (!t) {
t = title.match(/[A-Za-z]+[-_]?\d+/);
}
if (!t) {
t = title.match(/\d+[-_]?\d+/);
}
if (!t) {
t = title;
}
if (t) {
t = t.toString().replace("_", "-");
console.log("找到番号:" + t);
return t;
}
}
/**
* 执行归档到演员文件夹的功能
*/
function archiveToActorFolder() {
// 获取选中的文件数量
let selectedCount = $("iframe[rel='wangpan']").contents().find("li.selected").length;
let processedCount = 0;
let successCount = 0;
showPageNotification(`开始归档 ${selectedCount} 个文件...`, 'info', 3000);
// 获取所有已选择的文件
$("iframe[rel='wangpan']")
.contents()
.find("li.selected")
.each(function (index, v) {
let $item = $(v);
// 原文件名称
let file_name = $item.attr("title");
// 文件类型
let file_type = $item.attr("file_type");
// 跳过文件夹类型
if (file_type === "0") {
console.log("跳过文件夹: " + file_name);
processedCount++;
checkAllCompleted();
return;
}
// 文件id
let fid = $item.attr("file_id");
if (fid && file_name) {
let fh = getVideoCode(file_name);
if (fh) {
// 执行查询演员并归档
requestJavbusForActor(fid, fh, function() {
processedCount++;
successCount++;
checkAllCompleted();
}, function() {
processedCount++;
checkAllCompleted();
});
} else {
processedCount++;
checkAllCompleted();
}
} else {
processedCount++;
checkAllCompleted();
}
});
// 检查是否所有文件都处理完毕
function checkAllCompleted() {
if (processedCount === selectedCount) {
if (successCount > 0) {
showPageNotification(`归档完成: ${successCount}/${selectedCount} 个文件成功处理`, 'success', 5000);
} else {
showPageNotification(`归档完成: 没有成功处理的文件`, 'info', 5000);
}
}
}
}
/**
* 请求javbus获取演员信息并归档
* @param fid 文件id
* @param fh 番号
* @param successCallback 成功回调
* @param failCallback 失败回调
*/
function requestJavbusForActor(fid, fh, successCallback, failCallback) {
console.log("开始查询番号演员信息: " + fh);
GM_xmlhttpRequest({
method: "GET",
url: javbusDirectAccess + fh,
onload: xhr => {
// 分析返回的HTML内容
let response = $(xhr.responseText);
console.log("获取到javbus页面内容,开始查找演员信息");
// 打印页面中的演员相关HTML,用于调试
let actorElements = response.find("span.genre");
console.log("找到潜在演员元素数量: " + actorElements.length);
// 查找所有演员元素
let actresses = [];
actorElements.each(function() {
let anchor = $(this).find("a[href*='/star/']");
if (anchor.length > 0) {
let actorName = anchor.text().trim();
let actorLink = anchor.attr("href");
console.log("找到演员: " + actorName + ", 链接: " + actorLink);
actresses.push({ name: actorName, link: actorLink });
}
});
if (actresses.length > 0) {
// 取第一个演员
let firstActress = actresses[0];
console.log("选择首位演员: " + firstActress.name + " 进行归档");
// 查找或创建文件夹,然后移动文件
findOrCreateFolderAndMove(fid, firstActress.name, successCallback, failCallback);
} else {
console.log("在有码页面未找到演员信息,尝试查询无码页面");
if (javbusDirectAccess !== javbusUncensoredBase) {
// 尝试查询无码
GM_xmlhttpRequest({
method: "GET",
url: javbusUncensoredBase + fh,
onload: xhrUnc => {
let responseUnc = $(xhrUnc.responseText);
console.log("获取到javbus无码页面内容,开始查找演员信息");
// 打印页面中的演员相关HTML,用于调试
let actorElementsUnc = responseUnc.find("span.genre");
console.log("找到潜在演员元素数量(无码): " + actorElementsUnc.length);
// 查找所有演员元素
let actressesUnc = [];
actorElementsUnc.each(function() {
let anchor = $(this).find("a[href*='/star/']");
if (anchor.length > 0) {
let actorName = anchor.text().trim();
let actorLink = anchor.attr("href");
console.log("找到演员(无码): " + actorName + ", 链接: " + actorLink);
actressesUnc.push({ name: actorName, link: actorLink });
}
});
if (actressesUnc.length > 0) {
// 取第一个演员
let firstActressUnc = actressesUnc[0];
console.log("选择首位演员(无码): " + firstActressUnc.name + " 进行归档");
// 查找或创建文件夹,然后移动文件
findOrCreateFolderAndMove(fid, firstActressUnc.name, successCallback, failCallback);
} else {
console.log("HTML内容: " + xhrUnc.responseText.substring(0, 500) + "...");
GM_notification(getDetails(fh, "未找到演员信息"));
console.log("在有码和无码页面均未找到演员信息: " + fh);
}
}
});
} else {
console.log("HTML内容: " + xhr.responseText.substring(0, 500) + "...");
GM_notification(getDetails(fh, "未找到演员信息"));
console.log("在有码页面未找到演员信息: " + fh);
}
}
},
onerror: err => {
console.log("请求javbus页面失败: ", err);
GM_notification(getDetails(fh, "请求javbus页面失败"));
showPageNotification(`请求javbus页面失败: ${fh}`, 'error', 3000);
if (typeof failCallback === 'function') {
failCallback();
}
}
});
}
/**
* 查找或创建文件夹,然后移动文件
* @param fid 文件id
* @param actorName 演员名称
* @param successCallback 成功回调
* @param failCallback 失败回调
*/
function findOrCreateFolderAndMove(fid, actorName, successCallback, failCallback) {
console.log("开始查找或创建文件夹: " + actorName);
// 使用保存的归档根目录ID,如果未设置则使用根目录
let cid = archiveRootCid || ROOT_DIR_CID;
let dirName = archiveRootName || "根目录";
console.log("使用归档目录: " + dirName + " (CID: " + cid + ")");
// 清理演员名称,确保不会因为特殊字符导致查询问题
actorName = stringStandard(actorName);
console.log("处理后的演员名称: " + actorName);
// 首先检查目标文件夹下是否直接存在同名文件夹(列出所有子文件夹)
$.get("https://webapi.115.com/files", {
aid: 1,
cid: cid,
limit: 1000, // 获取足够多的文件/文件夹
offset: 0,
show_dir: 1, // 只显示文件夹
format: "json"
}, function(listData) {
let listResult = typeof listData === 'string' ? JSON.parse(listData) : listData;
console.log("直接获取目录下的所有文件夹: ", listResult);
let folderFound = false;
let targetCid = null;
// 检查是否有匹配的文件夹
if (listResult.state && listResult.data && listResult.data.length > 0) {
console.log("发现 " + listResult.data.length + " 个文件/文件夹");
// 遍历所有项目,查找匹配的文件夹
listResult.data.forEach(function(item) {
// 仅检查文件夹类型 - 115API中文件夹信息在'n'字段,不是'name'
// 文件夹类型可能通过'is_dir'或'm'为0或者存在'cid'而没有'fid'来判断
let isFolder = item.is_dir || (item.m === 0 && item.cid && !item.fid);
let folderName = item.n || item.name;
console.log("检查列表项: ", folderName, ", 是否文件夹: ", isFolder,
", 演员名称: ", actorName, ", 是否匹配: ", folderName === actorName);
if (isFolder && folderName === actorName) {
folderFound = true;
targetCid = item.cid;
console.log("找到完全匹配的文件夹(直接列表): " + folderName + ", ID: " + item.cid);
return false; // 跳出循环
}
});
}
if (folderFound && targetCid) {
// 文件夹存在,直接移动文件
console.log("使用现有文件夹: " + actorName + ", ID: " + targetCid);
moveFileToFolder(fid, targetCid, actorName, successCallback, failCallback);
} else {
// 如果直接列表没找到,再尝试搜索
console.log("直接列表未找到,尝试使用搜索API查找: " + actorName);
$.get("https://webapi.115.com/files/search", {
search_value: actorName,
format: "json",
aid: "1", // 搜索范围为115网盘
cid: cid, // 使用固定目录
file_type: "0", // 只搜索文件夹
limit: 1000
}, function(data) {
let result = typeof data === 'string' ? JSON.parse(data) : data;
console.log("搜索文件夹结果: ", result);
let searchFolderFound = false;
let searchTargetCid = null;
// 检查搜索结果
if (result.state && result.data && result.data.count > 0) {
console.log("找到 " + result.data.count + " 个可能的文件夹");
// 遍历搜索结果,查找完全匹配的文件夹
result.data.list.forEach(function(item) {
console.log("检查文件夹: " + item.name + ", 类型: " + item.file_type + ", 父目录: " + item.cid);
if (item.name === actorName && item.file_type === "0") {
searchFolderFound = true;
searchTargetCid = item.cid;
console.log("找到完全匹配的文件夹(搜索): " + item.name + ", ID: " + item.cid);
return false; // 跳出循环
}
});
}
if (searchFolderFound && searchTargetCid) {
// 搜索找到了文件夹,使用它
console.log("搜索发现现有文件夹: " + actorName + ", ID: " + searchTargetCid);
moveFileToFolder(fid, searchTargetCid, actorName, successCallback, failCallback);
} else {
// 最后确认,再次检查目标文件夹是否存在
console.log("最终确认,检查文件夹是否存在: " + actorName);
$.get("https://webapi.115.com/files", {
aid: 1,
cid: cid,
limit: 1000,
offset: 0,
show_dir: 1,
format: "json"
}, function(finalCheckData) {
let finalCheck = typeof finalCheckData === 'string' ? JSON.parse(finalCheckData) : finalCheckData;
let finalFolderFound = false;
let finalTargetCid = null;
// 最后一次检查文件夹是否存在
if (finalCheck.state && finalCheck.data && finalCheck.data.length > 0) {
finalCheck.data.forEach(function(item) {
// 同样更新此处的文件夹检测逻辑
let isFolder = item.is_dir || (item.m === 0 && item.cid && !item.fid);
let folderName = item.n || item.name;
if (isFolder && folderName === actorName) {
finalFolderFound = true;
finalTargetCid = item.cid;
console.log("最终确认找到文件夹: " + folderName + ", ID: " + item.cid);
return false;
}
});
}
if (finalFolderFound && finalTargetCid) {
console.log("最终确认发现文件夹,使用它: " + actorName);
moveFileToFolder(fid, finalTargetCid, actorName, successCallback, failCallback);
} else {
// 文件夹确实不存在,创建新文件夹
console.log("多次确认后,确定需要创建新文件夹: " + actorName);
$.post("https://webapi.115.com/files/add", {
pid: cid,
cname: actorName
}, function(createData) {
let createResult = typeof createData === 'string' ? JSON.parse(createData) : createData;
console.log("创建文件夹结果: ", createResult);
if (createResult.state) {
// 获取新创建的文件夹cid
let newFolderCid = createResult.cid;
console.log("新文件夹创建成功,ID: " + newFolderCid);
moveFileToFolder(fid, newFolderCid, actorName, successCallback, failCallback);
} else {
console.log("创建文件夹失败,响应码: " + createResult.errno + ", 错误: " + createResult.error);
if (createResult.errno === 20004) {
// 如果是"文件夹已存在"错误,尝试再次获取文件夹列表
console.log("文件夹已存在错误,尝试最后一次查找");
// 短暂延迟后再次尝试
setTimeout(function() {
$.get("https://webapi.115.com/files", {
aid: 1,
cid: cid,
limit: 1000,
offset: 0,
show_dir: 1,
format: "json"
}, function(lastData) {
let lastCheck = typeof lastData === 'string' ? JSON.parse(lastData) : lastData;
let foundFolder = null;
if (lastCheck.state && lastCheck.data && lastCheck.data.length > 0) {
lastCheck.data.forEach(function(item) {
// 同样更新此处的文件夹检测逻辑
let isFolder = item.is_dir || (item.m === 0 && item.cid && !item.fid);
let folderName = item.n || item.name;
if (isFolder && folderName === actorName) {
foundFolder = item;
return false;
}
});
}
if (foundFolder) {
console.log("最后尝试成功找到文件夹: " + foundFolder.name + ", ID: " + foundFolder.cid);
moveFileToFolder(fid, foundFolder.cid, actorName, successCallback, failCallback);
} else {
GM_notification(getDetails(actorName, "无法找到或创建文件夹"));
console.log("所有尝试均失败,无法找到或创建文件夹: " + actorName);
}
});
}, 1000);
} else {
GM_notification(getDetails(actorName, "创建文件夹失败"));
console.log("创建文件夹失败:", createResult);
}
}
}).fail(function(xhr) {
console.log("创建文件夹请求失败: ", xhr.status, xhr.statusText);
GM_notification(getDetails(actorName, "创建文件夹请求失败"));
});
}
}).fail(function(err) {
console.log("最终确认请求失败: ", err);
GM_notification(getDetails(actorName, "最终确认请求失败"));
});
}
}).fail(function(err) {
console.log("搜索文件夹请求失败: ", err);
GM_notification(getDetails(actorName, "搜索文件夹失败"));
});
}
}).fail(function(err) {
console.log("列出文件夹请求失败: ", err);
GM_notification(getDetails(actorName, "列出文件夹失败"));
});
}
/**
* 移动文件到指定文件夹
* @param fid 文件id
* @param targetCid 目标文件夹id
* @param actorName 演员名称(用于通知)
* @param successCallback 成功回调
* @param failCallback 失败回调
*/
function moveFileToFolder(fid, targetCid, actorName, successCallback, failCallback) {
console.log("开始移动文件: " + fid + " 到文件夹: " + actorName + " (" + targetCid + ")");
$.post("https://webapi.115.com/files/move", {
pid: targetCid,
fid: fid
}, function(data) {
let result = typeof data === 'string' ? JSON.parse(data) : data;
console.log("移动文件结果: ", result);
if (result.state) {
GM_notification(getDetails(actorName, "归档成功"));
showPageNotification(`文件成功归档到 ${actorName}`, 'success', 2000);
console.log("文件已成功移动到: " + actorName);
if (typeof successCallback === 'function') {
successCallback();
}
} else {
GM_notification(getDetails(actorName, "归档失败"));
showPageNotification(`归档到 ${actorName} 失败: ${result.error || '未知错误'}`, 'error', 3000);
console.log("移动文件失败:", result);
if (typeof failCallback === 'function') {
failCallback();
}
}
}).fail(function(err) {
console.log("移动文件请求失败: ", err);
GM_notification(getDetails(actorName, "移动文件失败"));
showPageNotification(`移动文件请求失败: ${err.statusText || '网络错误'}`, 'error', 3000);
if (typeof failCallback === 'function') {
failCallback();
}
});
}
})();