115Rename2025

115网盘视频整理工具:1.根据番号查询并重命名文件 2.支持javbus/avmoo查询 3.演员归档自动分类 4.可设置归档根目录 5.支持中文字幕和无码标记 6.支持文件夹归档 7.增强用户体验的通知提示 8.性能优化

اعتبارا من 09-04-2025. شاهد أحدث إصدار.

// ==UserScript==
// @name            115Rename2025
// @namespace           http://tampermonkey.net/
// @version             1.2
// @description         115网盘视频整理工具:1.根据番号查询并重命名文件 2.支持javbus/avmoo查询 3.演员归档自动分类 4.可设置归档根目录 5.支持中文字幕和无码标记 6.支持文件夹归档 7.增强用户体验的通知提示 8.性能优化
// @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 rootInfoId = 'archive-root-info-' + Date.now();
    
    // 在执行任何操作前,先清除可能存在的所有归档根目录信息元素
    function cleanupExistingRootInfo() {
        try {
            // 清除主文档中的元素
            const mainDocElements = document.querySelectorAll('[id^="archive-root-info"]');
            if (mainDocElements.length > 0) {
                console.log(`清理主文档中发现的${mainDocElements.length}个归档根目录信息元素`);
                mainDocElements.forEach(element => element.remove());
            }
            
            // 尝试清除所有iframe中的元素
            const iframes = document.querySelectorAll('iframe');
            iframes.forEach(iframe => {
                try {
                    if (iframe.contentDocument) {
                        const iframeElements = iframe.contentDocument.querySelectorAll('[id^="archive-root-info"]');
                        if (iframeElements.length > 0) {
                            console.log(`清理iframe中发现的${iframeElements.length}个归档根目录信息元素`);
                            iframeElements.forEach(element => element.remove());
                        }
                    }
                } catch (e) {
                    // 跨域iframe可能会抛出异常,忽略
                    console.log("无法访问iframe内容,可能是跨域限制");
                }
            });
        } catch (e) {
            console.error("清理归档根目录信息元素时出错:", e);
        }
    }
    
    // 立即执行清理
    cleanupExistingRootInfo();
    
    // 添加样式,使用更明确的选择器
    const notificationStyle = `
    <style>
        /* 归档根目录信息样式 */
        [id^="archive-root-info"] {
            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: 9998;
            font-size: 14px;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
            border-left: 4px solid #1890ff;
        }
        
        /* 临时通知样式 */
        .custom-notification {
            position: fixed;
            top: 80px; /* 位于根目录通知下方 */
            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);
    
    // 默认使用根目录ID
    const ROOT_DIR_CID = "0"; // 115网盘根目录的ID为"0"
    // 尝试从存储中获取根目录ID,如果不存在则为null(表示未设置)
    let archiveRootCid = GM_getValue("archiveRootCid", null);
    let archiveRootName = GM_getValue("archiveRootName", null);
    
    // 简单的页面内通知函数
    window.showPageNotification = function(message, type = 'info', duration = 3000) {
        // 为不同类型通知设置不同的默认持续时间
        if (duration === 3000) {
            if (type === 'success') duration = 3000;      // 成功通知更短
            else if (type === 'error') duration = 5000;   // 错误通知更长
            else if (type === 'info') duration = 3000;    // 信息通知标准
        }
        
        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);
    };
    
    // 简单函数:显示归档根目录信息
    function showArchiveRootInfo() {
        // 确保清理可能存在的其他实例
        cleanupExistingRootInfo();
        
        // 根据是否设置了根目录,显示不同的信息
        let rootDirMessage;
        if (archiveRootCid && archiveRootName) {
            rootDirMessage = `当前归档根目录: "${archiveRootName}"`;
        } else {
            rootDirMessage = "当前无归档根目录,将使用115网盘根目录";
        }
        
        // 创建信息元素,使用带时间戳的ID确保唯一性
        const infoElement = $(`<div id="${rootInfoId}" class="archive-root-info">${rootDirMessage}</div>`);
        
        // 确保只添加到主文档,不添加到iframe
        if (window.self === window.top) {
            $('body').append(infoElement);
            console.log("在主文档中显示归档根目录信息: " + rootDirMessage);
        }
    }
    
    // 设置延迟计时器
    let rootInfoTimer = null;
    
    // 只在主窗口(非iframe)中显示一次
    function initializeRootInfo() {
        // 只在主窗口中初始化,避免在iframe中创建
        if (window.self !== window.top) {
            console.log("处于iframe中,跳过显示归档根目录信息");
            return;
        }
        
        // 清除可能存在的计时器
        if (rootInfoTimer) {
            clearTimeout(rootInfoTimer);
        }
        
        // 设置新的计时器
        rootInfoTimer = setTimeout(function() {
            showArchiveRootInfo();
            rootInfoTimer = null;  // 清除引用
        }, 2000);
    }
    
    // 在页面完全加载后显示
    $(window).on('load', function() {
        initializeRootInfo();
    });
    
    // 如果window.load事件已触发,直接初始化
    if (document.readyState === 'complete') {
        initializeRootInfo();
    }
    
    // 不再在document.ready时创建,避免重复
    
    // 按钮
    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);

    // 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网盘根目录");
            }
            
            // 按钮添加时不再创建根目录信息,依赖页面加载时的创建
            console.log("按钮已添加,根目录信息状态: " + (window.rootInfoDisplayed ? "已显示" : "未显示"));
            
            // 结束定时任务
            clearInterval(interval);
        }
    }

    /**
     * 设置归档根目录
     * 获取选中的文件夹信息并保存为归档根目录
     */
    function setArchiveRoot() {
        // 获取选中的文件夹
        let selectedFolder = $("iframe[rel='wangpan']")
            .contents()
            .find("li.selected");
        
        // 检查是否只选择了一个文件夹
        if (selectedFolder.length !== 1) {
            GM_notification(getDetails("请只选择一个文件夹", "设置失败"));
            showPageNotification("请只选择一个文件夹", 'error', 3000);
            console.log("设置归档根目录失败:选择了 " + selectedFolder.length + " 个项目,请只选择一个文件夹");
            return;
        }
        
        let $item = $(selectedFolder[0]);
        // 检查是否是文件夹
        let file_type = $item.attr("file_type");
        if (file_type !== "0") {
            GM_notification(getDetails("请选择文件夹类型", "设置失败"));
            showPageNotification("请选择文件夹类型", 'error', 3000);
            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;
            
            // 更新归档根目录显示
            cleanupExistingRootInfo();
            showArchiveRootInfo();
            
            GM_notification(getDetails(name, "归档根目录设置成功"));
            showPageNotification(`归档根目录设置成功: "${name}"`, 'success', 5000);
            console.log("归档根目录设置成功: " + name + " (CID: " + cid + ")");
        } else {
            GM_notification(getDetails("无法获取文件夹ID", "设置失败"));
            showPageNotification("无法获取文件夹ID", 'error', 3000);
            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");
                
                // 根据类型获取正确的ID
                let fid;
                if (file_type === "0") {
                    // 文件夹
                    fid = $item.attr("cate_id");
                    console.log("处理文件夹: " + file_name + ", ID: " + fid);
                    
                    // 从文件夹名称中提取番号
                    let fh = getVideoCode(file_name);
                    if (fh) {
                        console.log("从文件夹名称中提取到番号: " + fh);
                        // 使用与文件相同的逻辑处理文件夹
                        requestJavbusForActor(fid, fh, function() {
                            processedCount++;
                            successCount++;
                            checkAllCompleted();
                        }, function() {
                            processedCount++;
                            checkAllCompleted();
                        });
                    } else {
                        console.log("无法从文件夹名称中提取番号: " + file_name);
                        showPageNotification(`无法从"${file_name}"提取番号`, 'error', 3000);
                        processedCount++;
                        checkAllCompleted();
                    }
                } else {
                    // 文件
                    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 {
                            console.log("无法从文件名称中提取番号: " + file_name);
                            showPageNotification(`无法从"${file_name}"提取番号`, 'error', 3000);
                            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("直接获取目录下的所有文件夹,开始查找匹配项");
            
            let folderFound = false;
            let targetCid = null;
            
            // 检查是否有匹配的文件夹
            if (listResult.state && listResult.data && listResult.data.length > 0) {
                console.log("发现 " + listResult.data.length + " 个文件/文件夹");
                
                // 遍历所有项目,查找匹配的文件夹 (使用some而不是forEach,可以在找到匹配项后立即中断)
                listResult.data.some(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;
                    
                    if (isFolder && folderName === actorName) {
                        folderFound = true;
                        targetCid = item.cid;
                        console.log("找到完全匹配的文件夹(直接列表): " + folderName + ", ID: " + item.cid);
                        return true; // 找到匹配项,中断循环
                    }
                    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("搜索文件夹结果,开始查找匹配项");
                    
                    let searchFolderFound = false;
                    let searchTargetCid = null;
                    
                    // 检查搜索结果
                    if (result.state && result.data && result.data.count > 0) {
                        console.log("找到 " + result.data.count + " 个可能的文件夹");
                        // 遍历搜索结果,查找完全匹配的文件夹 (使用some而不是forEach)
                        result.data.list.some(function(item) {
                            if (item.name === actorName && item.file_type === "0") {
                                searchFolderFound = true;
                                searchTargetCid = item.cid;
                                console.log("找到完全匹配的文件夹(搜索): " + item.name + ", ID: " + item.cid);
                                return true; // 找到匹配项,中断循环
                            }
                            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) {
                                // 使用some代替forEach
                                finalCheck.data.some(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 true; // 找到匹配项,中断循环
                                    }
                                    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) {
                                                        // 使用some代替forEach
                                                        lastCheck.data.some(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 true; // 找到匹配项,中断循环
                                                            }
                                                            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();
            }
        });
    }
})();