Greasy Fork

Greasy Fork is available in English.

Bangumi Super Enhancer

Bangumi 增强套件

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

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Bangumi Super Enhancer
// @namespace    https://tampermonkey.net/
// @version      1.0
// @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;
        }

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