Greasy Fork

来自缓存

Greasy Fork is available in English.

BaiduPanFileList

统计百度盘文件(夹)数量大小

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name       BaiduPanFileList
// @namespace  http://greasyfork.icu/zh-CN/scripts/5128-baidupanfilelist
// @version    2.0.016
// @description  统计百度盘文件(夹)数量大小
// @match	https://pan.baidu.com*
// @include	https://pan.baidu.com*
// @grant GM_xmlhttpRequest
// @grant GM_setClipboard
// @run-at document-end
// @copyright  2014+, [email protected]
// ==/UserScript==

// %Path% = 文件路径
// %FileName% = 文件名
// %Tab% = Tab键
// %FileSize% = 可读文件大小(带单位保留两位小数,如:6.18 MiB)
// %FileSizeInBytes% = 文件大小字节数(为一个非负整数)
(function () {
    'use strict';

    // 配置指定前缀和后缀数量统计
    const PREFIX_TO_COUNT = ['', ''];
    const SUFFIX_TO_COUNT = ['', ''];

    const RANDOM_BUTTON_COLOR = true;

    const FILE_LIST_PATTERN = "%Path%%Tab%%FileSize%(%FileSizeInBytes% Bytes)";

    const BUTTON_BACKGROUND_COLOR = [
        '#007BFF', '#0ABAB5', '#50C878',
        '#FF7F50', '#D4A017', '#7B1FA2',
        '#FF69B4', '#228B22', '#948DD6',
        '#FF8C00', '#C71585', '#EF4444'
    ];

    const BTN_WAITING_TEXT = "统计文件夹";
    const BTN_RUNNING_TEXT = "处理中";

    // 预过滤有效的前缀和后缀,避免重复计算,并转为小写
    const VALID_PREFIXES = PREFIX_TO_COUNT.filter(prefix => prefix && prefix.trim().length > 0).map(prefix => prefix.toLowerCase());
    const VALID_SUFFIXES = SUFFIX_TO_COUNT.filter(suffix => suffix && suffix.trim().length > 0).map(suffix => suffix.toLowerCase());

    // 按钮颜色 - 从预设颜色中随机选择
    const buttonColorHex = RANDOM_BUTTON_COLOR ? BUTTON_BACKGROUND_COLOR[Math.floor(Math.random() * BUTTON_BACKGROUND_COLOR.length)] : BUTTON_BACKGROUND_COLOR[0];

    // 将十六进制颜色转换为RGB
    function hexToRgb(hex) {
        const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
        return result ? {
            r: parseInt(result[1], 16),
            g: parseInt(result[2], 16),
            b: parseInt(result[3], 16)
        } : null;
    }

    const buttonColorRgb = hexToRgb(buttonColorHex);
    const buttonColorRgba = `rgba(${buttonColorRgb.r}, ${buttonColorRgb.g}, ${buttonColorRgb.b}, 0.4)`;
    const buttonColorRgbaHover = `rgba(${buttonColorRgb.r}, ${buttonColorRgb.g}, ${buttonColorRgb.b}, 0.6)`;

    // 检查是否已存在按钮,避免重复创建
    if (document.getElementById('baidupanfilelist-5128-floating-action-button')) {
        return;
    }

    // 检查是否在顶级窗口中,如果不是则退出(避免在iframe中重复创建)
    if (window !== window.top) {
        return;
    }

    // 创建按钮元素
    const button = document.createElement('div');
    button.id = 'baidupanfilelist-5128-floating-action-button';
    button.innerHTML = BTN_WAITING_TEXT;

    // 创建提示框
    const tooltip = document.createElement('div');
    tooltip.id = 'floating-button-tooltip';
    tooltip.innerHTML = '📁 点击统计当前文件夹<br/>🔍 Ctrl+点击 统计包含子文件夹<br/>⌨️ 快捷键:Q / Ctrl+Q';

    // 按钮样式
    const buttonStyles = {
        position: 'fixed',
        right: '20px',
        top: '50%',
        transform: 'translateY(-50%)',
        width: 'auto',
        minWidth: '80px',
        height: '36px',
        borderRadius: '18px',
        backgroundColor: buttonColorHex,
        color: 'white',
        border: 'none',
        cursor: 'pointer',
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
        padding: '0 12px',
        fontSize: '12px',
        fontWeight: 'bold',
        boxShadow: `0 4px 12px ${buttonColorRgba}`,
        zIndex: '10000',
        transition: 'background-color 0.2s ease, box-shadow 0.2s ease',
        userSelect: 'none',
        WebkitUserSelect: 'none',
        MozUserSelect: 'none',
        msUserSelect: 'none'
    };

    // 提示框样式
    const tooltipStyles = {
        position: 'fixed',
        backgroundColor: 'rgba(0, 0, 0, 0.8)',
        color: 'white',
        padding: '8px 12px',
        borderRadius: '6px',
        fontSize: '12px',
        lineHeight: '1.4',
        whiteSpace: 'nowrap',
        zIndex: '10001',
        opacity: '0',
        visibility: 'hidden',
        transition: 'all 0.3s ease',
        pointerEvents: 'none',
        transform: 'translateY(-50%)'
    };

    // 应用样式
    Object.assign(button.style, buttonStyles);
    Object.assign(tooltip.style, tooltipStyles);

    // 按钮状态
    let isProcessing = false;
    // 拖拽相关变量
    let isDragging = false;
    let hasMoved = false;
    let dragStartX, dragStartY;
    let buttonStartX, buttonStartY;
    let dragThreshold = 3; // 降低拖拽阈值,提高响应速度

    // 鼠标按下事件
    button.addEventListener('mousedown', function (e) {
        if (isProcessing) return; // 处理中不允许拖拽

        isDragging = true;
        hasMoved = false;
        dragStartX = e.clientX;
        dragStartY = e.clientY;

        const rect = button.getBoundingClientRect();
        buttonStartX = rect.left;
        buttonStartY = rect.top;

        button.style.cursor = 'grabbing';

        // 拖拽开始时隐藏提示框
        hideTooltip();

        e.preventDefault();
    });

    // 鼠标移动事件 - 优化为更流畅的拖拽
    document.addEventListener('mousemove', function (e) {
        if (!isDragging || isProcessing) return;

        const deltaX = e.clientX - dragStartX;
        const deltaY = e.clientY - dragStartY;

        // 降低拖拽阈值,提高响应速度
        if (Math.abs(deltaX) > dragThreshold || Math.abs(deltaY) > dragThreshold) {
            hasMoved = true;
        }

        const newX = buttonStartX + deltaX;
        const newY = buttonStartY + deltaY;

        // 限制按钮在视窗内
        const maxX = window.innerWidth - button.offsetWidth;
        const maxY = window.innerHeight - button.offsetHeight;

        const constrainedX = Math.max(0, Math.min(newX, maxX));
        const constrainedY = Math.max(0, Math.min(newY, maxY));

        // 使用 translate3d 进行硬件加速,提高性能
        button.style.left = constrainedX + 'px';
        button.style.top = constrainedY + 'px';
        button.style.right = 'auto';
        button.style.transform = 'translate3d(0, 0, 0)';

        e.preventDefault();
    });

    // 鼠标释放事件
    document.addEventListener('mouseup', function (e) {
        if (!isDragging) return;

        isDragging = false;
        button.style.cursor = isProcessing ? 'not-allowed' : 'pointer';

        // 如果没有移动,则触发点击事件
        if (!hasMoved && !isProcessing) {
            handleClick(e);
        }

        // 重置transform
        if (hasMoved) {
            button.style.transform = 'translate3d(0, 0, 0)';
        } else {
            button.style.transform = button.style.left ? 'translate3d(0, 0, 0)' : 'translateY(-50%)';
        }
    });

    // 点击处理函数 - 调用原有的文件统计功能
    async function handleClick(e) {
        if (isProcessing) return; // 防止重复点击

        // 检查是否按住了 Ctrl 键
        const includeSubDir = e && e.ctrlKey;

        try {
            // 调用原有的文件统计功能
            showInfo(includeSubDir);
        } catch (error) {
            alert("❌ 处理失败\n\n💡 提示:直接点击按钮重试即可,无需刷新页面");
            unlockButton();
        }
    }

    // 悬停效果
    button.addEventListener('mouseenter', function () {
        if (!isDragging && !isProcessing) {
            button.style.transform = button.style.transform.includes('translateY') ?
                'translateY(-50%) scale(1.05)' : 'scale(1.05)';

            if (!isProcessing) {
                button.style.boxShadow = `0 6px 16px ${buttonColorRgbaHover}`;
            }

            // 显示提示框
            showTooltip();
        }
    });

    button.addEventListener('mouseleave', function () {
        if (!isDragging) {
            button.style.transform = button.style.transform.includes('translateY') ?
                'translateY(-50%)' : (button.style.left ? 'translate3d(0, 0, 0)' : 'none');

            if (!isProcessing) {
                button.style.boxShadow = `0 4px 12px ${buttonColorRgba}`;
            }

            // 隐藏提示框
            hideTooltip();
        }
    });

    // 显示提示框
    function showTooltip() {
        const buttonRect = button.getBoundingClientRect();

        // 动态获取提示框实际尺寸
        tooltip.style.visibility = 'hidden';
        tooltip.style.opacity = '1';
        const tooltipRect = tooltip.getBoundingClientRect();
        const tooltipWidth = tooltipRect.width || 160; // 提供默认值
        const tooltipHeight = tooltipRect.height || 50;
        tooltip.style.opacity = '0';
        tooltip.style.visibility = 'hidden';

        // 计算按钮中心点
        const buttonCenterX = buttonRect.left + buttonRect.width / 2;
        const buttonCenterY = buttonRect.top + buttonRect.height / 2;

        // 计算屏幕中心点
        const screenCenterX = window.innerWidth / 2;
        const screenCenterY = window.innerHeight / 2;

        // 默认位置:按钮左侧
        let tooltipX = buttonRect.left - tooltipWidth - 10;
        let tooltipY = buttonCenterY - tooltipHeight / 2;

        // 判断按钮相对于屏幕中心的位置,调整提示框位置
        if (buttonCenterX > screenCenterX) {
            // 按钮在屏幕右侧,提示框显示在左侧
            tooltipX = buttonRect.left - tooltipWidth - 5;
        } else {
            // 按钮在屏幕左侧,提示框显示在右侧
            tooltipX = buttonRect.right + 5;
        }

        if (buttonCenterY > screenCenterY) {
            // 按钮在屏幕下方,提示框显示在上方
            tooltipY = buttonRect.top - tooltipHeight - 5;
        } else {
            // 按钮在屏幕上方,提示框显示在下方
            tooltipY = buttonRect.bottom + 5;
        }

        // 防止提示框超出屏幕边界
        if (tooltipX < 10) {
            tooltipX = 10;
        }
        if (tooltipX + tooltipWidth > window.innerWidth - 10) {
            tooltipX = window.innerWidth - tooltipWidth - 10;
        }
        if (tooltipY < 10) {
            tooltipY = 10;
        }
        if (tooltipY + tooltipHeight > window.innerHeight - 10) {
            tooltipY = window.innerHeight - tooltipHeight - 10;
        }

        // 应用位置
        tooltip.style.left = tooltipX + 'px';
        tooltip.style.top = tooltipY + 'px';
        tooltip.style.right = 'auto';
        tooltip.style.transform = 'none';
        tooltip.style.opacity = '1';
        tooltip.style.visibility = 'visible';
    }

    // 隐藏提示框
    function hideTooltip() {
        tooltip.style.opacity = '0';
        tooltip.style.visibility = 'hidden';
    }

    // 禁用右键菜单,防止 Ctrl+点击时弹出菜单
    button.addEventListener('contextmenu', function (e) {
        e.preventDefault();
        return false;
    });

    // 添加到页面
    document.body.appendChild(button);
    document.body.appendChild(tooltip);

    // 防止页面滚动时按钮位置错乱
    window.addEventListener('scroll', function () {
        if (!button.style.left) {
            // 如果按钮还在初始位置(右侧中间),保持fixed定位
            return;
        }
    });

    // 窗口大小改变时调整按钮位置
    window.addEventListener('resize', function () {
        const rect = button.getBoundingClientRect();
        const maxX = window.innerWidth - button.offsetWidth - 20; // 保持20px边距
        const maxY = window.innerHeight - button.offsetHeight;

        // 如果按钮被挤出右边界,调整到安全位置
        if (rect.right > window.innerWidth - 20) {
            if (button.style.left) {
                // 拖拽后的按钮
                button.style.left = Math.max(20, maxX) + 'px';
            } else {
                // 初始位置的按钮,切换到left定位
                button.style.right = 'auto';
                button.style.left = Math.max(20, maxX) + 'px';
            }
        }

        // 垂直位置保护
        if (rect.bottom > window.innerHeight) {
            button.style.top = Math.max(20, maxY) + 'px';
        }
    });

    // 键盘快捷键, 确保在按钮添加失败时依旧可用
    document.addEventListener("keydown", function (e) {
        // 检查焦点元素,避免在输入框等元素中触发
        const activeElement = document.activeElement;
        const isInputElement = activeElement && (
            activeElement.tagName === 'INPUT' ||
            activeElement.tagName === 'TEXTAREA' ||
            activeElement.contentEditable === 'true'
        );

        // 如果焦点在输入元素上,不处理快捷键
        if (isInputElement) {
            return;
        }

        // 使用标准的事件对象,无需兼容性处理
        let key = e.key || e.code;

        // 检测 Q 键 (Q 或 q)
        if (key === 'q' || key === 'Q' || key === 'KeyQ') {
            if (e.ctrlKey) {
                showInfo(true);
            } else {
                showInfo(false);
            }
            // 阻止默认行为
            e.preventDefault();
        }
    }, false);

    // 处理按钮和快捷键
    function showInfo(includeSubDir) {
        // 是否处理错误
        let isGetListHasError = false;
        if (isProcessing) {
            return;
        }
        lockButton();

        // 记录开始时间
        const startTime = Date.now();

        let strAlert = "";
        let numOfAllFiles = 0;
        let numOfAllFolder = 0;
        let prefixCounts = {};
        let suffixCounts = {};
        // 根据预过滤的配置初始化计数器
        VALID_PREFIXES.forEach(prefix => {
            prefixCounts[prefix] = 0;
        });
        VALID_SUFFIXES.forEach(suffix => {
            suffixCounts[suffix] = 0;
        });
        let allFilePath = [];
        let allFileSizeInBytes = 0;


        let currNumOfAccessFolder = 1;
        // 创建文件列表获取器
        const fileListGetter = FileListGetterFactory.createGetter(document.URL, {});
        fileListGetter.init();
        // 获取当前目录
        const currentDir = fileListGetter.getCurrentDirectory();
        processFileList(currentDir);

        // 处理文件列表
        function processFileList(filePath) {
            if (isGetListHasError) {
                return;
            }

            const callback = {
                parseResponse: function(jsonObj, url, data, callback) {
                    return fileListGetter.parseResponse(jsonObj, url, data, callback);
                },
                onSuccess: function(fileList) {
                    for (let fileInfo of fileList) {
                        if (fileInfo.isDir()) {
                            numOfAllFolder++;
                            allFilePath.push(fileInfo.getPath());
                            if (includeSubDir) {
                                currNumOfAccessFolder++;
                                processFileList(fileInfo.getPath());
                            }
                        } else {
                            numOfAllFiles++;
                            setButtonText(BTN_RUNNING_TEXT + "(" + numOfAllFiles + ")");
                            
                            // 根据SUFFIX_TO_COUNT和PREFIX_TO_COUNT配置动态计数
                            let currItemServerFilename = fileInfo.getName();
                            // 前缀统计
                            for (let prefix of VALID_PREFIXES) {
                                if (currItemServerFilename.toLowerCase().startsWith(prefix)) {
                                    prefixCounts[prefix]++;
                                    break; // 匹配到第一个前缀就停止,避免重复计数
                                }
                            }
                            // 后缀统计
                            for (let suffix of VALID_SUFFIXES) {
                                if (currItemServerFilename.toLowerCase().endsWith(suffix)) {
                                    suffixCounts[suffix]++;
                                    break; // 匹配到第一个后缀就停止,避免重复计数
                                }
                            }
                            allFileSizeInBytes += fileInfo.getSize();
                            if (typeof FILE_LIST_PATTERN === "string") {
                                allFilePath.push(FILE_LIST_PATTERN.replace("%FileName%", currItemServerFilename).replace("%Path%", fileInfo.getPath()).replace("%FileSizeInBytes%", fileInfo.getSize()).replace("%Tab%", "\t").replace("%FileSize%", getReadableFileSizeString(fileInfo.getSize())));
                            } else {
                                allFilePath.push(fileInfo.getPath() + "\t" + getReadableFileSizeString(fileInfo.getSize()) + "(" + fileInfo.getSize() + " Bytes)");
                            }
                        }
                    }
                    currNumOfAccessFolder--;
                    if (currNumOfAccessFolder === 0) {
                        const CTL = "\r\n";
                        let prefixCountsStr = "";
                        let suffixCountsStr = "";
                        // 按预过滤的顺序显示各前缀计数
                        VALID_PREFIXES.forEach(prefix => {
                            prefixCountsStr += prefix + ": " + prefixCounts[prefix] + CTL;
                        });

                        // 按预过滤的顺序显示各后缀计数
                        VALID_SUFFIXES.forEach(suffix => {
                            suffixCountsStr += suffix + ": " + suffixCounts[suffix] + CTL;
                        });
                        strAlert = currentDir + CTL + CTL + "文件夹数量: " + numOfAllFolder + ", 文件数量: " + numOfAllFiles + CTL + "大小: " + getReadableFileSizeString(allFileSizeInBytes) + "  (" + allFileSizeInBytes.toLocaleString() + " Bytes)" + CTL + prefixCountsStr + suffixCountsStr;
                        GM_setClipboard(strAlert + CTL + CTL + allFilePath.sort().join("\r\n") + "\r\n");
                        // 计算耗时
                        let durationSecondsStr = ((Date.now() - startTime) / 1000).toFixed(2);
                        window.setTimeout(() => {
                            alert("📊 统计完成" + (includeSubDir ? "(含子文件夹)" : "(仅当前文件夹)") + "!耗时 " + durationSecondsStr + " 秒\n\n" + strAlert.replace(/\r\n/g, "\n") + "\n\n✅ 详细文件列表已复制到剪贴板");
                            // 解锁悬浮按钮
                            unlockButton();
                        }, 0);
                    }
                },
                onError: function(errorMessage) {
                    showError(errorMessage);
                }
            };

            try {
                fileListGetter.getList(filePath, callback);
            } catch (error) {
                showError("🔧 文件列表获取失败\n\n💡 提示:可能是API权限问题或者返回数据格式变更,请重试\n错误详情: " + error.message);
            }
        }

        // 错误提示
        function showError(info) {
            isGetListHasError = true;
            alert(info);
            unlockButton();
        }

    }

    // 锁定按钮的方法
    function lockButton() {
        // 设置处理状态
        isProcessing = true;
        setButtonText(BTN_RUNNING_TEXT + "...");
        button.style.backgroundColor = '#6c757d';
        button.style.cursor = 'not-allowed';
        button.style.boxShadow = '0 4px 12px rgba(108, 117, 125, 0.4)';
    }

    // 解锁按钮的方法
    function unlockButton() {
        isProcessing = false;
        setButtonText(BTN_WAITING_TEXT);
        button.style.backgroundColor = buttonColorHex;
        button.style.cursor = 'pointer';
        button.style.boxShadow = `0 4px 12px ${buttonColorRgba}`;
    }

    // 解锁按钮的方法
    function setButtonText(text) {
        button.innerHTML = text;
        button.style.width = 'auto';
        void button.offsetWidth;
    }

    // 转换可读文件大小
    function getReadableFileSizeString(fileSizeInBytes) {
        let size = fileSizeInBytes; // 使用局部变量,避免修改参数
        let i = 0;
        const byteUnits = [' Bytes', ' KiB', ' MiB', ' GiB', ' TiB', ' PiB', ' EiB', ' ZiB', ' YiB'];
        while (size >= 1024) {
            size = size / 1024;
            i++;
        }
        return size.toFixed(2) + byteUnits[i];
    }


    // 文件列表获取器工厂
    class FileListGetterFactory {
        static createGetter(url, config) {
            if (url.includes("baidu.com")) {
                return new BaiduPanFileListGetter(config);
            }
            // 可以在这里添加其他云盘服务的判断
            // else if (url.includes("example.com")) {
            //     return new ExampleFileListGetter();
            // }
            throw new Error("不支持的URL: " + url);
        }
    }

    // 文件信息类
    class FileInfo {
        constructor(name, dir, path, size) {
            this.name = name;
            this.dir = dir;
            this.path = path;
            this.size = size;
        }

        getName() {
            return this.name;
        }

        isDir() {
            return this.dir;
        }

        getPath() {
            return this.path;
        }

        getSize() {
            return this.size;
        }
    }

    // 抽象文件列表获取器
    class AbstractFileListGetter {

        static METHOD_GET = 'GET';
        static METHOD_POST = 'POST';

        constructor(config = {}) {
            this.config = config;
        }

        // 获取当前目录
        getCurrentDirectory() {
            return "/";
        }

        init(){

        }

        // 发送HTTP请求的通用方法
        async sendRootRequest(url, method, data, callback) {
            GM_xmlhttpRequest({
                method: method,
                synchronous: false,
                url: url,
                data: data,
                timeout: 9999,
                onabort: function () {
                    callback.onError("⚠️ 网络请求被中断\n\n💡 提示:直接点击按钮重试即可");
                },
                onerror: function () {
                    callback.onError("❌ 网络请求失败\n\n💡 提示:请检查网络连接后重试");
                },
                ontimeout: function () {
                    callback.onError("⏰ 请求超时\n\n💡 提示:网络较慢,请稍后重试");
                },
                onload: async function (reText) {
                    let JSONObj = {};
                    try {
                        JSONObj = JSON.parse(reText.responseText);
                        // 调用子类的数据解析方法
                        const fileList = await callback.parseResponse(JSONObj, url, data, callback);
                        callback.onSuccess(fileList);
                    } catch (parseError) {
                        callback.onError("📄 数据解析失败\n\n错误详情: " + parseError.message);
                    }
                }
            });
        }

        // 同步发送请求的方法
        sendPageRequest(url, method, data) {
            return new Promise((resolve, reject) => {
                GM_xmlhttpRequest({
                    method: method,
                    synchronous: false,
                    url: url,
                    data: data,
                    timeout: 9999,
                    onabort: function () {
                        reject(new Error("⚠️ 分页网络请求被中断"));
                    },
                    onerror: function () {
                        reject(new Error("❌ 分页网络请求失败"));
                    },
                    ontimeout: function () {
                        reject(new Error("⏰ 分页请求超时"));
                    },
                    onload: function (reText) {
                        try {
                            const JSONObj = JSON.parse(reText.responseText);
                            resolve(JSONObj);
                        } catch (parseError) {
                            reject(new Error("📄 分页数据解析失败: " + parseError.message));
                        }
                    }
                });
            });
        }


        // 抽象方法,子类必须实现
        getList(filePath, callback) {
            throw new Error("getList方法必须在子类中实现");
        }

        // 抽象方法,子类必须实现数据解析逻辑
        async parseResponse(jsonObj, url, data, callback) {
            throw new Error("parseResponse方法必须在子类中实现");
        }
    }

    // 百度网盘文件列表获取器实现
    class BaiduPanFileListGetter extends AbstractFileListGetter {

        static PAGE_SIZE = 1000;

        static BASE_URL_API = `https://pan.baidu.com/api/list?channel=chunlei&clienttype=0&web=1&num=${BaiduPanFileListGetter.PAGE_SIZE}&page=1&dir=`;

        constructor(config) {
            super(config);
        }

        getCurrentDirectory() {
            let url = document.URL;
            while (url.includes("%25")) {
                url = url.replace("%25", "%");
            }

            if (!url.includes("path=")) {
                return "/";
            } else if (url.includes("path=")) {
                let path = url.substring(url.indexOf("path=") + 5);
                if (path.includes("&")) {
                    path = path.substring(0, path.indexOf("&"));
                }
                return decodeURIComponent(path);
            }
        }

        getList(filePath, callback) {
            const url = BaiduPanFileListGetter.BASE_URL_API + encodeURIComponent(filePath);
            this.sendRootRequest(url, AbstractFileListGetter.METHOD_GET, null, callback);
        }

        // 实现数据解析逻辑
        async parseResponse(jsonObj, url, data, callback) {
            const allFileList = [];
            let currentUrl = url;
            
            // 使用while循环处理分页
            while (true) {
                if (jsonObj.errno !== 0) {
                    throw new Error("API响应错误,错误码: " + jsonObj.errno + "。可能是权限问题");
                }
                const fileList = [];
                const size_list = jsonObj.list.length;
                let curr_item = null;

                // 解析当前页数据
                for (let i = 0; i < size_list; i++) {
                    curr_item = jsonObj.list[i];
                    const fileInfo = new FileInfo(
                        curr_item.server_filename,
                        curr_item.isdir === 1,
                        curr_item.path,
                        curr_item.size
                    );
                    fileList.push(fileInfo);
                }
                
                // 将当前页数据添加到总列表中
                allFileList.push(...fileList);
                
                // 如果当前页文件数量小于页面大小,说明没有更多页了
                if (fileList.length < BaiduPanFileListGetter.PAGE_SIZE) {
                    break;
                }
                
                // 获取下一页URL
                let currentPage = 1;
                const numMatch = currentUrl.match(/&page=\d+/);
                if (numMatch) {
                    currentPage = parseInt(numMatch[0].replace('&page=', ''));
                }
                let nextPage = currentPage + 1;
                currentUrl = currentUrl.replace(/&page=\d+/, '&page=' + nextPage);
                
                // 请求下一页数据
                jsonObj = await this.sendPageRequest(currentUrl, AbstractFileListGetter.METHOD_GET, data);
            }
            
            return allFileList;
        }
    }

})();