Greasy Fork

Greasy Fork is available in English.

Bangumi Super Enhancer

Bangumi 增强套件

当前为 2025-03-11 提交的版本,查看 最新版本

// ==UserScript==
// @name         Bangumi Super Enhancer
// @namespace    https://tampermonkey.net/
// @version      1.02
// @description  Bangumi 增强套件
// @author       Bios
// @match        *://bgm.tv/subject/*
// @match        *://chii.in/subject/*
// @match        *://bangumi.tv/subject*
// @match        *://bgm.tv.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-left: 10px;
            background-color: #1E90FF !important;
            color: white !important;
            border-radius: 10px !important;
            padding: 5px 10px !important;
            border: none !important;
            cursor: pointer !important;
        }

        .enhancer-textarea {
            width: 300px !important;
            height: 60px !important;
            border-radius: 10px !important;
            padding: 5px !important;
            margin: 5px 0 !important;
            resize: vertical !important; 
        }

        .enhancer-panel {
            margin-top: 10px;
            border-radius: 10px;
            padding: 10px;
            background: #f5f5f5;
        }
        .enhancer-panel > div {
            margin: 8px 0;
            text-align: center;
        }
    `);

    /* Wiki 按钮模块 */
    function initWikiButton() {
        const match = location.pathname.match(/\/subject\/(\d+)/);
        if (!match) return;

        const nav = document.querySelector(".subjectNav .navTabs");
        if (!nav) return;

        const li = document.createElement("li");
        li.innerHTML = `<a href="/subject/${match[1]}/edit_detail" target="_blank">Wiki</a>`;
        nav.appendChild(li);
    }

    /* 封面上传模块 */
    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 clone = form.parentElement.cloneNode(true);
                clone.querySelector("form").style.marginLeft = "45px";
                infoBox.parentNode.insertBefore(clone, 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("提交失败,请减少选中数量!");
        });
    }

    /* 批量关联模块 */
    function initBatchRelation() {
        if (!document.getElementById("indexCatBox")) return;

        // UI结构
        const panelHTML = `
            <div align="center" class="enhancer-panel">
                <textarea id="custom_ids" class="enhancer-textarea"
                    placeholder="输入 ID 或网址(可换行、支持各种分隔字符)"></textarea>
                <div id="relationListContainer"></div>
                <button id="btn_execute" class="btnCustom">生成</button>
            </div>
            <div align="center" class="enhancer-panel">
                <input id="id_start" type="number" placeholder="起始 ID"
                    style="width: 80px; padding: 5px; border-radius: 10px;">
                ~
                <input id="id_end" type="number" placeholder="结束 ID"
                    style="width: 80px; padding: 5px; border-radius: 10px;">
                <button id="btn_generate" class="btnCustom">生成</button>
            </div>
        `;

        // 插入到位置
        const searchMod = document.querySelector("#sbjSearchMod");
        if (searchMod) searchMod.insertAdjacentHTML("afterend", panelHTML);

        // 事件处理
        document.getElementById("btn_execute")?.addEventListener("click", () => {
            const ids = [...new Set($("#custom_ids").val().match(/\d+/g))].map(Number);
            if (ids.length) processBatch(ids);
        });

        document.getElementById("btn_generate")?.addEventListener("click", () => {
            const start = parseInt($("#id_start").val());
            const end = parseInt($("#id_end").val());
            if (start <= end) processBatch(Array.from({length: end - start + 1}, (_, i) => start + i));
        });

        // 处理逻辑
        function processBatch(ids) {
            if (!ids.length) return;

            const batch = ids.splice(0, 10);
            $("#subjectName").val(`bgm_id=${batch.join(",")}`);
            $("#findSubject").click();

            $("#subjectList").one("DOMSubtreeModified", function () {
                setTimeout(() => {
                    $("#subjectList .clearit p .avatar").click();
                    const type = $("#relationListContainer select").val();
                    $("#crtRelateSubjects li:not(.old)").slice(0, 10).find("select").val(type);

                    if (ids.length) setTimeout(() => processBatch(ids), 1000);
                }, 1000);
            });
        }
    }

    // 初始化入口
    function init() {
        initWikiButton();
        initCoverUpload();
        enhanceEpisodes();
        initBatchRelation();
    }

    if (document.readyState === "loading") {
        document.addEventListener("DOMContentLoaded", init);
    } else {
        init();
    }
})();