Bilibili评论区图片批量下载

批量下载B站评论区中的图片(暂仅支持动态和视频评论区)

// ==UserScript==
// @name         Bilibili评论区图片批量下载
// @namespace    BilibiliCommentImageDownloader
// @version      0.6.1
// @description  批量下载B站评论区中的图片(暂仅支持动态和视频评论区)
// @author       Kaesinol
// @license      MIT
// @match        https://t.bilibili.com/*
// @match        https://*.bilibili.com/opus/*
// @match        https://www.bilibili.com/video/*
// @grant        GM_download
// @grant        GM_xmlhttpRequest
// @grant        GM_registerMenuCommand
// @grant        GM_getValue
// @grant        GM_setValue
// @run-at       document-start
// @grant        unsafeWindow
// ==/UserScript==

(function () {
    'use strict';

    // 确保在脚本加载时就创建全局变量
    unsafeWindow.lastBiliReply = null;

    // 保存原始fetch方法
    const originalFetch = unsafeWindow.fetch;
    // 目标API路径配置
    const API_TYPES = {
        TRADITIONAL: '/x/v2/reply',
        WBI: '/x/v2/reply/wbi/main'
    };

    // 获取当前使用的API类型
    function getCurrentApiType() {
        return GM_getValue('biliApiType', 'WBI'); // 默认使用WBI接口
    }
    // 设置API类型
    function setApiType(type) {
        GM_setValue('biliApiType', type);
        console.log(`[API配置] 已切换到 ${type === 'WBI' ? 'WBI签名' : '传统'} 接口`);
    }

    // 获取目标API路径
    function getTargetApiPath() {
        const apiType = getCurrentApiType();
        return API_TYPES[apiType];
    }

    // 获取当前动态ID的函数
    function getCurrentDynamicId() {
        // 从URL获取动态ID
        const urlMatch = window.location.href.match(/\/opus\/(\d+)/);
        if (urlMatch) {
            return urlMatch[1];
        }

        // 从bili-comments元素获取oid
        const commentEl = document.querySelector("bili-comments[data-params]");
        if (commentEl) {
            const params = commentEl.getAttribute("data-params");
            const oidMatch = params && params.match(/\d{4,}/);
            return oidMatch ? oidMatch[0] : null;
        }

        return 'default';
    }

    // 去重函数,基于rpid去重
    function deduplicateReplies(replies) {
        const seen = new Set();
        return replies.filter(reply => {
            if (seen.has(reply.rpid)) {
                return false;
            }
            seen.add(reply.rpid);
            return true;
        });
    }

    // 重写fetch方法
    unsafeWindow.fetch = function (...args) {
        if (getCurrentApiType() == 'TRADITIONAL') {
            // 如果是传统接口,直接调用原始fetch
            return originalFetch.apply(this, args);
        }

        const requestUrl = args[0]?.url || args[0];
        const targetApiPath = getTargetApiPath();

        // 检查是否是目标API
        if (typeof requestUrl === 'string' && requestUrl.includes(targetApiPath)) {
            console.log(`[B站API监控] 捕获评论API请求 (${getCurrentApiType()}):`, requestUrl);
            // 执行原始fetch
            return originalFetch.apply(this, args)
                .then(response => {
                    // 克隆响应以便读取
                    const clone = response.clone();
                    return clone.json().then(data => {
                        const dynamicId = getCurrentDynamicId();
                        let allStoredData = {};

                        try {
                            const storedDataRaw = GM_getValue('biliReplyDict', '{}');
                            allStoredData = JSON.parse(storedDataRaw);
                        } catch (e) {
                            console.warn('解析存储的评论数据失败:', e);
                            allStoredData = {};
                        }

                        // 确保当前动态ID有对应的数组
                        if (!allStoredData[dynamicId]) {
                            allStoredData[dynamicId] = [];
                        }

                        const currentReplies = Array.isArray(data?.data?.replies) ? data.data.replies : [];
                        const currentTopReplies = Array.isArray(data?.data?.top_replies) ? data.data.top_replies : [];

                        // 合并当前数据与已存储的数据
                        const existingReplies = allStoredData[dynamicId];
                        const mergedReplies = [...currentReplies, ...currentTopReplies, ...existingReplies];

                        // 去重
                        const deduplicatedReplies = deduplicateReplies(mergedReplies);

                        // 检查是否有新数据
                        const hasNewData = deduplicatedReplies.length > existingReplies.length;

                        // 更新存储
                        allStoredData[dynamicId] = deduplicatedReplies;
                        GM_setValue('biliReplyDict', JSON.stringify(allStoredData));

                        // 修改返回的data,用于当前页面显示
                        const topRepliesCount = currentTopReplies.length;
                        data.data.top_replies = deduplicatedReplies.slice(0, topRepliesCount);
                        data.data.replies = deduplicatedReplies.slice(topRepliesCount);

                        console.log(`[评论去重] 动态${dynamicId}: 总计${deduplicatedReplies.length}条评论${hasNewData ? ' (有新数据)' : ''}`);

                        // 如果有新数据且下载菜单正在显示,则实时更新界面
                        if (hasNewData) {
                            setTimeout(() => {
                                const menuContainer = document.getElementById("bili-img-download-menu");
                                if (menuContainer && menuContainer.style.display === "block") {
                                    console.log('[实时更新] 检测到新评论,更新下载界面');
                                    // 触发自定义事件通知界面更新
                                    window.dispatchEvent(new CustomEvent('biliCommentUpdate'));
                                }
                            }, 100);
                        }

                        return response;
                    });

                })
                .catch(error => {
                    console.error('[B站API监控] 请求出错:', error);
                    throw error;
                });
        }
        // 非目标API,正常执行
        return originalFetch.apply(this, args);
    };
    if (getCurrentApiType() === 'WBI') {
        console.log(`[B站API监控] 脚本已注入,开始监控评论API`);
    }
    // 暴露函数供下面的代码使用
    unsafeWindow.getCurrentApiType = getCurrentApiType;
    unsafeWindow.setApiType = setApiType;
})();

(function () {
    "use strict";

    // 当前页码
    let currentPage = 1;

    // 创建下载菜单区域
    function createDownloadMenu() {
        const menuContainer = document.createElement("div");
        menuContainer.id = "bili-img-download-menu";
        menuContainer.style.cssText = `
            position: fixed;
            top: 70px;
            right: 20px;
            width: 400px;
            max-height: 600px;
            overflow-y: auto;
            background-color: #fff;
            border: 1px solid #ccc;
            border-radius: 5px;
            padding: 10px;
            z-index: 9999;
            box-shadow: 0 0 10px rgba(0,0,0,0.2);
            display: none;
        `;

        const menuHeader = document.createElement("div");
        menuHeader.innerHTML = "<h3>评论图片下载</h3>";
        menuHeader.style.cssText = `
            margin-bottom: 10px;
            padding-bottom: 5px;
            border-bottom: 1px solid #eee;
            display: flex;
            justify-content: space-between;
        `;

        const closeButton = document.createElement("span");
        closeButton.innerHTML = "×";
        closeButton.style.cssText = `
            cursor: pointer;
            font-size: 18px;
            font-weight: bold;
        `;
        closeButton.onclick = function () {
            menuContainer.style.display = "none";
        };

        // 添加API配置按钮
        const configButton = document.createElement("span");
        configButton.innerHTML = "⚙️";
        configButton.title = "API配置";
        configButton.style.cssText = `
            cursor: pointer;
            font-size: 16px;
            margin-right: 10px;
        `;
        configButton.onclick = function () {
            showApiConfigDialog();
        };

        menuHeader.appendChild(configButton);
        menuHeader.appendChild(closeButton);
        menuContainer.appendChild(menuHeader);

        const menuContent = document.createElement("div");
        menuContent.id = "bili-img-download-content";
        menuContainer.appendChild(menuContent);

        // 添加分页控制区域
        const paginationDiv = document.createElement("div");
        paginationDiv.id = "bili-img-pagination";
        paginationDiv.style.cssText = `
            display: flex;
            justify-content: space-between;
            margin-top: 10px;
            padding-top: 10px;
            border-top: 1px solid #eee;
        `;

        const prevButton = document.createElement("button");
        prevButton.textContent = "上一页";
        prevButton.id = "bili-prev-page";
        prevButton.style.cssText = `
            padding: 5px 10px;
            background-color: #00a1d6;
            color: white;
            border: none;
            border-radius: 3px;
            cursor: pointer;
        `;
        prevButton.disabled = true;

        const pageInfo = document.createElement("span");
        pageInfo.id = "bili-page-info";
        pageInfo.textContent = "第1页";
        pageInfo.style.cssText = `
            line-height: 30px;
        `;

        const nextButton = document.createElement("button");
        nextButton.textContent = "下一页";
        nextButton.id = "bili-next-page";
        nextButton.style.cssText = `
            padding: 5px 10px;
            background-color: #00a1d6;
            color: white;
            border: none;
            border-radius: 3px;
            cursor: pointer;
        `;

        paginationDiv.appendChild(prevButton);
        paginationDiv.appendChild(pageInfo);
        paginationDiv.appendChild(nextButton);
        menuContainer.appendChild(paginationDiv);

        document.body.appendChild(menuContainer);
        return menuContainer;
    }

    // 获取当前动态ID
    function getCurrentDynamicId() {
        // 从URL获取动态ID
        const urlMatch = window.location.href.match(/\/opus\/(\d+)/);
        if (urlMatch) {
            return urlMatch[1];
        }

        // 从bili-comments元素获取oid
        const commentEl = document.querySelector("bili-comments[data-params]");
        if (commentEl) {
            const params = commentEl.getAttribute("data-params");
            const oidMatch = params && params.match(/\d{4,}/);
            return oidMatch ? oidMatch[0] : null;
        }

        return 'default';
    }

    // 获取当前使用的API类型
    function getCurrentApiType() {
        return unsafeWindow.getCurrentApiType ? unsafeWindow.getCurrentApiType() : GM_getValue('biliApiType', 'WBI');
    }

    // 设置API类型
    function setApiType(type) {
        if (unsafeWindow.setApiType) {
            unsafeWindow.setApiType(type);
        } else {
            GM_setValue('biliApiType', type);
        }
    }

    // 显示API配置对话框
    function showApiConfigDialog() {
        // 创建遮罩层
        const overlay = document.createElement("div");
        overlay.style.cssText = `
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0, 0, 0, 0.5);
            z-index: 10000;
            display: flex;
            justify-content: center;
            align-items: center;
        `;

        // 创建对话框
        const dialog = document.createElement("div");
        dialog.style.cssText = `
            background-color: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
            max-width: 400px;
            width: 90%;
        `;

        const title = document.createElement("h3");
        title.textContent = "API接口配置";
        title.style.cssText = `
            margin: 0 0 15px 0;
            color: #333;
        `;

        const description = document.createElement("p");
        description.innerHTML = `
            选择要使用的评论API接口:<br>
            <small style="color: #666;">
            • WBI签名接口:新版接口,支持实时拦截,禁用翻页<br>
            • 传统接口:旧版接口,支持翻页功能
            </small>
        `;
        description.style.cssText = `
            margin-bottom: 15px;
            line-height: 1.5;
        `;

        const currentApiType = getCurrentApiType();

        // WBI接口选项
        const wbiOption = document.createElement("label");
        wbiOption.style.cssText = `
            display: block;
            margin-bottom: 10px;
            cursor: pointer;
        `;
        wbiOption.innerHTML = `
            <input type="radio" name="apiType" value="WBI" ${currentApiType === 'WBI' ? 'checked' : ''}>
            WBI签名接口 (实时更新,无翻页)
        `;

        // 传统接口选项
        const traditionalOption = document.createElement("label");
        traditionalOption.style.cssText = `
            display: block;
            margin-bottom: 15px;
            cursor: pointer;
        `;
        traditionalOption.innerHTML = `
            <input type="radio" name="apiType" value="TRADITIONAL" ${currentApiType === 'TRADITIONAL' ? 'checked' : ''}>
            传统接口 (支持翻页)
        `;

        // 按钮容器
        const buttonContainer = document.createElement("div");
        buttonContainer.style.cssText = `
            display: flex;
            justify-content: flex-end;
            gap: 10px;
        `;

        // 取消按钮
        const cancelButton = document.createElement("button");
        cancelButton.textContent = "取消";
        cancelButton.style.cssText = `
            padding: 8px 16px;
            border: 1px solid #ccc;
            background-color: white;
            border-radius: 4px;
            cursor: pointer;
        `;
        cancelButton.onclick = function () {
            document.body.removeChild(overlay);
        };

        // 确定按钮
        const confirmButton = document.createElement("button");
        confirmButton.textContent = "确定";
        confirmButton.style.cssText = `
            padding: 8px 16px;
            border: none;
            background-color: #00a1d6;
            color: white;
            border-radius: 4px;
            cursor: pointer;
        `;
        confirmButton.onclick = function () {
            const selectedRadio = dialog.querySelector('input[name="apiType"]:checked');
            if (selectedRadio) {
                const newApiType = selectedRadio.value;
                if (newApiType !== currentApiType) {
                    setApiType(newApiType);
                    alert(`API接口已切换到${newApiType === 'WBI' ? 'WBI签名' : '传统'}接口\n请刷新页面使配置生效`);
                }
            }
            document.body.removeChild(overlay);
        };

        buttonContainer.appendChild(cancelButton);
        buttonContainer.appendChild(confirmButton);

        dialog.appendChild(title);
        dialog.appendChild(description);
        dialog.appendChild(wbiOption);
        dialog.appendChild(traditionalOption);
        dialog.appendChild(buttonContainer);

        overlay.appendChild(dialog);
        document.body.appendChild(overlay);

        // 点击遮罩层关闭对话框
        overlay.onclick = function (e) {
            if (e.target === overlay) {
                document.body.removeChild(overlay);
            }
        };
    }

    // 获取OID,统一从 #bili-comments 元素的 data-params 属性中提取
    function getOid() {
        const commentEl = document.querySelector("bili-comments[data-params]");
        if (commentEl) {
            const params = commentEl.getAttribute("data-params");
            const oidMatch = params && params.match(/\d{4,}/);
            return oidMatch ? oidMatch[0] : null;
        }
        return null;
    }

    // 从存储中获取当前动态的数据
    function getStoredData(dynamicId) {
        try {
            const allStoredData = JSON.parse(GM_getValue('biliReplyDict', '{}'));
            return allStoredData[dynamicId] || [];
        } catch (e) {
            console.warn('获取存储数据失败:', e);
            return [];
        }
    }

    // 从API获取数据
    function fetchCommentData(oid, page = 1) {
        return new Promise((resolve, reject) => {
            // 根据当前页面类型选择 type
            let initialType = 11;
            if (
                window.location.href.indexOf("https://www.bilibili.com/video/") === 0
            ) {
                initialType = 1;
            }
            const fetchWithType = (type) => {
                const apiUrl = `https://api.bilibili.com/x/v2/reply?type=${type}&oid=${oid}&pn=${page}`;
                GM_xmlhttpRequest({
                    method: "GET",
                    url: apiUrl,
                    onload: function (response) {
                        try {
                            const data = JSON.parse(response.responseText);
                            if (data && data.code === 0) {
                                resolve(data);
                            } else if (type === 11) {
                                console.warn("Type 11 failed, retrying with Type 17...");
                                fetchWithType(17);
                            } else {
                                reject("获取数据失败: " + (data.message || "未知错误"));
                            }
                        } catch (e) {
                            reject("解析数据失败: " + e.message);
                        }
                    },
                    onerror: function (error) {
                        reject("网络请求失败: " + error);
                    },
                });
            };

            fetchWithType(initialType);
        });
    }

    // 处理获取到的数据
    function processData(replies, page) {
        const processedData = [];
        for (const reply of replies) {
            if (!reply.member || !reply.content) continue;

            const pictures = reply.content.pictures || [];
            if (pictures.length === 0) continue;

            const message = reply.content.message || "";
            // 储存完整消息和截断消息
            const truncatedMessage =
                message.length > 10 ? message.substring(0, 10) + "..." : message;
            // 硬截断为20个字符
            const hardTruncatedMessage =
                message.length > 20 ? message.substring(0, 20) + "..." : message;

            const displayText = `${reply.member.uname
                } - ${truncatedMessage} - ${formatTimestamp(reply.ctime)}`;

            const imageData = pictures.map((pic, index) => {
                const originalUrl = pic.img_src;
                const fileExtension = originalUrl.split(".").pop().split("?")[0];

                // 处理biz_scene,移除opus_前缀
                let bizScene = reply.reply_control?.biz_scene || "unknown";
                bizScene = bizScene.replace("opus_", "");

                // 新的命名格式
                return {
                    url: originalUrl,
                    fileName: `${reply.member.uname} - ${reply.member.mid} - ${bizScene} - ${index}.${fileExtension}`,
                };
            });

            processedData.push({
                displayText,
                fullMessage: message,
                truncatedMessage: hardTruncatedMessage,
                username: reply.member.uname,
                timestamp: formatTimestamp(reply.ctime),
                images: imageData,
            });
        }

        return processedData;
    }

    // 格式化时间戳
    function formatTimestamp(timestamp) {
        const date = new Date(timestamp * 1000);
        return `${date.getFullYear()}-${padZero(date.getMonth() + 1)}-${padZero(
            date.getDate()
        )} ${padZero(date.getHours())}:${padZero(date.getMinutes())}`;
    }

    // 数字补零
    function padZero(num) {
        return num < 10 ? "0" + num : num;
    }

    // 更新分页控制区域的可见性
    function updatePaginationVisibility() {
        const paginationDiv = document.getElementById("bili-img-pagination");
        const apiType = getCurrentApiType();

        if (paginationDiv) {
            // WBI接口隐藏分页,传统接口显示分页
            paginationDiv.style.display = apiType === 'WBI' ? 'none' : 'flex';
        }
    }

    // 创建下载选项
    function createDownloadOptions(processedData, menuContent) {
        menuContent.innerHTML = "";

        if (processedData.length === 0) {
            menuContent.innerHTML = "<p>没有找到包含图片的评论</p>";
            return;
        }

        // 添加数据统计信息
        const statsDiv = document.createElement("div");
        statsDiv.style.cssText = `
            padding: 5px 0;
            margin-bottom: 10px;
            font-size: 12px;
            color: #666;
            border-bottom: 1px solid #eee;
        `;
        const apiType = getCurrentApiType();
        const apiTypeText = apiType === 'WBI' ? 'WBI签名接口(实时更新)' : '传统接口';
        statsDiv.textContent = `共找到 ${processedData.length} 条包含图片的评论 [${apiTypeText}]`;
        menuContent.appendChild(statsDiv);

        for (let i = 0; i < processedData.length; i++) {
            const item = processedData[i];

            const downloadOption = document.createElement("div");
            downloadOption.className = "download-option";
            downloadOption.style.cssText = `
                padding: 8px;
                margin: 5px 0;
                border: 1px solid #eee;
                border-radius: 3px;
                cursor: pointer;
                transition: background-color 0.2s;
            `;

            const downloadOptionContent = document.createElement("div");
            downloadOptionContent.style.cssText = `
                display: flex;
                justify-content: space-between;
                align-items: center;
            `;

            const infoDiv = document.createElement("div");
            infoDiv.style.cssText = `
                display: flex;
                flex-wrap: nowrap;
                align-items: center;
                overflow: hidden;
                flex: 1;
            `;

            const usernameSpan = document.createElement("span");
            usernameSpan.textContent = item.username;
            usernameSpan.style.cssText = `
                font-weight: bold;
                margin-right: 5px;
                white-space: nowrap;
            `;

            const messageSpan = document.createElement("span");
            messageSpan.textContent = item.truncatedMessage;
            messageSpan.title = item.fullMessage; // 添加tooltip显示完整消息内容
            messageSpan.style.cssText = `
                margin: 0 5px;
                white-space: nowrap;
                overflow: hidden;
                text-overflow: ellipsis;
                flex: 1;
            `;

            const timeSpan = document.createElement("span");
            timeSpan.textContent = item.timestamp;
            timeSpan.style.cssText = `
                font-size: 11px;
                color: #999;
                white-space: nowrap;
                margin-left: 5px;
            `;

            const countSpan = document.createElement("span");
            countSpan.style.cssText = `
                color: #00a1d6;
                white-space: nowrap;
                margin-left: 10px;
            `;
            countSpan.textContent = `[${item.images.length}张]`;

            infoDiv.appendChild(usernameSpan);
            infoDiv.appendChild(messageSpan);
            infoDiv.appendChild(timeSpan);

            downloadOptionContent.appendChild(infoDiv);
            downloadOptionContent.appendChild(countSpan);
            downloadOption.appendChild(downloadOptionContent);

            downloadOption.addEventListener("mouseover", function () {
                this.style.backgroundColor = "#f5f5f5";
            });

            downloadOption.addEventListener("mouseout", function () {
                this.style.backgroundColor = "transparent";
            });

            downloadOption.addEventListener("click", function () {
                downloadImages(item.images);
            });

            menuContent.appendChild(downloadOption);
        }
    }

    // 下载图片
    function downloadImages(images) {
        let downloaded = 0;

        console.log("开始下载", `准备下载 ${images.length} 张图片...`);

        for (const image of images) {
            GM_download({
                url: image.url,
                name: image.fileName,
                onload: function () {
                    downloaded++;
                    if (downloaded === images.length) {
                        console.log(`成功下载 ${downloaded} 张图片...`);
                    }
                },
                onerror: function (error) {
                    console.log(`图片 ${image.fileName} 下载失败`, error);
                },
            });
        }
    }

    // 实时刷新下载界面
    function refreshDownloadInterface() {
        const menuContainer = document.getElementById("bili-img-download-menu");
        if (!menuContainer || menuContainer.style.display !== "block") {
            return;
        }

        // 保持当前页码,重新加载数据
        loadAndDisplayDataSilent(currentPage);
    }

    // 静默加载数据(不显示加载提示)
    async function loadAndDisplayDataSilent(page = 1) {
        const menuContent = document.getElementById("bili-img-download-content");
        const pageInfo = document.getElementById("bili-page-info");
        const prevButton = document.getElementById("bili-prev-page");

        try {
            const dynamicId = getCurrentDynamicId();
            const apiType = getCurrentApiType();

            let allReplies = [];

            if (apiType === 'WBI') {
                // WBI接口:优先从存储中获取数据
                let storedReplies = getStoredData(dynamicId);
                if (storedReplies.length > 0) {
                    allReplies = storedReplies;
                }
            } else {
                // 传统接口:直接从API获取数据支持翻页
                const oid = getOid();
                if (!oid) {
                    menuContent.innerHTML = "<p>错误: 无法获取OID,请确保在正确的页面</p>";
                    return;
                }

                const data = await fetchCommentData(oid, page);
                const targetData = data.data;
                allReplies = page === 1
                    ? [...(targetData.top_replies || []), ...(targetData.replies || [])]
                    : targetData.replies || [];
            }

            const processedData = processData(allReplies, page);
            createDownloadOptions(processedData, menuContent);

            // 更新分页控制区域的可见性
            updatePaginationVisibility();

            // 更新分页信息(仅对传统接口有效)
            if (apiType === 'TRADITIONAL') {
                currentPage = page;
                pageInfo.textContent = `第${page}页`;
                prevButton.disabled = page <= 1;

                // 如果没有数据,禁用下一页按钮
                const nextButton = document.getElementById("bili-next-page");
                if (processedData.length === 0) {
                    nextButton.disabled = true;
                } else {
                    nextButton.disabled = false;
                }
            }
        } catch (error) {
            console.error("Silent refresh error:", error);
        }
    }

    // 加载数据并显示
    async function loadAndDisplayData(page = 1) {
        const menuContainer =
            document.getElementById("bili-img-download-menu") || createDownloadMenu();
        const menuContent = document.getElementById("bili-img-download-content");
        const pageInfo = document.getElementById("bili-page-info");
        const prevButton = document.getElementById("bili-prev-page");

        menuContainer.style.display = "block";
        menuContent.innerHTML = "<p>正在加载数据...</p>";

        try {
            const dynamicId = getCurrentDynamicId();
            const apiType = getCurrentApiType();
            console.log(`当前动态ID: ${dynamicId}, API类型: ${apiType}`);

            let allReplies = [];

            if (apiType === 'WBI') {
                // WBI接口:优先从存储中获取数据,不支持翻页
                let storedReplies = getStoredData(dynamicId);
                if (storedReplies.length > 0) {
                    console.log(`从存储中加载到 ${storedReplies.length} 条评论`);
                    allReplies = storedReplies;
                } else {
                    // 如果存储中没有数据,提示用户滚动页面或等待
                    menuContent.innerHTML = "<p>WBI接口模式:请先滚动评论区加载数据,或等待页面自动加载评论</p>";
                    return;
                }
            } else {
                // 传统接口:直接从API获取数据,支持翻页
                const oid = getOid();
                if (!oid) {
                    menuContent.innerHTML = "<p>错误: 无法获取OID,请确保在正确的页面</p>";
                    return;
                }

                const data = await fetchCommentData(oid, page);
                const targetData = data.data;
                allReplies = page === 1
                    ? [...(targetData.top_replies || []), ...(targetData.replies || [])]
                    : targetData.replies || [];
            }

            const processedData = processData(allReplies, page);
            createDownloadOptions(processedData, menuContent);

            // 更新分页控制区域的可见性
            updatePaginationVisibility();

            // 更新分页信息(仅对传统接口有效)
            if (apiType === 'TRADITIONAL') {
                currentPage = page;
                pageInfo.textContent = `第${page}页`;
                prevButton.disabled = page <= 1;

                // 如果没有数据,禁用下一页按钮
                const nextButton = document.getElementById("bili-next-page");
                if (processedData.length === 0) {
                    nextButton.disabled = true;
                } else {
                    nextButton.disabled = false;
                }
            }
        } catch (error) {
            menuContent.innerHTML = `<p>错误: ${error}</p>`;
            console.error("Error:", error);
        }
    }

    // 添加导航按钮
    function addNavButton() {
        if (location.href.includes('bilibili.com/video/')) {
            const root1 = document.querySelector("bili-comments")?.shadowRoot;
            const root2 = root1?.querySelector("bili-comments-header-renderer")?.shadowRoot;
            if (!root1 || !root2) {
                setTimeout(addNavButton, 2000);
                return;
            }
            if (root2) {
                const buttons = root2.querySelectorAll("bili-text-button");
                const lastButton = buttons[buttons.length - 1];

                if (lastButton) {
                    const newButton = document.createElement("bili-text-button");
                    newButton.textContent = "解析评论区图片";
                    lastButton.after(newButton);
                    newButton.addEventListener("click", function () {
                        loadAndDisplayData();
                    });
                }
            }
        } else {
            const navContainer = document.querySelector(".bili-tabs__nav__items");
            if (!navContainer) {
                // 如果找不到导航容器,稍后再试
                setTimeout(addNavButton, 1000);
                return;
            }

            const navItem = document.createElement("div");
            navItem.className = "bili-tabs__nav__item";
            navItem.textContent = "解析评论区图片";
            navItem.style.cssText = `
            cursor: pointer;
        `;

            navItem.addEventListener("click", function () {
                loadAndDisplayData();
            });

            navContainer.appendChild(navItem);
        }
    }

    // 设置分页事件监听
    function setupPaginationEvents() {
        document.addEventListener("click", function (e) {
            const apiType = getCurrentApiType();

            // 只有传统接口才支持翻页
            if (apiType === 'TRADITIONAL') {
                if (e.target.id === "bili-prev-page" && !e.target.disabled) {
                    if (currentPage > 1) {
                        loadAndDisplayData(currentPage - 1);
                    }
                } else if (e.target.id === "bili-next-page" && !e.target.disabled) {
                    loadAndDisplayData(currentPage + 1);
                }
            }
        });
    }

    // 监听评论更新事件
    function setupCommentUpdateListener() {
        window.addEventListener('biliCommentUpdate', function () {
            refreshDownloadInterface();
        });
    }
    // 主函数
    function main() {
        // 创建下载菜单但不显示
        createDownloadMenu();

        // 添加导航按钮
        addNavButton();

        // 设置分页事件
        setupPaginationEvents();

        // 设置评论更新监听
        setupCommentUpdateListener();

        // 添加油猴脚本菜单命令,点击后弹出下载界面
        GM_registerMenuCommand("显示下载界面", function () {
            loadAndDisplayData(1);
        });

        // 添加API配置菜单命令
        GM_registerMenuCommand("API接口配置", function () {
            showApiConfigDialog();
        });
    }

    // 页面加载完成后执行
    window.addEventListener("load", main);
})();