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