Block some video cards recommended on the homepage of Bilibili. The rules are to block those from creators with a certain number of small fans, block live streams and right-hand promotion, and block those with advertising tags.
// ==UserScript== // @name 屏蔽B站营销视频和推广视频 // @name:zh-CN 屏蔽B站营销视频和推广视频 // @name:zh-TW 屏蔽B站营销视频和推广视频 // @name:en Block Bilibili's marketing videos and promotional videos // @namespace http://tampermonkey.net/ // @version 2.4 // @description 屏蔽部分B站(bilibili)主页推荐的视频卡片,屏蔽up主粉丝少于一定数量的,屏蔽直播与右侧推广,屏蔽带广告标签的 // @description:zh-CN 屏蔽部分B站(bilibili)主页推荐的视频卡片,屏蔽up主粉丝少于一定数量的,屏蔽直播与右侧推广,屏蔽带广告标签的 // @description:zh-TW 遮罩部分B站(bilibili)主頁推薦的視頻卡片,遮罩up主粉絲少於一定數量的,遮罩直播與右側推廣,遮罩帶廣告標籤的 // @description:en Block some video cards recommended on the homepage of Bilibili. The rules are to block those from creators with a certain number of small fans, block live streams and right-hand promotion, and block those with advertising tags. // @author anonymous // @match https://www.bilibili.com/ // @match https://www.bilibili.com/?spm_id_from=* // @icon https://www.bilibili.com/favicon.ico // @grant none // @license GNU General Public License v3.0 // ==/UserScript== //async functions' is only available in ESS (use 'esversion: 8'). (function () { 'use strict'; // 定义需要屏蔽的两种视频卡片类名 const BLOCKED_CLASSES = ['floor-single-card', 'bili-live-card is-rcmd']; // 定义需要屏蔽的最小的follower数 const MIN_FOLLOWER = 2000; // 定义接口前缀 const API_USERDATA = 'https://api.bilibili.com/x/relation/stat?vmid='; // 定义已处理卡片数量 let processedCards = 0; function getUid(card) { // 传入一个视频卡片,获取其中的uid并转化为数字并返回 const ownerLink = card.querySelector('.bili-video-card__info--owner'); if (ownerLink) { const uid = ownerLink.href.split('/').pop(); if (uid.match(/^\d+$/)) { return Number(uid); // return uid; } else { //console.log(`🟢remove becouse can't get uid: ${processedCards}, uid: ${uid}`); logMessages += `🟢remove becouse can't get uid: ${processedCards}, uid: ${uid}\n`; return -1; } } //console.log(`🟢remove becouse can't get ownerLink, processedCards: ${processedCards}, ownerLink: ${ownerLink}`); logMessages += `🟢remove becouse can't get ownerLink, processedCards: ${processedCards}, ownerLink: ${ownerLink}\n`; return -1; } async function getFollower(uid) { // 传入uid,返回follower数 const response = await fetch(`${API_USERDATA}${uid}`); //console.log(`🟢getFollower, uid: ${uid}` + response); logMessages += `🟢getFollower, uid: ${uid}\n`; const data = await response.json(); if (data.code === 0) { return data.data.follower; } else { //console.log(`🔴getFollower error, uid: ${uid}, message: ${data.message}`); logMessages += `🔴getFollower error, uid: ${uid}, message: ${data.message}\n`; return -1; } } // 对于每一个card,获取uid,然后获取follower,如果follower小于MIN_FOLLOWER,就remove // 未能获取到uid或者follower的,也remove // 不满足上面需要remove的,就processedCards++ // 进行异步处理,增加加载速度 async function editCards(card) { const uid = getUid(card); if (uid === -1) { //console.log(`🟢remove because getUid error, uid: ${uid}`); logMessages += `🟢remove because getUid error, uid: ${uid}\n`; card.remove(); return; } const follower = await getFollower(uid); if (follower === -1) { console.log(`🔴keep because getFollower error, uid: ${uid}`) return; } if (follower < MIN_FOLLOWER) { //console.log(`🟢remove because follower < ${MIN_FOLLOWER}, uid: ${uid}, follower: ${follower}`); logMessages += `🟢remove because follower < ${MIN_FOLLOWER}, uid: ${uid}, follower: ${follower}\n`; card.remove(); return; } } let isProcessing = false; // 创建Intersection Observer实例 const observer = new IntersectionObserver((entries, observer) => { entries.forEach(entry => { if (entry.isIntersecting) { // 处理进入视口的元素 editCards(entry.target); // 处理完毕后,停止观察该元素 observer.unobserve(entry.target); } }); }, { rootMargin: '0px', threshold: 0.2 }); // 可以根据需要调整配置 // 对新加载的内容进行观察 function observeNewCards() { const cards = document.querySelectorAll('.bili-video-card.is-rcmd, .floor-single-card, .bili-live-card.is-rcmd'); cards.forEach(card => { // 对每一个card进行观察 // 如果已经处理过了,就不再处理 if (card.dataset.processed) return; observer.observe(card); // 标记为已处理 card.dataset.processed = true }); } // 使用MutationObserver来监听新内容的加载,并调用observeNewCards const mutationObserver = new MutationObserver((mutations) => { // 如果正在处理中,就不再处理, 避免检测到自己remove时发生的变化,导致重复加载 if (isProcessing) return; isProcessing = true; logMessages += `🤓mutationObserver, mutations: ${mutations.length}\n`; //console.log(`🤓mutationObserver, mutations: ${mutations.length}`); mutations.forEach(mutation => { //输出变动的节点的信息,转化为字符串输出 //console.log(`👉🏻mutationObserver, mutation: ${JSON.stringify(mutation)}`); //logMessages += `👉🏻mutationObserver, mutation: ${JSON.stringify(mutation)}\n`; if (mutation.type === 'childList') { observeNewCards(); } }); isProcessing = false; }); //监控 class="container is-version8" 的元素 mutationObserver.observe(document.querySelector('.container.is-version8'), { childList: true, }); // 页面加载完成后,立即执行一次,以观察初始内容 observeNewCards(); // 自定义 log 函数,每5s 输出一次debug,防止控制台输出过多 let logMessages = ''; setInterval(() => { if (logMessages === '') return; console.log(logMessages); logMessages = ''; }, 10000); })();