115Rename2025

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

Verze ze dne 09. 04. 2025. Zobrazit nejnovější verzi.

K instalaci tototo skriptu si budete muset nainstalovat rozšíření jako Tampermonkey, Greasemonkey nebo Violentmonkey.

You will need to install an extension such as Tampermonkey to install this script.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Violentmonkey.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Userscripts.

You will need to install an extension such as Tampermonkey to install this script.

K instalaci tohoto skriptu si budete muset nainstalovat manažer uživatelských skriptů.

(Už mám manažer uživatelských skriptů, nechte mě ho nainstalovat!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Už mám manažer uživatelských stylů, nechte mě ho nainstalovat!)

// ==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();
            }
        });
    }
})();