// ==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]);
}
})();