您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
115网盘视频整理工具:1.根据番号查询并重命名文件 2.支持javbus/avmoo查询 3.演员归档自动分类 4.可设置归档根目录 5.支持中文字幕和无码标记
当前为
// ==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(); } }); } })();