★Bangumi动画详情&播放源整合★

支持在多个动画网站一键跳转Bangumi查看详情,并在Bangumi番剧详情页中新增“播放源”按钮,可快速跳转至B站、次元城、稀饭动漫等站点搜索播放源

// ==UserScript==
// @name         ★Bangumi动画详情&播放源整合★
// @namespace    http://tampermonkey.net/
// @version      0.2.0
// @description  支持在多个动画网站一键跳转Bangumi查看详情,并在Bangumi番剧详情页中新增“播放源”按钮,可快速跳转至B站、次元城、稀饭动漫等站点搜索播放源
// @author       Aomine
// @match        *.bgm.tv/subject/*
// @match        *://www.agedm.io/play*
// @match        *://www.cycani.org/watch*
// @match        *://www.mutean.com/vodplay*
// @match        *://www.aafun.cc/f*
// @match        *://www.ntdm8.com/play*
// @match        *://www.mwcy.net/play*
// @match        *://dm.xifanacg.com/watch*
// @match        *://anich.emmmm.eu.org/b*
// @match        *://www.bilibili.com/bangumi/play*
// @match        *://www.gugu3.com/index.php/vod/play/id/*/sid/*/nid*
// @grant        GM_xmlhttpRequest
// @grant        GM_registerMenuCommand
// @grant        GM_getValue
// @grant        GM_setValue
// @connect      bgm.tv
// @license      GPL License
// @icon         https://bgm.tv/img/favicon.ico
// ==/UserScript==

(function() {
    'use strict';

    // 初始化设置
    const config = {
        showButton: GM_getValue('showButton', true),  // 默认显示按钮
        excludedSites: GM_getValue('excludedSites', [])  // 默认不排除任何站点
    };

    // 注册(不可用)菜单命令
    GM_registerMenuCommand("⚙️ 脚本设置", openSettings);
    GM_registerMenuCommand(config.showButton ? "🔘 按钮开关" :"❌ 隐藏按钮" , toggleButton);

    // 各网站标题选择器配置
    const siteSelectors = {
        'www.agedm.io': '.card-body .card-title',
        'www.cycani.org':'a.player-title-link',
        'www.mutean.com': '.module-info-heading h1 a',
        'anich.emmmm.eu.org': 'section[player-info] a[href^="/b/"]',
        'www.ntdm8.com': 'h4#detailname a:first-child',
        'www.mwcy.net': 'h2.top20 a.player-title-link',
        'dm.xifanacg.com': 'a.player-title-link',
        'www.gugu3.com': 'a.player-title-link',
        'www.aafun.cc': [
            'h2.play-title .hl-infos-title',
            '.hl-dc-title .hl-data-menu'
        ],
        'www.bilibili.com': [
            '.mediainfo_mediaTitle__Zyiqh',
            '[class*="mediaTitle"]',
            '.video-info .video-title',
            '.media-title',
            'h1.title'
        ].join(', ')
    };

    // 切换按钮显示状态
    function toggleButton() {
        config.showButton = !config.showButton;
        GM_setValue('showButton', config.showButton);

        const button = document.querySelector('#bangumiJumpButton');
        if (button) {
            if (config.showButton) {
                button.style.display = 'block';
                setTimeout(() => { button.style.opacity = '1'; }, 10);
            } else {
                button.style.opacity = '0';
                setTimeout(() => { button.style.display = 'none'; }, 300);
            }
        } else if (config.showButton && shouldShowButton()) {
            createJumpButton();
        }
    }

    // 打开设置界面
    function openSettings() {
        const settings = `
            <div style="padding:10px;font-family:Arial,sans-serif;max-width:500px">
                <h3>Bangumi跳转脚本设置</h3>
                <label style="display:block;margin:10px 0">
                    <input type="checkbox" ${config.showButton ? 'checked' : ''}
                           id="showButtonCheckbox">
                    显示右下角"查看详情"按钮
                </label>
                <h4>排除网站:</h4>
                ${Object.keys(siteSelectors).map(domain => `
                    <label style="display:block;margin:5px 0">
                        <input type="checkbox" ${config.excludedSites.includes(domain) ? 'checked' : ''}
                               class="excludeCheckbox" data-domain="${domain}">
                        ${domain}
                    </label>
                `).join('')}
            </div>
            <script>
                document.getElementById('showButtonCheckbox').addEventListener('change', function() {
                    window.opener.postMessage({
                        type: 'updateShowButton',
                        value: this.checked
                    }, '*');
                });

                document.querySelectorAll('.excludeCheckbox').forEach(checkbox => {
                    checkbox.addEventListener('change', function() {
                        window.opener.postMessage({
                            type: 'updateExcludedSite',
                            domain: this.dataset.domain,
                            value: this.checked
                        }, '*');
                    });
                });
            </script>
        `;

        const win = window.open('', '_blank', 'width=500,height=400');
        win.document.write(settings);

        // 监听设置窗口的消息
        window.addEventListener('message', function(event) {
            if (event.data.type === 'updateShowButton') {
                config.showButton = event.data.value;
                GM_setValue('showButton', config.showButton);
                toggleButton(); // 直接调用切换函数更新按钮状态
            } else if (event.data.type === 'updateExcludedSite') {
                const excluded = GM_getValue('excludedSites', []);
                if (event.data.value && !excluded.includes(event.data.domain)) {
                    excluded.push(event.data.domain);
                } else {
                    const index = excluded.indexOf(event.data.domain);
                    if (index > -1) excluded.splice(index, 1);
                }
                GM_setValue('excludedSites', excluded);
                config.excludedSites = excluded;

                // 检查当前站点是否被排除
                const button = document.querySelector('#bangumiJumpButton');
                if (button) {
                    if (shouldShowButton()) {
                        button.style.display = 'block';
                        setTimeout(() => { button.style.opacity = '1'; }, 10);
                    } else {
                        button.style.opacity = '0';
                        setTimeout(() => { button.style.display = 'none'; }, 300);
                    }
                }
            }
        });
    }

    // 判断是否显示按钮
    function shouldShowButton() {
        // 如果全局关闭按钮或当前站点被排除
        if (!config.showButton || config.excludedSites.includes(window.location.hostname)) {
            return false;
        }

        const path = window.location.pathname;
        return (
            (window.location.hostname === 'www.mutean.com' && path.includes('/vodplay')) ||
            (window.location.hostname === 'www.aafun.cc' && path.includes('/f')) ||
            (window.location.hostname === 'www.cycani.org' && path.includes('/watch')) ||
            (window.location.hostname === 'www.agedm.io' && path.includes('/play')) ||
            (window.location.hostname === 'www.ntdm8.com' && path.includes('/play')) ||
            (window.location.hostname === 'www.mwcy.net' && path.includes('/play')) ||
            (window.location.hostname === 'dm.xifanacg.com' && path.includes('/watch')) ||
            (window.location.hostname === 'www.bilibili.com' && path.includes('/play')) ||
            (window.location.hostname === 'anich.emmmm.eu.org' && path.includes('/b')) ||
            (window.location.hostname === 'www.gugu3.com' && path.includes('/bindex.php/vod/play/id')) ||
            !!getAnimeTitle()
        );
    }

    // 获取动画标题
    function getAnimeTitle() {
        const domain = window.location.hostname;
        const selector = siteSelectors[domain];

        if (!selector) {
            console.warn(`当前网站 ${domain} 未配置标题选择器`);
            return null;
        }

        const titleElement = document.querySelector(selector);
        if (titleElement) {
            let title = titleElement.textContent.trim();
            title = title.replace(/^【.+?】/, '')
                .replace(/^《|》$/g, '')
                .replace(/^"|"$/g, '')
                .trim();
            return title;
        }
        return null;
    }

    // 使用Bangumi API获取动画ID
    function getBangumiId(title) {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: "GET",
                url: `https://api.bgm.tv/search/subject/${encodeURIComponent(title)}?type=2&responseGroup=small`,
                headers: {
                    "User-Agent": "Mozilla/5.0 (BangumiScript)"
                },
                onload: function(response) {
                    try {
                        const data = JSON.parse(response.responseText);
                        if (data.list && data.list.length > 0) {
                            resolve(data.list[0].id);
                        } else {
                            reject('未找到匹配的动画');
                        }
                    } catch (e) {
                        reject('解析API响应失败');
                    }
                },
                onerror: function(error) {
                    reject('API请求失败');
                }
            });
        });
    }

    // 跳转到Bangumi详情页
    async function jumpToBangumi() {
        const animeTitle = getAnimeTitle();
        if (!animeTitle) {
            alert('无法获取动画标题');
            return;
        }

        try {
            const subjectId = await getBangumiId(animeTitle);
            window.open(`https://bgm.tv/subject/${subjectId}`, '_blank');
            console.log(`跳转到Bangumi详情页: ${animeTitle}`);
        } catch (error) {
            console.warn(`直接跳转失败: ${error}, 改用搜索页`);
            const encodedTitle = encodeURIComponent(animeTitle);
            window.open(`https://bgm.tv/subject_search/${encodedTitle}?cat=2`, '_blank');
        }
    }

    // 跳转到Bangumi搜索页(右击功能)
    function jumpToBangumiSearch() {
        const animeTitle = getAnimeTitle();
        if (!animeTitle) {
            alert('无法获取动画标题');
            return;
        }
        const encodedTitle = encodeURIComponent(animeTitle);
        window.open(`https://bgm.tv/subject_search/${encodedTitle}?cat=2`, '_blank');
        console.log(`跳转到Bangumi搜索页: ${animeTitle}`);
    }

    // 创建右下角按钮
    function createJumpButton() {
        if (!shouldShowButton()) return;

        const button = document.createElement('button');
        button.id = 'bangumiJumpButton';
        button.textContent = '查看详情';
        button.style.position = 'fixed';
        button.style.bottom = '20px';
        button.style.right = '20px';
        button.style.zIndex = '9999';
        button.style.padding = '8px 16px';
        button.style.backgroundColor = '#1E88E5';
        button.style.color = 'white';
        button.style.border = 'none';
        button.style.borderRadius = '4px';
        button.style.cursor = 'pointer';
        button.style.fontSize = '14px';
        button.style.fontWeight = '500';
        button.style.boxShadow = '0 2px 10px rgba(0,0,0,0.2)';
        button.style.transition = 'all 0.3s ease';
        button.style.opacity = config.showButton ? '1' : '0';
        button.style.display = config.showButton ? 'block' : 'none';

        // 添加title属性用于悬停提示
        button.title = '搜索错误时右击';

        // 悬停效果
        button.addEventListener('mouseover', () => {
            button.style.backgroundColor = '#1565C0';
            button.style.transform = 'translateY(-2px)';
        });
        button.addEventListener('mouseout', () => {
            button.style.backgroundColor = '#1E88E5';
            button.style.transform = 'translateY(0)';
        });

        // 左键点击事件
        button.addEventListener('click', jumpToBangumi);

        // 右键点击事件
        button.addEventListener('contextmenu', (e) => {
            e.preventDefault();
            jumpToBangumiSearch();
        });

        document.body.appendChild(button);
    }

    // 键盘事件处理
    function handleKeyPress(e) {
        const isShiftF8 = (e.key === 'F8' && e.shiftKey) ||
              (e.keyCode === 119 && e.shiftKey);
        if (isShiftF8) {
            e.preventDefault();
            e.stopPropagation();
            jumpToBangumi();
            return false;
        }
    }

    // 添加键盘监听
    function addKeyListener() {
        document.addEventListener('keydown', handleKeyPress);
        window.addEventListener('keydown', handleKeyPress);
    }

    // 主函数
    function main() {
        addKeyListener();
        if (shouldShowButton()) {
            createJumpButton();
        }
        console.log('Bangumi跳转脚本已加载',
                    config.showButton ? '按钮已启用' : '按钮已禁用');
    }

    // 页面加载完成后初始化
    if (document.readyState === 'complete') {
        main();
    } else {
        window.addEventListener('load', main);
    }
})();

// ========== Bangumi 播放源扩展模块 ==========
(function() {
    'use strict';
    if (!/bgm\.tv\/subject\/\d+/.test(location.href)) return;

    // --- 搜索引擎列表 ---
    const searchEngines = [
        { name: "次元城动画", url: "https://www.cycani.org/search.html?wd=${name}" },
        { name: "稀饭动漫", url: "https://dm.xifanacg.com/search.html?wd=${name}" },
        { name: "MuteFun", url: "https://www.mutean.com/vodsearch/${name}-------------.html" },
        { name: "咕咕番", url: "https://www.gugu3.com/index.php/vod/search.html?wd=${name}" },
        { name: "NT动漫", url: "http://www.ntdm8.com/search/-------------.html?wd=${name}&page=1" },
        { name: "风铃动漫", url: "https://www.aafun.cc/feng-s.html?wd=${name}" },
        { name: "喵物次元", url: "https://www.mwcy.net/search.html?wd=${name}" },
        { name: "Bilibili", url: "https://search.bilibili.com/bangumi?keyword=${name}&from_source=webtop_search&spm_id_from=666.4&search_source=5" }
    ];

    // --- 从 infobox 获取动画中文名 ---
    function getAnimeName() {
        const li = document.querySelector('#infobox li');
        if (!li) return null;
        const text = li.textContent.replace(/^中文名[::]\s*/, '').trim();
        return text || null;
    }

    // --- 创建按钮与菜单 ---
    function createPlaySourceButton() {
        const shareDiv = document.querySelector('.shareBtn');
        if (!shareDiv) return;

        const container = document.createElement('span');
        container.className = 'action play-source-action';
        container.style.position = 'relative';

        const button = document.createElement('a');
        button.href = 'javascript:void(0);';
        button.className = 'icon play_source_btn bve-processed';
        button.title = '查看播放源';
        button.innerHTML = `
              <span class="ico_play" style="margin-right:4px;">&#9654;</span>
              <span class="title">播放源</span>
        `;

        // 下拉菜单
        const menu = document.createElement('div');
        menu.className = 'play-source-menu';
        Object.assign(menu.style, {
            display: 'none',
            position: 'absolute',
            top: '30px',
            left: '0',
            background: '#fafafa',
            border: '1px solid #ccc',
            borderRadius: '6px',
            boxShadow: '0 3px 8px rgba(0,0,0,0.2)',
            zIndex: '9999',
            maxHeight: '220px',
            overflowY: 'auto',
            width: '160px',
            fontSize: '13px',
            padding: '4px 0',
            color: '#333'
        });

        searchEngines.forEach(engine => {
            const item = document.createElement('div');
            item.textContent = engine.name;
            Object.assign(item.style, {
                padding: '8px 12px',
                cursor: 'pointer',
                userSelect: 'none'
            });
            item.addEventListener('mouseover', () => {
                item.style.background = '#e6f0ff';
                item.style.color = '#1E63D0';
            });
            item.addEventListener('mouseout', () => {
                item.style.background = '';
                item.style.color = '#333';
            });
            item.addEventListener('click', () => {
                const animeName = getAnimeName();
                if (!animeName) {
                    alert('无法获取动画名称');
                    return;
                }
                const url = engine.url.replace('${name}', encodeURIComponent(animeName));
                window.open(url, '_blank');
                menu.style.display = 'none';
            });
            menu.appendChild(item);
        });

        // 点击按钮展开或关闭菜单
        button.addEventListener('click', e => {
            e.stopPropagation();
            menu.style.display = menu.style.display === 'block' ? 'none' : 'block';
        });

        // 点击外部隐藏菜单
        document.addEventListener('click', () => {
            menu.style.display = 'none';
        });

        container.appendChild(button);
        container.appendChild(menu);
        shareDiv.appendChild(container);
    }

    // --- 初始化 ---
    function init() {
        const shareDiv = document.querySelector('.shareBtn');
        if (shareDiv) createPlaySourceButton();
        else {
            const observer = new MutationObserver((mutations, obs) => {
                if (document.querySelector('.shareBtn')) {
                    createPlaySourceButton();
                    obs.disconnect();
                }
            });
            observer.observe(document.body, { childList: true, subtree: true });
        }
    }

    window.addEventListener('load', init);
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址