Greasy Fork is available in English.

dgj's bilibili ads guard

remove all ads from bilibili.com

// ==UserScript==
// @name         dgj's bilibili ads guard
// @namespace    http://tampermonkey.net/
// @version      2024-06-18
// @description  remove all ads from bilibili.com
// @author       noobdawn
// @match        https://www.bilibili.com/
// @icon         https://www.google.com/s2/favicons?sz=64&domain=bilibili.com
// @grant        GM_xmlhttpRequest
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    var blockData = localStorage.getItem("dgjBlockData");
    //if (blockData === null)
    {
        blockData = {
            "up_name": "",
            // up主的名字

            "up_uid": "",
            // up主的UID

            "video_keyword": 
                // sb游戏
                "剑网3,原神,崩坏,星穹铁道,星铁,绝区,未定事件簿,米哈游,仙家军,明日方舟,鹰角,海猫,鸣潮,战双帕弥什,库洛,少前,少女前线,面包房少女,散爆,羽中,无期迷途,叠纸,奇迹暖暖,无限暖暖,恋与深空,恋与制作人,百面千相,姚润昊,深空之眼,来古弥新,物华弥新,新月同行,归龙潮,破晓序列,卡拉彼丘,碧蓝档案,蔚蓝档案,雀魂,以闪亮之名,光与夜之恋,尘白禁区",
            // 视频的关键词

            "video_label": 
                // sb游戏 
                "鸣潮,鸣潮公测二创,鸣潮公测创作者激励计划,新三国,火影忍者手游,地下城与勇士,dnf,dnfpk,300英雄,MOBA,三百英雄,剑网3,星穹铁道2.2波提欧,暴雪,原神,崩坏,崩坏学园2,崩坏3,崩坏星穹铁道,星穹铁道,绝区零,绝区0,未定事件簿,米哈游,仙家军,明日方舟,鹰角,鹰角网络,海猫,鸣潮,战双帕弥什,库洛,少前,少前2,少女前线,少女前线2,面包房少女,散爆,羽中,叠纸游戏,奇迹暖暖,无限暖暖,恋与深空,恋与制作人,百面千相,姚润昊,深空之眼,来古弥新,物华弥新,新月同行,归龙潮,望月,破晓序列,卡拉彼丘,碧蓝档案,蔚蓝档案,雀魂,以闪亮之名,光与夜之恋,尘白禁区" +
                // 没营养的东西
                "助眠,生活,情感,日常,校园,娱乐,记录,舞蹈,Vtuber,hololive,免单挑战,探店,美食,时尚,穿搭"
            // 视频的标签
            
        };
        blockData.up_name = blockData.up_name.split(",");
        blockData.video_keyword = blockData.video_keyword.split(",");
        blockData.video_label = blockData.video_label.split(",");
        blockData.up_uid = blockData.up_uid.split(",");
        localStorage.setItem("dgjBlockData", JSON.stringify(blockData));
    }

    const DEBUG = true;

    function removeAds(node, msg) {
        if (DEBUG)
        {
            // 删除所有子元素
            while (node.firstChild) {
                node.removeChild(node.firstChild);
            }
            // 添加一个文本节点
            node.appendChild(document.createTextNode(msg));
        }
        else
            node.remove();
    }

    // 从URL中获取UID
    function GetUidFromUrl(url) {
        var index = url.indexOf("space.bilibili.com/");
        if (index === -1)
            return -1;
        // 将剩下的所有字符转换为字符串
        var uid = url.substring(index + 19);
        return parseInt(uid);
    }

    // 根据UP主屏蔽视频
    function BlockVideoByUp(node) {
        // 向下查找,找到class为"bili-video-card__info--owner"的a,这就是UP主的链接
        var owner = node.getElementsByClassName("bili-video-card__info--owner");
        if (owner.length > 0) {
            var uid = GetUidFromUrl(owner[0].href);
            if (uid === -1)
                return true;
            // 如果uid在blockData的up_uid数组中,就删除这个视频
            if (blockData.up_uid.indexOf(uid) !== -1)
            {
                removeAds(node, "触发UP主:" + uid);
                return true;
            }
            owner = node.getElementsByClassName("bili-video-card__info--author");
            if (owner.length > 0) {
                var name = owner[0].innerText;
                // 如果name在blockData的up_name数组中,就删除这个视频
                if (blockData.up_name.indexOf(name) !== -1)
                {
                    removeAds(node, "触发UP主:" + name);
                    return true;
                }
            }
            return false;
        }
        return true;
    }

    // 根据视频屏蔽视频
    function BlockVideoByVideo(node) {
        var link = node.getElementsByClassName("bili-video-card__info--tit");
        if (link.length > 0) {
            console.log(link[0].title);
            var title = link[0].title;
            for (var j = 0; j < blockData.video_keyword.length; j++){
                if (title.includes(blockData.video_keyword[j])) {
                    removeAds(node, "触发关键字:" + blockData.video_keyword[j]);
                    return;
                }
            }
        }
        // 向下查找,找到class为"bili-video-card__image--link"的a,这就是视频的链接
        link = node.getElementsByClassName("bili-video-card__image--link");
        if (link.length > 0) {
            var url = link[0].href;
            // 打开视频链接,获取视频的关键词和标签
            GM_xmlhttpRequest({
                method: "GET",
                url: url,
                onload: function(response) {
                    var data = response.responseText;
                    // 找到<meta data-vue-meta="true" itemprop="keywords" name="keywords" content="
                    let start_string = "<meta data-vue-meta=\"true\" itemprop=\"keywords\" name=\"keywords\" content=\"";
                    let end_string = "\"><meta";
                    var index = data.indexOf(start_string);
                    if (index !== -1)
                    {
                        var keywords = data.substring(index + start_string.length, data.indexOf(end_string, index));
                        var keywordArray = keywords.split(",");
                        // 抛弃第一个元素
                        keywordArray.shift();
                        console.log(keywordArray);
                        // 如果labels数组中有一个元素在blockData的video_label数组中,就删除这个视频
                        for (i = 0; i < keywordArray.length; i++)
                        {
                            if (blockData.video_label.indexOf(keywordArray[i]) !== -1)
                            {
                                removeAds(node, "触发标签:" + keywordArray[i]);
                                return;
                            }
                        }
                    }
                }
            });
        }
    }

    // 每次页面元素变化时,都会触发此函数
    var observer = new MutationObserver(function(mutations) {
        // 遍历所有变化
        mutations.forEach(function(mutation) {
            mutation.addedNodes.forEach(function(node) {
                if (node instanceof HTMLDivElement) {
                    // 为菜单新增一个按钮
                    if (node.className === "bili-video-card__info--no-interest-panel")
                    {
                        // 创建一个新的选项,class为"bili-video-card__info--no-interest-panel--item",innerText为"屏蔽此UP主"
                        var item = document.createElement("div");
                        item.className = "bili-video-card__info--no-interest-panel--item";
                        item.innerText = "屏蔽此UP主";
                        // 将新的选项插入到菜单中
                        node.appendChild(item);
                        // 监听点击事件
                        item.addEventListener("click", function() {
                            // todo
                        });
                    }

                    // 如果新增的节点的class为"floor-single-card",这部分一般是直播间、番剧、国创、课堂、综艺推荐卡片,干掉
                    if (node.className === "floor-single-card") {
                        removeAds(node, "Ban原因:推送");
                        return;
                    }
                    // 如果新增的节点的class为"bili-video-card is-rcmd",这部分一般是不能点不感兴趣的卡片,直接干掉
                    if (node.className === "bili-video-card is-rcmd") {
                        removeAds(node, "Ban原因:广告");
                        return;
                    }
                    if (node.className === "bili-video-card is-rcmd enable-no-interest")
                    {
                        node.style.marginTop = '22px';
                    }
                    
                    if (BlockVideoByUp(node) !== true)
                        BlockVideoByVideo(node);
                }
            });
        });
    });

    // 监听整个文档
    observer.observe(document, {
        childList: true,
        subtree: true
    });

    // 移除滚动播放广告
    // 找到所有class为"recommended-swipe grid-anchor"的div,这就是左上角的广告滚动页
    var ads = document.getElementsByClassName("recommended-swipe grid-anchor");
    for (var i = 0; i < ads.length; i++) {
        removeAds(ads[i], "Ban原因:广告滚动页");
    }

    // 找到所有首页的广告
    // 找到所有class为"bili-video-card is-rcmd"的div
    ads = document.getElementsByClassName("bili-video-card is-rcmd");
    for (i = 0; i < ads.length; i++) {
        if (ads[i].className === "bili-video-card is-rcmd")
        {
            if (ads[i].parentNode.className === "feed-card")
                removeAds(ads[i].parentNode, "Ban原因:广告");
            else
                removeAds(ads[i], "Ban原因:广告");
        }
    }

    ads = document.getElementsByClassName("feed-card");
    for (i = 0; i < ads.length; i++) {
        // 将样式中的margin-top设为40像素
        ads[i].style.marginTop = '22px';
        if (BlockVideoByUp(ads[i]) !== true)
            BlockVideoByVideo(ads[i]);
    }


})();