Skip to content

Instantly share code, notes, and snippets.

@herbaldrinkmate
Last active February 21, 2026 21:52
  • Select an option

Select an option

Disable Bilibili Danmaku and Autoplay
// ==UserScript==
// @name Disable Bilibili Danmaku and Autoplay
// @name:zh-CN 禁用哔哩哔哩弹幕及自动连播
// @namespace http://tampermonkey.net/
// @version 1.1
// @description Turns off Bilibili danmaku and autoplay switches. Works even when signed out or after clearing cookies.
// @description:zh-CN 自动关闭B站弹幕及自动连播,即使处于未登录状态或清理了 Cookie,该脚本依然可以正常运行。
// @match *://www.bilibili.com/video/*
// @match *://player.bilibili.com/player.html*
// @match *://www.bilibili.com/blackboard/html5mobileplayer.html*
// @grant none
// @icon https://www.bilibili.com/favicon.ico
// @run-at document-start
// @license MIT
// ==/UserScript==
(function() {
'use strict';
let lastExecutionTime = 0;
let burstTimer = null;
const COOLDOWN_MS = 2000; // Intervals between scans during idle periods to save CPU
/**
* Core function to detect and click active switches.
*/
const disableSettings = () => {
let foundAnything = false;
// 1. PC/Embedded Player
// Only trigger click if 'checked' is true to prevent toggle loops
const danmakuInput = document.querySelector('.bui-danmaku-switch-input');
if (danmakuInput && danmakuInput.checked) {
danmakuInput.click();
foundAnything = true;
}
// 2. Autoplay switch
// Once clicked, the '.on' class is removed, preventing re-execution
const autoPlayBtn = document.querySelector('.next-play .switch-btn.on, .video-pod .switch-btn.on');
if (autoPlayBtn) {
autoPlayBtn.click();
foundAnything = true;
}
// 3. Mobile Player
// Mobile UI uses 'mplayer-active' class on an icon rather than a checkbox
const mobileDanmakuBtn = document.querySelector('.mplayer-btn-comment .mplayer-active');
if (mobileDanmakuBtn) {
mobileDanmakuBtn.click();
foundAnything = true;
}
lastExecutionTime = Date.now();
return foundAnything;
};
/**
* Burst Mode: Increases scanning frequency to 200ms when dynamic changes are detected.
* Ensures settings are disabled immediately after SPA navigation or page load.
*/
const triggerBurst = () => {
if (burstTimer) return;
let attempts = 0;
burstTimer = setInterval(() => {
const deactivated = disableSettings();
attempts++;
// Exit burst mode once switches are handled or timeout (5s) is reached
if (deactivated || attempts > 25) {
clearInterval(burstTimer);
burstTimer = null;
}
}, 200);
};
/**
* MutationObserver to handle Bilibili's SPA navigation.
* Monitors node additions within the player container.
*/
const observer = new MutationObserver((mutations) => {
let shouldCheck = false;
for (let i = 0; i < mutations.length; i++) {
if (mutations[i].addedNodes.length > 0) {
shouldCheck = true;
break;
}
}
if (shouldCheck) {
const now = Date.now();
// If the last scan was long ago, start a high-frequency burst
if (now - lastExecutionTime > COOLDOWN_MS) {
triggerBurst();
} else {
disableSettings();
}
}
});
const init = () => {
// Narrow the scope to the main container found during DOM analysis to reduce overhead
const targetContainer = document.querySelector('#mirror-vdcon') || document.body;
observer.observe(targetContainer, {
childList: true,
subtree: true
});
// Run initial burst to catch elements rendered during early load
triggerBurst();
};
// Initialize as soon as the body is available
if (document.body) {
init();
} else {
const preObserver = new MutationObserver(() => {
if (document.body) {
preObserver.disconnect();
init();
}
});
preObserver.observe(document.documentElement, { childList: true });
}
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment