Greasy Fork

屏蔽B站营销视频和推广视频

屏蔽部分B站(bilibili)主页推荐的视频卡片,屏蔽up主粉丝少于一定数量的,屏蔽直播与右侧推广,屏蔽带广告标签的

目前为 2024-06-14 提交的版本。查看 最新版本

// ==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      1.1
// @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==

(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(`🔴getUid error, processedCards: ${processedCards}, uid: ${uid}`);
        return -1;
      }
    }

    console.log(`🔴getUid error, ownerLink error, processedCards: ${processedCards}, ownerLink: ${ownerLink}`);
    return -1;
  }


  async function getFollower(uid) {
    // 传入uid,返回follower数
    const response = await fetch(`${API_USERDATA}${uid}`);
    const data = await response.json();
    if (data.code === 0) {
      return data.data.follower;
    } else {
      console.log(`🔴getFollower error, uid: ${uid}, message: ${data.message}`);
      return -1;
    }
  }

  // 对于每一个card,获取uid,然后获取follower,如果follower小于MIN_FOLLOWER,就remove
  // 未能获取到uid或者follower的,也remove
  // 不满足上面需要remove的,就processedCards++
  // 进行异步处理,增加加载速度

  // 最大并行处理数
  const MAX_PARALLEL = 0;
  // 当前并行处理数
  let parallel = 0;

  function editCards(card) {

    const uid = getUid(card);
    if (uid === -1) {
      console.log(`🟢remove because getUid error, uid: ${uid}`);
      card.remove();
      return;
    }

    const follower = getFollower(uid);
    if (follower < MIN_FOLLOWER) {
      console.log(`🟢remove because follower < ${MIN_FOLLOWER}, uid: ${uid}, follower: ${follower}`);
      card.remove();
      return;
    }
  }


  // 创建Intersection Observer实例
  const observer = new IntersectionObserver((entries, observer) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        // 处理进入视口的元素
        editCards(entry.target);
        processedCards++;
        parallel--;
        // 处理完毕后,停止观察该元素
        observer.unobserve(entry.target);
      }
    });
  }, { rootMargin: '20px', threshold: 0.1 }); // 可以根据需要调整配置


  // 对新加载的内容进行观察
  function observeNewCards() {
    const cards = document.querySelectorAll('.bili-video-card.is-rcmd, .floor-single-card, .bili-live-card.is-rcmd');
    cards.forEach(card => {
      observer.observe(card);
    });
  }

  // 使用MutationObserver来监听新内容的加载,并调用observeNewCards
  const mutationObserver = new MutationObserver((mutations) => {
    mutations.forEach(mutation => {
      if (mutation.type === 'childList') {
        observeNewCards();
      }
    });
  });

  mutationObserver.observe(document.querySelector('.recommended-container_floor-aside'), {
    childList: true,
    subtree: true
  });

  // 页面加载完成后,立即执行一次,以观察初始内容
  observeNewCards();
})();