B站首页和视频页一键拉黑UP主

在B站首页和视频页添加拉黑按钮,一键拉黑UP主。

// ==UserScript==
// @name         B站首页和视频页一键拉黑UP主
// @description  在B站首页和视频页添加拉黑按钮,一键拉黑UP主。
// @match        https://bilibili.com/
// @match        https://www.bilibili.com/*
// @match        https://www.bilibili.com/video/*
// @match        https://www.bilibili.com/video/*
// @icon         https://www.bilibili.com/favicon.ico
// @version      1.0.6
// @grant        GM_registerMenuCommand
// @grant        GM_unregisterMenuCommand
// @grant        GM_addStyle
// @namespace    https://github.com/codertesla/bilibili-1-click-blocker
// @author       codertesla
// @supportURL   https://github.com/codertesla/bilibili-1-click-blocker
// @homepageURL  https://github.com/codertesla/bilibili-1-click-blocker
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    // 调试功能
    const DEBUG = true;
    function log(...args) {
        if (DEBUG) {
            console.log('[拉黑脚本]', ...args);
        }
    }

    // 等待jQuery加载
    function waitForJQuery(callback) {
        if (typeof jQuery !== 'undefined') {
            log('jQuery已加载');
            callback(jQuery);
        } else {
            log('等待jQuery加载...');
            setTimeout(function () { waitForJQuery(callback); }, 50);
        }
    }

    // 主函数
    waitForJQuery(function ($) {
        log('脚本初始化开始');

        // 添加样式
        if (typeof GM_addStyle !== 'undefined') {
            GM_addStyle(`
        .bilibili-blacklist-btn {
          color: #fb7299 !important;
          cursor: pointer !important;
          font-weight: normal !important;
          margin-right: 8px !important;
          display: inline-flex !important;
          align-items: center !important;
          justify-content: center !important;
          padding: 1px 5px !important;
          border: 1px solid #fb7299 !important;
          border-radius: 4px !important;
          font-size: 11px !important;
          transition: all 0.2s ease !important;
          background-color: white !important;
          box-shadow: 0 0 2px rgba(251, 114, 153, 0.2) !important;
          width: auto !important;
          min-width: unset !important;
          max-width: none !important;
          box-sizing: border-box !important;
          text-align: center !important;
          white-space: nowrap !important;
          /* 确保内容均匀分布 */
          gap: 1px !important;
        }
        .bilibili-blacklist-btn:hover {
          background-color: #fb7299 !important;
          color: white !important;
          box-shadow: 0 0 4px rgba(251, 114, 153, 0.4) !important;
        }
        .bilibili-blacklist-btn:active {
          transform: scale(0.95) !important;
        }
        .bilibili-blacklist-btn::before {
          /* 移除或注释掉原有的 content 属性 */
          /* content: "\\e63c"; */
          font-family: "Arial" !important; /* 更改字体为常规字体,如果使用其他图标字体库则需要修改 */
          margin-right: 4px !important; /* 保持间隔 */
          font-size: 11px !important;
          display: inline-block !important;
          text-align: center !important;
        }
        /* 按钮动画效果 */
        @keyframes fadeIn {
          from { opacity: 0; transform: translateY(5px); }
          to { opacity: 1; transform: translateY(0); }
        }
        .bilibili-blacklist-btn {
          animation: fadeIn 0.3s ease-out !important;
        }
        /* 优化视频页按钮样式 */
        .video-info-detail .bilibili-blacklist-btn {
          margin-left: 5px !important;
          vertical-align: middle !important;
          min-width: unset !important;
          max-width: none !important;
          font-size: 10px !important;
          padding: 0px 4px !important;
          white-space: nowrap !important;
        }
        .video-info-detail .bilibili-blacklist-btn::before {
          font-size: 10px !important;
          width: 10px !important;
        }
        /* 视频卡片上更紧凑的按钮样式 */
        .blacklist-button-container .bilibili-blacklist-btn {
          padding: 0px 4px !important;
          font-size: 10px !important;
          min-width: unset !important;
          max-width: none !important;
          white-space: nowrap !important;
        }
        .blacklist-button-container .bilibili-blacklist-btn::before {
          font-size: 10px !important;
          margin-right: 0 !important;
          width: 10px !important;
        }
        /* 在卡片上的悬浮样式 */
        .blacklist-button-container {
          position: absolute !important;
          top: 5px !important;
          right: 5px !important;
          z-index: 100 !important;
          opacity: 0 !important;
          transition: opacity 0.2s ease !important;
        }
        .bili-video-card:hover .blacklist-button-container,
        .video-card:hover .blacklist-button-container,
        .feed-card:hover .blacklist-button-container {
          opacity: 1 !important;
        }
        /* 自定义提示框样式 */
        .bili-blacklist-toast {
          position: fixed !important;
          top: 20px !important;
          left: 50% !important;
          transform: translateX(-50%) !important;
          background-color: rgba(0, 0, 0, 0.7) !important;
          color: white !important;
          padding: 10px 20px !important;
          border-radius: 4px !important;
          z-index: 9999 !important;
          font-size: 14px !important;
          box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2) !important;
          display: flex !important;
          align-items: center !important;
          min-width: 200px !important;
          max-width: 80% !important;
          box-sizing: border-box !important;
          animation: toastIn 0.3s ease-out !important;
        }
        @keyframes toastIn {
          from { opacity: 0; transform: translate(-50%, -20px); }
          to { opacity: 1; transform: translate(-50%, 0); }
        }
        .bili-blacklist-toast-icon {
          color: #fb7299 !important;
          margin-right: 10px !important;
          font-size: 18px !important;
          display: flex !important;
          align-items: center !important;
          justify-content: center !important;
        }
        .bili-blacklist-toast-content {
          flex-grow: 1 !important;
          display: flex !important;
          align-items: center !important;
        }
        /* 紧凑型按钮样式 */
        .bilibili-blacklist-btn-compact {
          padding: 0px 4px !important;
          font-size: 10px !important;
          min-width: unset !important;
          max-width: none !important;
          white-space: nowrap !important;
        }
        .bilibili-blacklist-btn-compact::before {
          font-size: 10px !important;
          margin-right: 0 !important;
          width: 10px !important;
        }
      `);
        }

        // Cookie获取函数
        function getCookie(name) {
            const value = `; ${document.cookie}`;
            const parts = value.split(`; ${name}=`);
            if (parts.length === 2) return parts.pop().split(';').shift();
            return '';
        }

        // 检查是否登录(不可用)
        const csrf = getCookie('bili_jct');
        if (!csrf) {
            log('警告: 未获取到bili_jct Cookie,可能未登录(不可用)');
            showToast('B站拉黑脚本提示:请先登录(不可用)B站账号!', 5000);
        } else {
            log('成功获取CSRF token');
        }

        // 菜单控制
        const menuctl = ({ initValue = 0 }) => {
            let total = initValue;
            let menuId = null;

            const currentName = () => "去管理黑名单 --( " + (total < 1 ? "请留意黑名单数量 )" : `总共:${total} )`);

            const register = () => {
                try {
                    menuId = GM_registerMenuCommand(currentName(), () => {
                        window.open('https://account.bilibili.com/account/blacklist', '_blank');
                    });
                    log('注册(不可用)菜单成功: ', currentName());
                } catch (e) {
                    log('注册(不可用)菜单失败: ', e);
                }
            };

            register();

            const ctl = {
                get total() { return total; },
                set total(newValue) {
                    if (newValue == total) return;
                    if (menuId !== null) {
                        try {
                            GM_unregisterMenuCommand(menuId);
                            log('解除注册(不可用)旧菜单');
                        } catch (e) {
                            log('解除注册(不可用)旧菜单失败: ', e);
                        }
                    }
                    total = newValue;
                    register();
                },
            };
            return ctl;
        };

        const menu = menuctl({ initValue: 0 });

        // 显示自定义提示框
        function showToast(message, duration = 3000) {
            // 移除已有的提示框
            $('.bili-blacklist-toast').remove();

            // 创建新的提示框
            const toast = $(`
                <div class="bili-blacklist-toast">
                    <span class="bili-blacklist-toast-icon">✓</span>
                    <div class="bili-blacklist-toast-content">${message}</div>
                </div>
            `);

            // 添加到页面
            $('body').append(toast);

            // 设置自动关闭
            setTimeout(() => {
                toast.css({
                    'opacity': '0',
                    'transform': 'translate(-50%, -20px)'
                });
                setTimeout(() => toast.remove(), 300);
            }, duration);
        }

        // 拉黑功能
        window.tools_toblack = (uid, upName) => {
            log('执行拉黑操作,UID:', uid, '名称:', upName);

            // 用于视频页面的提示
            const isVideoPage = window.location.href.includes('/video/');

            fetch("https://api.bilibili.com/x/relation/modify", {
                method: "POST",
                credentials: 'include',
                headers: {
                    "Content-Type": "application/x-www-form-urlencoded",
                },
                body: new URLSearchParams({
                    'fid': uid,
                    'act': 5,
                    're_src': 11,
                    'gaia_source': 'web_main',
                    'csrf': getCookie('bili_jct'),
                })
            }).then(res => {
                if (!res.ok) {
                    throw new Error(`HTTP error! Status: ${res.status}`);
                }
                return res.json();
            }).then(data => {
                log('拉黑API响应:', data);
                if (data.code === 0) {
                    log('拉黑成功:', uid);

                    // 在首页移除对应元素
                    if (!isVideoPage) {
                        $('div.uid_' + uid).each(function () {
                            log('移除元素:', this);
                            $(this).fadeOut(300, function () {
                                $(this).remove();
                            });
                        });
                        showToast(`已成功将 "${upName || 'UP主'}" 加入黑名单`);
                    } else {
                        // 在视频页面显示拉黑成功提示
                        showToast(`已成功将 "${upName || 'UP主'}" 加入黑名单`);
                    }

                } else {
                    log('拉黑失败:', data.message || '未知错误');
                    showToast(`拉黑失败: ${data.message || '未知错误'}`);
                }
            }).catch(err => {
                log('拉黑请求错误:', err);
                showToast('拉黑请求失败,可能是网络问题或未登录(不可用)');
            });

            updateBlacklistCount();
        };

        // 更新黑名单计数
        function updateBlacklistCount() {
            fetch("https://api.bilibili.com/x/relation/blacks?re_version=0&pn=1&ps=20&jsonp=jsonp&web_location=333.33", {
                method: "GET",
                credentials: 'include',
            }).then(res => {
                if (!res.ok) {
                    throw new Error(`HTTP error! Status: ${res.status}`);
                }
                return res.json();
            }).then(data => {
                log('黑名单API响应:', data);
                if (data.code === 0) {
                    menu.total = data.data.total;
                    log('更新黑名单计数:', data.data.total);
                } else {
                    log('获取黑名单失败:', data.message || '未知错误');
                }
            }).catch(err => {
                log('获取黑名单请求错误:', err);
            });
        }

        // 处理首页
        function processHomePage() {
            log('处理首页');

            // 查找可能的视频卡片容器
            const possibleContainers = [
                '.bili-video-card',
                '.video-card',
                '.bili-video-card__wrap',
                '.feed-card'
            ];

            let found = false;

            possibleContainers.forEach(selector => {
                const cards = $(selector);
                if (cards.length > 0) {
                    log(`找到视频卡片 (${selector}):`, cards.length);
                    found = true;

                    cards.each((index, card) => {
                        // 检查是否已经处理过该卡片
                        if ($(card).data('toblack-processed') || $(card).find('.bilibili-blacklist-btn').length > 0) {
                            return;
                        }

                        // 尝试多种可能的UP主选择器
                        const possibleOwnerSelectors = [
                            '.bili-video-card__info--owner',
                            '.up-name',
                            '.bili-video-card__info--author',
                            '.author-text',
                            '.up-name__text'
                        ];

                        let ownerElement = null;
                        let ownerUrl = null;

                        for (const selector of possibleOwnerSelectors) {
                            const element = $(card).find(selector);
                            if (element.length > 0) {
                                ownerElement = element;
                                ownerUrl = element.attr('href');
                                log(`找到UP主元素 (${selector}):`, ownerUrl);
                                break;
                            }
                        }

                        if (!ownerUrl) {
                            $(card).data('toblack-processed', true);
                            return;
                        }

                        // 从URL中提取UID
                        let uid = '';
                        if (ownerUrl.includes('/space.bilibili.com/')) {
                            uid = ownerUrl.split('/space.bilibili.com/')[1].split('?')[0].split('/')[0];
                        } else if (ownerUrl.includes('/space/')) {
                            uid = ownerUrl.split('/space/')[1].split('?')[0].split('/')[0];
                        } else {
                            uid = ownerUrl.substr(ownerUrl.lastIndexOf('/') + 1).split('?')[0];
                        }

                        if (!uid) {
                            log('无法从URL提取UID:', ownerUrl);
                            return;
                        }

                        const upName = ownerElement.text().trim();
                        log('提取UID:', uid, '名称:', upName);

                        // 尝试找到适合放置按钮的位置
                        const possibleTargets = [
                            '.bili-video-card__info--bottom',
                            '.bili-video-card__info',
                            '.video-card__info',
                            '.info-bottom',
                            $(card) // 如果找不到特定元素,直接附加到卡片上
                        ];

                        let targetElement = null;

                        for (const target of possibleTargets) {
                            const element = typeof target === 'string' ? $(card).find(target) : target;
                            if (element.length > 0) {
                                targetElement = element;
                                break;
                            }
                        }

                        if (targetElement) {
                            // 创建按钮元素
                            const blackButton = $(`<a class="bilibili-blacklist-btn bilibili-blacklist-btn-compact" data-uid="${uid}">拉黑</a>`);
                            blackButton.on('click', function (e) {
                                e.preventDefault();
                                e.stopPropagation();
                                window.tools_toblack(uid, upName);
                            });

                            // 插入按钮
                            if (targetElement === $(card)) {
                                // 如果是卡片本身,创建一个容器
                                const container = $('<div class="blacklist-button-container"></div>');
                                container.append(blackButton);
                                targetElement.css('position', 'relative').append(container);
                            } else {
                                // 常规情况,前置到目标元素
                                targetElement.prepend(blackButton);
                            }

                            $(card).data('toblack-processed', true).addClass('uid_' + uid);
                            log('已添加拉黑按钮到卡片:', index);
                        }
                    });
                }
            });

            if (!found) {
                log('未找到任何视频卡片,可能DOM结构已变更');
            }
        }

        // 处理视频页面
        function processVideoPage() {
            log('处理视频页面');

            // 查找UP主信息区域
            function findAndProcessUpInfo() {
                // 可能的UP主信息选择器 - 结合原有选择器和优化后的选择器
                const possibleUpSelectors = [
                    // 原有选择器(这些是有效的)
                    '.upname .name',
                    '.upname a',
                    '.up-name',
                    '.up-info_right',
                    '.up-info .name',
                    '.user-name',
                    '.username',
                    'a[href*="/space.bilibili.com/"]',
                    'a[href*="/space/"]',
                    // 更精确的选择器(作为补充)
                    '.up-info .upname .name',
                    '.up-info .upname a',
                    '.video-info-detail .up-info .name',
                    '.video-info-detail a[href*="/space.bilibili.com/"]'
                ];

                for (const selector of possibleUpSelectors) {
                    const upElements = $(selector);

                    if (upElements.length > 0) {
                        log(`找到视频页UP主元素 (${selector}):`, upElements.length);

                        upElements.each(function () {
                            const upElement = $(this);

                            // 如果已处理过,跳过
                            if (upElement.data('toblack-processed')) {
                                return;
                            }

                            // 获取UP主URL和名称
                            let upUrl = upElement.attr('href');
                            let upName = upElement.text().trim();

                            // 如果没有href属性(不是链接),尝试查找父级或子级的链接
                            if (!upUrl) {
                                const parentLink = upElement.closest('a[href*="/space"]');
                                const childLink = upElement.find('a[href*="/space"]');

                                if (parentLink.length > 0) {
                                    upUrl = parentLink.attr('href');
                                    if (!upName) upName = parentLink.text().trim();
                                } else if (childLink.length > 0) {
                                    upUrl = childLink.attr('href');
                                    if (!upName) upName = childLink.text().trim();
                                }
                            }

                            if (!upUrl) {
                                log('未找到UP主链接');
                                return;
                            }

                            // 从URL中提取UID
                            let uid = '';
                            if (upUrl.includes('/space.bilibili.com/')) {
                                uid = upUrl.split('/space.bilibili.com/')[1].split('?')[0].split('/')[0];
                            } else if (upUrl.includes('/space/')) {
                                uid = upUrl.split('/space/')[1].split('?')[0].split('/')[0];
                            } else {
                                uid = upUrl.substr(upUrl.lastIndexOf('/') + 1).split('?')[0];
                            }

                            if (!uid) {
                                log('无法从URL提取UID:', upUrl);
                                return;
                            }

                            log('提取视频页UID:', uid, '名称:', upName);

                            // 创建拉黑按钮
                            const blackButton = $(`<a class="bilibili-blacklist-btn bilibili-blacklist-btn-compact" data-uid="${uid}">拉黑</a>`);
                            blackButton.on('click', function (e) {
                                e.preventDefault();
                                e.stopPropagation();
                                window.tools_toblack(uid, upName);
                            });

                            // 添加按钮到UP主名称旁边
                            if (upElement.hasClass('name') && upElement.closest('.upname').length > 0) {
                                // 针对新的HTML结构特殊处理
                                const upnameDiv = upElement.closest('.upname');
                                // 将按钮添加到包含UP主名称的span后面
                                upElement.after(blackButton);
                            } else if (upElement.find('.name').length > 0) {
                                // 如果选中的是包含.name的元素
                                upElement.find('.name').after(blackButton);
                            } else {
                                upElement.after(blackButton);
                            }
                            upElement.data('toblack-processed', true);
                            log('已添加拉黑按钮到视频页UP主:', upName);
                        });

                        return true;
                    }
                }

                log('未找到视频页UP主信息');
                return false;
            }

            // 首次尝试
            if (!findAndProcessUpInfo()) {
                // 视频页面可能是动态加载的,需要多次重试
                log('未找到UP主信息,将在稍后重试');

                // 添加多次重试的逻辑
                let retryCount = 0;
                const maxRetries = 5;
                const retryInterval = 800; // 毫秒

                const retryTimer = setInterval(() => {
                    retryCount++;
                    log(`第${retryCount}次重试寻找UP主信息...`);

                    if (findAndProcessUpInfo() || retryCount >= maxRetries) {
                        clearInterval(retryTimer);
                        if (retryCount >= maxRetries && !findAndProcessUpInfo()) {
                            log('在多次尝试后仍未找到UP主信息');
                        }
                    }
                }, retryInterval);
            }
        }

        // 统一处理入口
        function processPage() {
            // 添加节流,避免频繁处理
            if (window.processingPage) {
                log('页面正在处理中,跳过');
                return;
            }

            window.processingPage = true;
            setTimeout(() => { window.processingPage = false; }, 1000);

            const isVideoPage = window.location.href.includes('/video/');

            if (isVideoPage) {
                log('检测到视频页面');
                processVideoPage();
            } else {
                log('检测到首页');
                processHomePage();
            }
        }

        // 设置DOM观察器
        function setupObserver() {
            // 如果已经设置了观察器,则不重复设置
            if (window.blacklistObserverSet) {
                log('观察器已存在,跳过设置');
                return;
            }

            log('设置DOM观察器');

            const observer = new MutationObserver(function (mutations) {
                let needsUpdate = false;

                mutations.forEach(mutation => {
                    if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                        needsUpdate = true;
                    }
                });

                if (needsUpdate) {
                    log('检测到DOM变更,更新拉黑按钮');
                    processPage();
                }
            });

            // 观察主要内容区域而不是整个body
            const isVideoPage = window.location.href.includes('/video/');
            let targetNode;

            if (isVideoPage) {
                // 视频页面选择主要内容区域,但避开导航栏
                targetNode = document.querySelector('#app #viewbox_report') || // UP主信息通常在这里
                    document.querySelector('#app .video-info-box') ||
                    document.querySelector('#app .video-info-detail') ||
                    document.querySelector('#app .video-container-v1') ||
                    document.querySelector('#bilibiliPlayer') ||
                    document.body; // 如果以上都不存在,则降级为body
            } else {
                // 首页选择主要内容区域
                targetNode = document.querySelector('#app .bili-layout-main') ||
                    document.querySelector('#app .bili-grid') ||
                    document.querySelector('#app .feed-card') ||
                    document.body; // 如果以上都不存在,则降级为body
            }

            log('选择观察节点:', targetNode);

            observer.observe(targetNode, {
                childList: true,
                subtree: true
            });

            // 标记观察器已设置
            window.blacklistObserverSet = true;

            log('DOM观察器已启动');

            // 初始加载时检查
            setTimeout(processPage, 1000);

            // 页面可能是动态加载的,定期检查,但减少频率
            window.blacklistInterval = setInterval(processPage, 5000);
        }

        // 暴露给全局作用域,方便调试
        window.blacklistScript = {
            processPage: processPage,
            processHomePage: processHomePage,
            processVideoPage: processVideoPage
        };

        // 在页面就绪后启动
        $(document).ready(function () {
            log('页面就绪,初始化脚本');
            setupObserver();
            updateBlacklistCount();
        });

        // 额外的保障,防止document.ready不触发
        setTimeout(function () {
            log('延时检查初始化');
            if (!$._data(document, 'events') || !$._data(document, 'events').ready) {
                log('未检测到document.ready事件,手动初始化');
                setupObserver();
                updateBlacklistCount();
            }
        }, 2000);

        log('脚本初始化完成');
    });
})();

QingJ © 2025

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