Greasy Fork is available in English.
Bangumi 增强套件
当前为
// ==UserScript==
// @name Bangumi Super Enhancer 222
// @namespace https://tampermonkey.net/
// @version 2.2
// @description Bangumi 增强套件
// @author Bios
// @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/*
// @grant GM_xmlhttpRequest
// @grant GM_addStyle
// @license MIT
// @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js
// ==/UserScript==
(function () {
"use strict";
// 样式增强
GM_addStyle(`
.btnCustom {
margin: 5px 0;
background-color: #1E90FF !important;
color: white !important;
border-radius: 10px !important;
padding: 5px 15px !important;
border: none !important;
cursor: pointer !important;
transition: opacity 0.2s;
}
.btnCustom:hover {
opacity: 0.8;
}
.enhancer-textarea {
width: 100%;
min-height: 60px;
max-height: 300px;
border: 1px solid #ddd;
border-radius: 10px;
padding: 8px;
margin: 8px 0;
resize: vertical;
font-size: 13px;
box-sizing: border-box;
}
.enhancer-panel {
margin: 15px 0;
border-radius: 10px;
padding: 15px;
background: #f5f5f5;
border: 1px solid #e0e0e0;
}
#coverUploadForm {
text-align: center;
padding: 10px;
}
#coverUploadForm input[type="file"] {
margin: 10px auto;
display: block;
}
`);
/* Wiki 按钮模块 */
function initWikiButton() {
// 排除编辑页面
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 || nav.querySelector(".wiki-button")) return;
const li = document.createElement("li");
li.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) {
li.innerHTML = `<a href="${wikiUrl}" target="_blank" onclick="this.closest('li').remove()">Wiki</a>`;
nav.appendChild(li);
}
}
// 监听 URL 变化
function observeURLChanges() {
let lastURL = location.href;
new MutationObserver(() => {
if (location.href !== lastURL) {
lastURL = location.href;
initWikiButton();
}
}).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);
}
}
/* 章节批量编辑 */
function enhanceEpisodes() {
if (!location.pathname.includes("/ep")) return;
const formHash = document.querySelector("[name=formhash]")?.value;
if (!formHash) return;
document.querySelector("[name=edit_ep_batch]")?.addEventListener("submit", async (e) => {
e.preventDefault();
const episodes = [...document.querySelectorAll('[name="ep_mod[]"]:checked')].map(el => el.value);
let batchSize = 100;
while (batchSize >= 50 && episodes.length > 0) {
try {
const data = new URLSearchParams();
data.append("formhash", formHash);
data.append("chkall", "on");
data.append("submit", "批量修改");
episodes.splice(0, batchSize).forEach(ep => data.append("ep_mod[]", ep));
const res = await fetch(location.pathname + "/edit_batch", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: data
});
if (!res.ok) throw new Error();
alert(`成功提交 ${batchSize} 个章节`);
return;
} catch {
batchSize = Math.floor(batchSize / 2);
}
}
alert("提交失败,请减少选中数量!");
});
}
/* 获取当前页面的 ID(即 URL 里的 subject/xxx、character/xxx、person/xxx)*/
function getCurrentPageID() {
const match = window.location.pathname.match(/\/(subject|character|person)\/(\d+)/);
return match ? parseInt(match[2], 10) : null;
}
const currentPageID = getCurrentPageID(); /* 获取当前条目 ID */
/* 批量关联模块 */
function initBatchRelation() {
if (!document.getElementById("indexCatBox")) return;
const panelHTML = `
<div class="enhancer-panel">
<textarea id="custom_ids" class="enhancer-textarea"
placeholder="输入 ID 或网址(可换行,支持各种分隔字符)"></textarea>
<div style="text-align: center">
<button id="btn_execute" class="btnCustom">自动添加关联</button>
</div>
</div>
<div class="enhancer-panel">
<div style="display: flex; gap: 10px; justify-content: center">
<input id="id_start" type="number" placeholder="起始ID"
style="width: 100px; padding: 6px; border-radius: 8px; border: 1px solid #ddd;">
<span style="line-height: 30px">~</span>
<input id="id_end" type="number" placeholder="结束ID"
style="width: 100px; padding: 6px; border-radius: 8px; border: 1px solid #ddd;">
</div>
<div style="text-align: center; margin-top: 12px">
<button id="btn_generate" class="btnCustom">自动添加关联</button>
</div>
</div>
`;
const searchMod = document.querySelector("#sbjSearchMod");
if (searchMod) searchMod.insertAdjacentHTML("afterend", panelHTML);
document.getElementById("btn_execute")?.addEventListener("click", () => {
const rawInput = $("#custom_ids").val();
const ids = extractUniqueIDs(rawInput);
if (ids.length) processBatch(ids);
});
document.getElementById("btn_generate")?.addEventListener("click", () => {
const start = parseInt($("#id_start").val(), 10);
const end = parseInt($("#id_end").val(), 10);
if (isNaN(start) || isNaN(end) || start > end) {
alert("请输入有效的起始和结束 ID!");
return;
}
const ids = Array.from({ length: end - start + 1 }, (_, i) => start + i);
processBatch([...new Set(ids)]);
});
function extractUniqueIDs(input) {
const allIDs = input.match(/\d+/g) || [];
let uniqueIDs = [...new Set(allIDs.map(Number))].filter(id => id > 0);
if (currentPageID) {
uniqueIDs = uniqueIDs.filter(id => id !== currentPageID);
}
return uniqueIDs;
}
function processBatch(ids) {
if (!ids.length) {
alert("未找到有效的 ID 或所有 ID 都已被排除!");
return;
}
const batch = ids.splice(0, 10);
$("#subjectName").val(`bgm_id=${batch.join(",")}`);
$("#findSubject").click();
$("#subjectList").one("DOMSubtreeModified", function () {
setTimeout(() => {
$("#subjectList .subjectItem input").each(function () {
this.checked = true;
});
setTimeout(() => {
$("#saveSubject").click();
alert("所有关联项已成功添加!");
}, 300);
}, 500);
});
}
}
/* 启动功能 */
function startEnhancer() {
initWikiButton();
observeURLChanges();
initCoverUpload();
enhanceEpisodes();
initBatchRelation();
}
startEnhancer();
})();