您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
批量下载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); })();