// ==UserScript==
// @name Bangumi Ultimate Enhancer
// @namespace https://tampermonkey.net/
// @version 2.3
// @description Bangumi 终极增强套件 - 集成Wiki按钮、关联按钮、封面上传、批量关联、批量分集编辑等功能
// @author Bios (improved by Claude)
// @match *://bgm.tv/subject/*
// @match *://chii.in/subject/*
// @match *://bangumi.tv/subject*
// @match *://bgm.tv/character/*
// @match *://chii.in/character/*
// @match *://bangumi.tv/character/*
// @match *://bgm.tv/person/*
// @match *://chii.in/person/*
// @match *://bangumi.tv/person/*
// @connect bgm.tv
// @grant GM_xmlhttpRequest
// @license MIT
// @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js
// @run-at document-idle
// ==/UserScript==
(function() {
"use strict";
// 在页面加载完成后执行
$(document).ready(function() {
// 判断当前是否在关联页面
const isRelatedPage = window.location.href.indexOf('/add_related') !== -1;
// 判断当前是否在条目/角色/人物主页
const isMainPage = /\/(subject|character|person)\/\d+$/.test(window.location.pathname);
// 根据页面类型选择初始化不同功能
if (isRelatedPage) {
initBatchRelation();
} else if (isMainPage) {
initIDCollector();
}
});
// 样式注入
function injectStyles() {
$('head').append(`
<style>
/* 通用按钮美化 */
.btnCustom {
margin: 5px 0;
background-color: #1E90FF !important;
color: white !important;
border-radius: 5px !important;
padding: 6px 18px !important;
border: none !important;
cursor: pointer !important;
position: relative;
top: -4px;
left: -8px;
font-size: 14px;
font-weight: bold;
text-align: center;
display: flex;
justify-content: center;
align-items: center;
display: inline-block;
transition: opacity 0.2s, transform 0.2s;
}
.btnCustom:hover {
opacity: 0.9;
box-shadow: 3px 6px 12px rgba(0, 0, 0, 0.15);
}
/* 输入框优化 */
.enhancer-textarea {
width: 100%;
min-height: 80px;
max-height: 300px;
border: 1px solid #ccc;
border-radius: 12px;
padding: 10px;
margin: 10px 0;
resize: vertical;
font-size: 14px;
box-sizing: border-box;
background: #fdfdfd;
transition: all 0.05s ease-in-out;
}
.enhancer-textarea:focus {
border-color: #1E90FF;
box-shadow: 0 0 8px rgba(30, 144, 255, 0.3);
outline: none;
}
/* 卡片式面板美化 */
.enhancer-panel {
margin: 10px 0;
border-radius: 10px;
padding: 12px;
background: #ffffff;
border: 1px solid #e0e0e0;
box-shadow: 2px 4px 8px rgba(0, 0, 0, 0.05);
}
.enhancer-panel h3 {
margin-top: 0;
margin-bottom: 10px;
font-size: 15px;
color: #333;
}
/* 标签和选择框美化 */
.select-label {
margin-right: 8px;
font-weight: bold;
color: #555;
}
.chitanda_item_type {
display: flex;
margin-right: auto;
align-items: center;
margin-bottom: 8px;
}
/* 主要容器美化 */
.chitanda_wrapper {
margin: 15px 0;
background: #fff;
padding: 15px;
border-radius: 10px;
border: 1px solid #eaeaea;
box-shadow: 2px 4px 8px rgba(0, 0, 0, 0.05);
}
/* 进度提示美化 */
.chitanda_progress {
margin: 12px 0;
color: #d346bb;
font-weight: bold;
font-size: 18px;
text-align: center;
}
/* 信息提示框优化 */
.chitanda_item_not_found, .chitanda_item_dupe {
margin-top: 8px;
padding: 8px;
min-height: 15px;
border-radius: 6px;
font-size: 12px;
}
.chitanda_item_not_found {
color: #e74c3c;
background: #ffebeb;
border: 1px solid #e74c3c;
}
.chitanda_item_dupe {
color: #3498db;
background: #eaf6ff;
border: 1px solid #3498db;
}
/* 标题优化 */
.chitanda_header {
font-size: 16px;
margin: 12px 0 6px;
color: #333;
font-weight: bold;
}
/* 按钮区域对齐优化 */
.chitanda_controls {
display: flex;
justify-content: flex-end;
align-items: center;
}
/* 标签导航美化 */
.tab-nav {
display: flex;
border-bottom: 2px solid #ddd;
margin-bottom: 15px;
}
.tab-nav button {
background: none;
border: none;
padding: 10px 20px;
cursor: pointer;
font-size: 14px;
font-weight: bold;
border-bottom: 3px solid transparent;
transition: all 0.3s;
}
.tab-nav button.active {
border-bottom: 3px solid #1E90FF;
color: #1E90FF;
}
.tab-nav button:hover {
color: #1E90FF;
}
/* 选项卡面板优化 */
.tab-panel {
display: none;
}
.tab-panel.active {
display: block;
animation: fadeIn 0.3s ease-in-out;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(5px); }
to { opacity: 1; transform: translateY(0); }
}
/* 行内表单优化 */
.flex-row {
display: flex;
gap: 12px;
align-items: center;
}
/* 输入框美化 */
.input-number {
width: 100px;
padding: 8px;
border-radius: 8px;
border: 1px solid #ddd;
background: #f9f9f9;
transition: all 0.2s;
}
.input-number:focus {
border-color: #1E90FF;
box-shadow: 0 0 5px rgba(30, 144, 255, 0.3);
outline: none;
}
</style>
`);
}
/* Wiki 按钮和关联按钮模块 */
function initNavButtons() {
// 排除编辑页面
if (/(edit_detail|edit)$/.test(location.pathname)) return;
// 排除 add_related 和 upload_img 页面
if (/add_related|upload_img/.test(location.pathname)) return;
const matchSubject = location.pathname.match(/\/subject\/(\d+)/);
const matchPerson = location.pathname.match(/\/person\/(\d+)/);
const matchCharacter = location.pathname.match(/\/character\/(\d+)/);
const nav = document.querySelector(".subjectNav .navTabs, .navTabs");
if (!nav) return;
// 检查Wiki按钮是否已存在
if (!nav.querySelector(".wiki-button")) {
const wikiLi = document.createElement("li");
wikiLi.className = "wiki-button";
let wikiUrl = "";
if (matchSubject) {
wikiUrl = `${location.origin}/subject/${matchSubject[1]}/edit_detail`;
} else if (matchPerson) {
wikiUrl = `${location.origin}/person/${matchPerson[1]}/edit`;
} else if (matchCharacter) {
wikiUrl = `${location.origin}/character/${matchCharacter[1]}/edit`;
}
if (wikiUrl) {
wikiLi.innerHTML = `<a href="${wikiUrl}" target="_blank">Wiki</a>`;
nav.appendChild(wikiLi);
}
}
// 为主条目添加关联按钮
if (matchSubject && !nav.querySelector(".relate-button")) {
const relateLi = document.createElement("li");
relateLi.className = "relate-button";
// 关联按钮默认链接到添加动画关联
const relateUrl = `${location.origin}/subject/${matchSubject[1]}/add_related/subject/anime`;
relateLi.innerHTML = `<a href="${relateUrl}" target="_blank">关联</a>`;
nav.appendChild(relateLi);
}
}
// 监听 URL 变化
function observeURLChanges() {
let lastURL = location.href;
new MutationObserver(() => {
if (location.href !== lastURL) {
lastURL = location.href;
initNavButtons();
}
}).observe(document, { subtree: true, childList: true });
}
/* 封面上传模块 */
async function initCoverUpload() {
if (document.querySelector("img.cover")) return;
const infoBox = document.querySelector("#bangumiInfo");
if (!infoBox) return;
const links = document.querySelectorAll(".tip_i p a.l");
if (links.length < 2) return;
try {
const res = await fetch(links[1].href);
const doc = new DOMParser().parseFromString(await res.text(), "text/html");
const form = doc.querySelector("#columnInSubjectA .text form");
if (form) {
const container = document.createElement("div");
container.className = "enhancer-panel";
const clone = form.parentElement.cloneNode(true);
const uploadForm = clone.querySelector("form");
uploadForm.id = "coverUploadForm";
const fileInput = uploadForm.querySelector("input[type=file]");
fileInput.style.width = "100%";
const submitBtn = uploadForm.querySelector("input[type=submit]");
submitBtn.className = "btnCustom";
submitBtn.style.width = "120px";
submitBtn.style.margin = "10px auto 0";
container.appendChild(uploadForm);
infoBox.parentNode.insertBefore(container, infoBox);
}
} catch (e) {
console.error("封面加载失败:", e);
}
}
/* ------------------------------
批量分集编辑器功能模块
------------------------------ */
const BatchEpisodeEditor = {
CHUNK_SIZE: 20,
BASE_URL: '',
CSRF_TOKEN: '',
// 初始化方法
init() {
if (!this.isEpisodePage()) return;
this.BASE_URL = location.pathname.replace(/\/edit_batch$/, '');
this.CSRF_TOKEN = $('[name=formhash]')?.value || '';
if (!this.CSRF_TOKEN) return;
this.bindHashChange();
this.upgradeCheckboxes();
// 添加功能标识
const header = document.querySelector('h2.subtitle');
if (header) {
const notice = document.createElement('div');
notice.className = 'bgm-enhancer-status';
notice.textContent = '已启用分批编辑功能,支持超过20集的批量编辑';
header.parentNode.insertBefore(notice, header.nextSibling);
}
},
// 检查是否为分集页面
isEpisodePage() {
return /^\/subject\/\d+\/ep(\/edit_batch)?$/.test(location.pathname);
},
// 监听hash变化处理批量编辑
bindHashChange() {
const processHash = () => {
const ids = this.getSelectedIdsFromHash();
if (ids.length > 0) this.handleBatchEdit(ids);
};
window.addEventListener('hashchange', processHash);
if (location.hash.includes('episodes=')) processHash();
},
// 增强复选框功能
upgradeCheckboxes() {
// 动态更新表单action
const updateFormAction = () => {
const ids = $$('[name="ep_mod[]"]:checked').map(el => el.value);
$('form[name="edit_ep_batch"]').action =
`${this.BASE_URL}/edit_batch#episodes=${ids.join(',')}`;
};
$$('[name="ep_mod[]"]').forEach(el =>
el.addEventListener('change', updateFormAction)
);
// 全选功能
$('[name=chkall]')?.addEventListener('click', () => {
$$('[name="ep_mod[]"]').forEach(el => el.checked = true);
updateFormAction();
});
},
// 从hash获取选中ID
getSelectedIdsFromHash() {
const match = location.hash.match(/episodes=([\d,]+)/);
return match ? match[1].split(',').filter(Boolean) : [];
},
// 批量编辑主逻辑
async handleBatchEdit(episodeIds) {
try {
// 分块加载数据
const chunks = this.createChunks(episodeIds, this.CHUNK_SIZE);
const dataChunks = await this.loadChunkedData(chunks);
// 填充表单数据
$('#summary').value = dataChunks.flat().join('\n');
$('[name=ep_ids]').value = episodeIds.join(',');
// 增强表单提交
this.upgradeFormSubmit(chunks, episodeIds);
window.chiiLib?.ukagaka?.presentSpeech('数据加载完成');
} catch (err) {
console.error('批量处理失败:', err);
alert('数据加载失败,请刷新重试');
}
},
// 分块加载数据
async loadChunkedData(chunks) {
window.chiiLib?.ukagaka?.presentSpeech('正在加载分集数据...');
return Promise.all(chunks.map(chunk =>
this.fetchChunkData(chunk).then(data => data.split('\n'))
));
},
// 获取单块数据
async fetchChunkData(episodeIds) {
const params = new URLSearchParams();
params.append('chkall', 'on');
params.append('submit', '批量修改');
params.append('formhash', this.CSRF_TOKEN);
episodeIds.forEach(id => params.append('ep_mod[]', id));
const res = await fetch(`${this.BASE_URL}/edit_batch`, {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: params
});
const html = await res.text();
const match = html.match(/<textarea [^>]*name="ep_list"[^>]*>([\s\S]*?)<\/textarea>/i);
return match?.[1]?.trim() || '';
},
// 增强表单提交处理
upgradeFormSubmit(chunks, originalIds) {
const form = $('form[name="edit_ep_batch"]');
if (!form) return;
form.onsubmit = async (e) => {
e.preventDefault();
// 验证数据完整性
const inputData = $('#summary').value.trim().split('\n');
if (inputData.length !== originalIds.length) {
alert(`数据不匹配 (预期 ${originalIds.length} 行,实际 ${inputData.length} 行)`);
return;
}
try {
window.chiiLib?.ukagaka?.presentSpeech('正在提交数据...');
await this.saveChunkedData(chunks, inputData);
window.chiiLib?.ukagaka?.presentSpeech('保存成功');
location.href = this.BASE_URL;
} catch (err) {
console.error('保存失败:', err);
alert('保存过程中发生错误');
}
};
},
// 分块保存数据
async saveChunkedData(chunks, fullData) {
const dataChunks = this.createChunks(fullData, this.CHUNK_SIZE);
return Promise.all(chunks.map((idChunk, index) =>
this.saveChunkData(idChunk, dataChunks[index])
));
},
// 保存单块数据
async saveChunkData(episodeIds, chunkData) {
const params = new URLSearchParams();
params.append('formhash', this.CSRF_TOKEN);
params.append('rev_version', '0');
params.append('editSummary', $('#editSummary')?.value || '');
params.append('ep_ids', episodeIds.join(','));
params.append('ep_list', chunkData.join('\n'));
params.append('submit_eps', '改好了');
await fetch(`${this.BASE_URL}/edit_batch`, {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: params
});
},
// 通用分块方法
createChunks(array, size) {
return Array.from(
{ length: Math.ceil(array.length / size) },
(_, i) => array.slice(i * size, (i + 1) * size)
);
}
};
// ID收集器 - 用于条目/角色/人物主页
function initIDCollector() {
injectStyles();
// 获取当前页面ID
const getPageID = () => {
const match = location.pathname.match(/\/(subject|character|person)\/(\d+)/);
return match ? parseInt(match[2]) : null;
};
const currentID = getPageID();
// 创建界面容器
const container = document.createElement("div");
container.innerHTML = `
<div class="enhancer-panel">
<h3>批量关联助手</h3>
<p>当前位置:ID收集 - 点击下方按钮前往关联页面</p>
<div style="text-align: center">
<a href="${window.location.pathname}/add_related/subject" class="btnCustom">
前往批量关联页面
</a>
</div>
</div>
`;
// 插入到页面
const targetNode = $("#sbjSearchMod") || $(".main_content");
if (targetNode.length) targetNode.after(container);
}
// 批量关联功能 - 用于关联页面
function initBatchRelation() {
injectStyles();
// 参数配置
const DELAY_AFTER_CLICK = 250;
const DELAY_BETWEEN_ITEMS = 500;
const MAX_RETRY_ATTEMPTS = 10;
const RETRY_INTERVAL = 100;
// 全局变量
let globalItemType = '1';
let currentProcessingIndex = -1;
// 根据当前 URL 判断页面类型
function getCurrentPageType() {
const url = window.location.href;
if (url.indexOf('/add_related/character') !== -1) {
return 'character';
} else if (url.indexOf('/add_related/subject') !== -1) {
return 'subject';
} else {
return 'person';
}
}
// 生成关联类型选择下拉框
function generateTypeSelector() {
const pageType = getCurrentPageType();
if (pageType === 'character') {
let options = '';
const relationTypes = {
'1': '主角',
'2': '配角',
'3': '客串'
};
for (let [value, text] of Object.entries(relationTypes)) {
options += `<option value="${value}">${text}</option>`;
}
return `<span class="select-label">类型: </span><select>${options}</select>`;
} else {
// 如果有 genPrsnStaffList 函数则调用,否则返回空字符串
return `<span class="select-label"></span>${(typeof genPrsnStaffList === "function") ? genPrsnStaffList(-1) : ''}`;
}
}
// 针对传入的元素内的下拉框进行设置,并通过递归确保修改成功
function setRelationTypeWithElement($li, item_type) {
return new Promise((resolve) => {
let attempts = 0;
function trySet() {
// 确保我们获取的是当前元素内部的select,而不是全局的
let $select = $li.find('select').first();
if ($select.length > 0) {
// 先确保下拉框可交互
if ($select.prop('disabled')) {
setTimeout(trySet, RETRY_INTERVAL);
return;
}
$select.val(item_type);
// 触发 change 事件
const event = new Event('change', { bubbles: true });
$select[0].dispatchEvent(event);
setTimeout(() => {
if ($select.val() == item_type) {
resolve(true);
} else if (attempts < MAX_RETRY_ATTEMPTS) {
attempts++;
setTimeout(trySet, RETRY_INTERVAL);
} else {
resolve(false);
}
}, 200);
} else if (attempts < MAX_RETRY_ATTEMPTS) {
attempts++;
setTimeout(trySet, RETRY_INTERVAL);
} else {
resolve(false);
}
}
trySet();
});
}
// 点击项目后利用 MutationObserver 监听新增条目,然后对该条目的下拉框设置类型
function processItem(element, item_type) {
return new Promise((resolve) => {
// 关联列表容器
const container = document.querySelector('#crtRelateSubjects');
if (!container) {
return resolve(false);
}
// 保存处理前的条目列表
const initialItems = Array.from(container.children);
// 绑定 MutationObserver 监听子节点变化
const observer = new MutationObserver((mutations) => {
// 获取当前所有条目
const currentItems = Array.from(container.children);
// 找出新增的条目(在当前列表中但不在初始列表中的元素)
const newItems = currentItems.filter(item => !initialItems.includes(item));
if (newItems.length > 0) {
observer.disconnect();
const newItem = newItems[0]; // 获取第一个新增条目
// 确保等待DOM完全渲染
setTimeout(async () => {
// 使用新的条目元素直接查找其内部的select
const $select = $(newItem).find('select');
if ($select.length > 0) {
const success = await setRelationTypeWithElement($(newItem), item_type);
resolve(success);
} else {
resolve(false);
}
}, DELAY_AFTER_CLICK);
}
});
observer.observe(container, { childList: true, subtree: true });
// 触发点击
$(element).click();
// 超时防护
setTimeout(() => {
observer.disconnect();
resolve(false);
}, MAX_RETRY_ATTEMPTS * RETRY_INTERVAL);
});
}
// 处理单个项目(若搜索结果不唯一则自动选择第一个)
async function processSingleItem(elements, item_type, search_name, idx, item_list, item_num) {
return new Promise((resolve) => {
if (elements.length === 0) {
$('.chitanda_item_not_found').append(search_name + ' ');
resolve(false);
return;
}
if (elements.length > 1) {
$('.chitanda_item_dupe').append(`${search_name} `);
processItem(elements[0], item_type).then((success) => {
resolve(success);
});
} else {
processItem(elements[0], item_type).then((success) => {
resolve(success);
});
}
});
}
// 处理下一个项目
async function proceedToNextItem(idx, item_list, item_type, item_num) {
if (idx < item_num - 1) {
setTimeout(async () => {
await ctd_findItemFunc(item_list, item_type, idx + 1);
}, DELAY_BETWEEN_ITEMS);
} else {
setTimeout(() => {
$('#subjectList').empty();
$('#subjectList').show();
alert('全部添加完成');
}, DELAY_BETWEEN_ITEMS);
}
}
// 核心查找及处理函数:依次检索每个条目并处理
var ctd_findItemFunc = async function(item_list, item_type, idx) {
currentProcessingIndex = idx;
item_type = globalItemType;
let search_name = item_list[idx].trim();
if (!search_name) {
proceedToNextItem(idx, item_list, item_type, item_list.length);
return;
}
var item_num = item_list.length;
$('#subjectList').html('<tr><td>正在检索中...</td></tr>');
var search_mod = $('#sbjSearchMod').attr('value');
try {
const response = await new Promise((resolve, reject) => {
$.ajax({
type: "GET",
url: '/json/search-' + search_mod + '/' + encodeURIComponent(search_name),
dataType: 'json',
success: resolve,
error: reject
});
});
var html = '';
if ($(response).length > 0) {
subjectList = response;
for (var i in response) {
if ($.inArray(search_mod, enableStaffSbjType) != -1) {
html += genSubjectList(response[i], i, 'submitForm');
} else {
html += genSubjectList(response[i], i, 'searchResult');
}
}
$('#subjectList').html(html);
$('.chitanda_current_idx').text(idx + 1);
$('.chitanda_all_num').text(item_num);
await new Promise(resolve => setTimeout(resolve, 400)); // 减少等待时间
var elements = $('#subjectList>li>a.avatar.h');
await processSingleItem(elements, item_type, search_name, idx, item_list, item_num);
await proceedToNextItem(idx, item_list, item_type, item_num);
} else {
$("#robot").fadeIn(300);
$("#robot_balloon").html(`没有找到 ${search_name} 的相关结果`);
$("#robot").animate({ opacity: 1 }, 500).fadeOut(500); // 减少动画时间
$('.chitanda_item_not_found').append(search_name + ' ');
$('#subjectList').html(html);
$('.chitanda_current_idx').text(idx + 1);
$('.chitanda_all_num').text(item_num);
await proceedToNextItem(idx, item_list, item_type, item_num);
}
} catch (error) {
console.error('查询出错:', error);
$("#robot").fadeIn(300);
$("#robot_balloon").html('通信错误,您是不是重复查询太快了?');
$("#robot").animate({ opacity: 1 }, 500).fadeOut(1000); // 减少动画时间
$('#subjectList').html('');
setTimeout(async () => {
if (idx < item_list.length - 1) {
await ctd_findItemFunc(item_list, item_type, idx + 1);
} else {
$('#subjectList').empty();
$('#subjectList').show();
alert('全部添加完成,但部分查询出错');
}
}, 1500); // 减少等待时间
}
};
// 从ID范围中提取ID列表
function getIDsFromRange(start, end) {
const startID = parseInt(start, 10);
const endID = parseInt(end, 10);
if (isNaN(startID) || isNaN(endID) || startID > endID) {
alert("ID范围无效");
return [];
}
return Array.from({ length: endID - startID + 1 }, (_, i) => "bgm_id=" + (startID + i));
}
// 从自由文本中提取ID列表
function getIDsFromText(input) {
if (input.trim() === "") {
alert("请输入ID或内容");
return [];
}
// 先检查是否以bgm_id=开头
if (input.indexOf("bgm_id=") === 0) {
var idStr = input.substring(7);
var ids = idStr.split(/[\s,,、\/]+/);
return ids.map(function(id) {
return "bgm_id=" + id.trim();
}).filter(function(x) { return x; });
}
// 检查是否包含纯数字或URL形式的ID
const urlPattern = /(bgm\.tv|bangumi\.tv|chii\.in)\/(subject|character|person)\/(\d+)/g;
const numberPattern = /\b\d+\b/g;
let matches = [];
let urlMatches = [];
let match;
// 提取URL中的ID
while ((match = urlPattern.exec(input)) !== null) {
urlMatches.push(match[3]); // ID部分
}
// 如果找到URL形式的ID,则优先使用
if (urlMatches.length > 0) {
return urlMatches.map(id => "bgm_id=" + id);
}
// 否则尝试提取纯数字ID
while ((match = numberPattern.exec(input)) !== null) {
matches.push(match[0]);
}
if (matches.length > 0) {
return matches.map(id => "bgm_id=" + id);
}
// 如果没有找到任何ID形式,则按正常项目分隔
return input.split(/['\,、,\/']/)
.map(function(s) { return s.trim(); })
.filter(function(s) { return s.length > 0; });
}
// 批量查找入口函数
var chitanda_MultiFindItemFunc = async function() {
let item_type = '1';
let typeSelector = $('.chitanda_item_type select');
if (typeSelector.length > 0) {
item_type = typeSelector.val();
if (item_type == '-999') {
alert('请先选择关联类型');
return false;
}
globalItemType = item_type;
}
let ctd_item_list = [];
const activeTab = $('.tab-panel.active').attr('id');
if (activeTab === 'tab-text') {
// 处理文本输入模式
const inputVal = $('#custom_ids').val().trim();
ctd_item_list = getIDsFromText(inputVal);
} else if (activeTab === 'tab-range') {
// 处理ID范围模式
const startID = $('#id_start').val().trim();
const endID = $('#id_end').val().trim();
ctd_item_list = getIDsFromRange(startID, endID);
}
if (ctd_item_list.length === 0) {
return false;
}
$('#subjectList').hide();
$('.chitanda_item_not_found').empty();
$('.chitanda_item_dupe').empty();
$('.chitanda_current_idx').text('0');
$('.chitanda_all_num').text(ctd_item_list.length);
currentProcessingIndex = -1;
await ctd_findItemFunc(ctd_item_list, item_type, 0);
};
// 切换标签页
function switchTab(tabId) {
$('.tab-nav button').removeClass('active');
$(`.tab-nav button[data-tab="${tabId}"]`).addClass('active');
$('.tab-panel').removeClass('active');
$(`#${tabId}`).addClass('active');
}
// 根据页面类型设定 UI 标题
let uiTitle = '人物';
const pageType = getCurrentPageType();
if (pageType === 'character') {
uiTitle = '角色';
} else if (pageType === 'subject') {
uiTitle = '条目';
}
// 创建改进的UI界面
$('.subjectListWrapper').after(`
<div class="chitanda_wrapper">
<h3>批量关联助手</h3>
<div class="tab-nav">
<button data-tab="tab-text" class="active">自由文本输入</button>
<button data-tab="tab-range">ID范围输入</button>
</div>
<div id="tab-text" class="tab-panel active">
<textarea id="custom_ids" class="enhancer-textarea"
placeholder="输入ID或网址(支持多种格式:纯数字ID、bgm_id=xxx格式、完整网址、名称文本,可用逗号、空格等分隔)"></textarea>
</div>
<div id="tab-range" class="tab-panel">
<div class="flex-row" style="justify-content: center">
<input id="id_start" type="number" placeholder="起始ID" class="input-number">
<span style="line-height: 30px">~</span>
<input id="id_end" type="number" placeholder="结束ID" class="input-number">
</div>
</div>
<div class="chitanda_controls" style="margin-top: 10px">
<span class="chitanda_item_type"></span>
<button id="btn_ctd_multi_search" class="btnCustom">批量关联</button>
</div>
<div class="chitanda_progress">
添加进度:<span class="chitanda_current_idx">0</span>/<span class="chitanda_all_num">0</span>
</div>
<div class="chitanda_header">未找到的${uiTitle}:</div>
<div class="chitanda_item_not_found"></div>
<div class="chitanda_header">存在多个结果的${uiTitle}(已自动选择第一个):</div>
<div class="chitanda_item_dupe"></div>
</div>
`);
// 添加关联类型选择器
$('.chitanda_item_type').append(generateTypeSelector());
$('.chitanda_item_type select').prepend('<option value="-999">请选择关联类型</option>').val('-999');
// 绑定事件
$('#btn_ctd_multi_search').on('click', chitanda_MultiFindItemFunc);
$('.chitanda_item_type select').on('change', function() {
globalItemType = $(this).val();
});
$('.tab-nav button').on('click', function() {
switchTab($(this).data('tab'));
});
}
/* 启动所有功能 */
function startEnhancer() {
// 启动超级增强器功能
initNavButtons();
observeURLChanges();
initCoverUpload();
// 启动批量分集编辑器功能
BatchEpisodeEditor.init();
console.log("Bangumi Ultimate Enhancer 已启动");
}
// 在DOM加载完成后启动脚本
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', startEnhancer);
} else {
startEnhancer();
}
})();