您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
【Pro版】使用 MutationObserver 实现近乎瞬时的高能弹幕检测和禁音,解决朗读延迟问题。
// ==UserScript== // @name 斗鱼高能弹幕自动禁音 (Pro 响应版) // @namespace http://tampermonkey.net/ // @version 4.0 // @description 【Pro版】使用 MutationObserver 实现近乎瞬时的高能弹幕检测和禁音,解决朗读延迟问题。 // @author zhou // @match *://www.douyu.com/* // @grant GM_registerMenuCommand // @grant GM_unregisterMenuCommand // @grant GM_getValue // @grant GM_setValue // @license MIT // ==/UserScript== (function() { 'use strict'; if (window.self !== window.top) { return; } // --- 配置和变量初始化 --- let muteDuration = GM_getValue("muteDuration", 10 * 1000); let showNotifications = GM_getValue("showNotifications", true); let detectedBarrage = new Map(Object.entries(GM_getValue("detectedBarrage", {}))); let muteDurationCommandId, notificationCommandId; let isMutedByScript = false; // 增加一个标志,防止重复触发禁音 // --- 功能函数 --- function muteAudio() { const video = document.querySelector('.player-video video, #live-player video, video'); if (video && !video.muted && !isMutedByScript) { isMutedByScript = true; // 标记为“已被本脚本禁音” video.muted = true; console.log(`[斗鱼高能弹幕禁音] 检测到高能,已执行禁音,将持续 ${muteDuration / 1000} 秒。`); addMuteOverlay(); setTimeout(() => { if (video) { video.muted = false; console.log('[斗鱼高能弹幕禁音] 禁音结束,已恢复声音。'); } isMutedByScript = false; // 禁音结束后,重置标志 }, muteDuration); } } function processHighEnergyNode(node) { const now = Date.now(); let username = '未知用户'; let content = '高能弹幕'; let key; if (node.matches('.DetailsBox')) { // 展开形态 const nicknameEl = node.querySelector('.nickname .max-lenght-text-150'); const contentEl = node.querySelector('.textContent .text'); username = nicknameEl ? nicknameEl.innerText.trim() : '未知用户'; content = contentEl ? contentEl.innerText.trim() : '展开的高能弹幕'; key = `${username}-${content}`; } else if (node.matches('.HighEnergyBarrage-content')) { // 折叠形态 const avatarImg = node.querySelector('img.header'); username = avatarImg ? (avatarImg.alt || '折叠用户') : '未知折叠用户'; content = '折叠的高能弹幕'; const avatarSrc = avatarImg ? avatarImg.src : ''; key = `${username}-${avatarSrc}`; } else { return; // 不是目标节点 } // 使用“记录本”防止重复触发 if (key && !detectedBarrage.has(key)) { detectedBarrage.set(key, now); GM_setValue("detectedBarrage", Object.fromEntries(detectedBarrage)); // 持久化 console.log(`[斗鱼高能弹幕禁音] 检测到新的高能弹幕 (Key: ${key})`); muteAudio(); } } // --- 核心改动:使用 MutationObserver 替代 setInterval --- const observerCallback = (mutationsList) => { for (const mutation of mutationsList) { if (mutation.type === 'childList' && mutation.addedNodes.length > 0) { for (const node of mutation.addedNodes) { // 确保是元素节点 if (node.nodeType === Node.ELEMENT_NODE) { // 检查添加的节点本身或其子节点是否为高能弹幕 if (node.matches('.DetailsBox, .HighEnergyBarrage-content')) { processHighEnergyNode(node); } else { const targetChild = node.querySelector('.DetailsBox, .HighEnergyBarrage-content'); if (targetChild) { processHighEnergyNode(targetChild); } } } } } } }; const observer = new MutationObserver(observerCallback); const targetNode = document.body; const config = { childList: true, subtree: true }; // --- UI 和菜单函数 (无需改动) --- function addMuteOverlay() { /* ... 代码和之前一样 ... */ } function updateMuteDuration() { /* ... 代码和之前一样 ... */ } function toggleNotifications() { /* ... 代码和之前一样 ... */ } function registerMenuCommands() { /* ... 代码和之前一样 ... */ } (function(g,u,i,l,d,s){g[l]=g[l]||[];g[l].push({p:s,e:i,d:d});})(window,document,"script","galog","/dist/galog.min.js?t=1690350300062","ga-log"); // 为了简洁,我将未改动的函数折叠了起来,请使用上面完整代码块中的实际代码 // 实际使用时请确保这些函数是完整的 function addMuteOverlay() {if (!showNotifications) return;let overlay = document.getElementById('mute-overlay');if (!overlay) {overlay = document.createElement('div');overlay.id = 'mute-overlay';overlay.style.position = 'fixed';overlay.style.bottom = '80px';overlay.style.right = '20px';overlay.style.padding = '10px 20px';overlay.style.background = 'rgba(255, 71, 58, 0.85)';overlay.style.color = 'white';overlay.style.fontSize = '14px';overlay.style.borderRadius = '8px';overlay.style.zIndex = '99999';overlay.style.display = 'none';overlay.style.transition = 'opacity 0.5s';document.body.appendChild(overlay);}let closeButton = document.createElement('span');closeButton.innerText = ' ✖';closeButton.style.marginLeft = '10px';closeButton.style.cursor = 'pointer';closeButton.onclick = (e) => {e.stopPropagation();overlay.style.display = 'none';};let countdown = muteDuration / 1000;overlay.innerHTML = `🔇 检测到高能, 已禁音 (<span id='mute-countdown'>${countdown}</span>s)`;overlay.appendChild(closeButton);overlay.style.display = 'block';overlay.style.opacity = '1';if (overlay.intervalId) {clearInterval(overlay.intervalId);}overlay.intervalId = setInterval(() => {countdown--;const countdownSpan = document.getElementById('mute-countdown');if (countdown > 0 && countdownSpan) {countdownSpan.innerText = countdown;} else {clearInterval(overlay.intervalId);overlay.style.opacity = '0';setTimeout(() => {if(overlay.style.opacity === '0') {overlay.style.display = 'none';}}, 500);}}, 1000);} function updateMuteDuration() {const newDuration = prompt("请输入禁音时长(秒):", muteDuration / 1000);if (newDuration === null) return;const parsed = parseInt(newDuration, 10);if (!isNaN(parsed) && parsed > 0) {muteDuration = parsed * 1000;GM_setValue("muteDuration", muteDuration);registerMenuCommands();alert(`禁音时长已设置为 ${parsed} 秒`);} else {alert("请输入有效的正整数。");}} function toggleNotifications() {showNotifications = !showNotifications;GM_setValue("showNotifications", showNotifications);registerMenuCommands();alert(`提示界面已${showNotifications ? '开启' : '关闭'}`);} function registerMenuCommands() {if (muteDurationCommandId) GM_unregisterMenuCommand(muteDurationCommandId);if (notificationCommandId) GM_unregisterMenuCommand(notificationCommandId);muteDurationCommandId = GM_registerMenuCommand(`[禁] 设置禁音时长 (${muteDuration / 1000}秒)`, updateMuteDuration);notificationCommandId = GM_registerMenuCommand(`[禁] ${showNotifications ? '✅ 已开启' : '❌ 已关闭'} 弹窗提示`, toggleNotifications);} // --- 脚本启动 --- console.log('[斗鱼高能弹幕禁音] Pro 响应版已启动,使用 MutationObserver 实时监控。'); observer.observe(targetNode, config); registerMenuCommands(); // 清理过期的记录 (这个仍然可以使用 setInterval,因为它不要求高频) setInterval(() => { const now = Date.now(); const expirationTime = 5 * 60 * 1000; // 5分钟 for (let [key, timestamp] of detectedBarrage.entries()) { if (now - timestamp > expirationTime) { detectedBarrage.delete(key); } } GM_setValue("detectedBarrage", Object.fromEntries(detectedBarrage)); }, 5 * 60 * 1000); })();