// ==UserScript== // @name YouTube Ad Blocker Popup Remover // @namespace https://github.com/tizee-tampermonkey-scripts/tampermonkey-ytb-adblocker-remover // @version 1.1.0 // @description Automatically remove YouTube's anti-ad-blocker popup messages // @downloadURL https://raw.githubusercontent.com/tizee-tampermonkey-scripts/tampermonkey-ytb-adblocker-remover/refs/heads/main/user.js // @updateURL https://raw.githubusercontent.com/tizee-tampermonkey-scripts/tampermonkey-ytb-adblocker-remover/refs/heads/main/user.js // @author tizee // @match https://www.youtube.com/* // @match https://youtube.com/* // @grant none // @run-at document-start // @noframes // ==/UserScript== (function() { 'use strict'; const SCRIPT_PREFIX = '[YT-POPUP-KILLER]'; // 目标选择器配置 const SELECTORS = { popupContainer: 'ytd-popup-container', dialogContainer: 'tp-yt-paper-dialog', enforcementView: 'ytd-enforcement-message-view-model' }; // 检测关键词 const AD_BLOCKER_KEYWORDS = [ 'ad blocker', 'ad-blocker', 'adblocker', 'Ad blockers are not allowed', 'Video playback will be blocked', 'allowlisted or the ad blocker is disabled' ]; // 已处理的弹窗记录,避免重复处理 const processedPopups = new WeakSet(); /** * 检查元素内容是否包含广告拦截器相关文本 * @param {Element} element - 要检查的DOM元素 * @returns {boolean} 是否包含相关文本 */ function containsAdBlockerContent(element) { const textContent = element.textContent || element.innerText || ''; const lowerText = textContent.toLowerCase(); return AD_BLOCKER_KEYWORDS.some(keyword => lowerText.includes(keyword.toLowerCase()) ); } /** * 验证是否为目标弹窗 * @param {Element} element - 要验证的元素 * @returns {boolean} 是否为目标弹窗 */ function isTargetPopup(element) { // 检查是否已经处理过 if (processedPopups.has(element)) { return false; } // 检查是否为popup容器 if (!element.matches || !element.matches(SELECTORS.popupContainer)) { return false; } // 检查是否包含对话框和enforcement view const hasDialog = element.querySelector(SELECTORS.dialogContainer); const hasEnforcementView = element.querySelector(SELECTORS.enforcementView); if (!hasDialog || !hasEnforcementView) { return false; } // 验证内容 return containsAdBlockerContent(element); } /** * 移除目标弹窗 * @param {Element} popup - 要移除的弹窗元素 */ function removePopup(popup) { try { // 标记为已处理 processedPopups.add(popup); console.debug(`${SCRIPT_PREFIX} Removing ad-blocker popup:`, popup); // 立即隐藏 popup.style.display = 'none'; popup.style.visibility = 'hidden'; popup.style.opacity = '0'; popup.style.pointerEvents = 'none'; // 移除元素 if (popup.parentNode) { popup.parentNode.removeChild(popup); console.debug(`${SCRIPT_PREFIX} Successfully removed ad-blocker popup`); } } catch (error) { console.error(`${SCRIPT_PREFIX} Error removing popup:`, error); } } /** * 扫描并移除所有匹配的弹窗 * @param {Element} rootNode - 要扫描的根节点 */ function scanAndRemovePopups(rootNode = document) { // 查找所有可能的弹窗容器 const popupContainers = rootNode.querySelectorAll(SELECTORS.popupContainer); let removedCount = 0; popupContainers.forEach(popup => { if (isTargetPopup(popup)) { removePopup(popup); removedCount++; } }); if (removedCount > 0) { console.debug(`${SCRIPT_PREFIX} Removed ${removedCount} ad-blocker popup(s)`); } return removedCount; } /** * 处理DOM变化 * @param {MutationRecord[]} mutations - DOM变化记录 */ function handleMutations(mutations) { let needsFullScan = false; mutations.forEach(mutation => { if (mutation.type !== 'childList' || !mutation.addedNodes.length) { return; } mutation.addedNodes.forEach(node => { if (node.nodeType !== Node.ELEMENT_NODE) { return; } // 检查新增节点本身 if (node.matches && node.matches(SELECTORS.popupContainer)) { if (isTargetPopup(node)) { removePopup(node); return; } } // 检查是否包含弹窗容器 if (node.querySelector && node.querySelector(SELECTORS.popupContainer)) { needsFullScan = true; } }); }); // 如果检测到可能的弹窗,进行全面扫描 if (needsFullScan) { setTimeout(() => scanAndRemovePopups(), 10); } } /** * 定期扫描机制 - 限时1分钟 */ function startPeriodicScan() { let slowScanInterval; // 初始快速扫描 - 前5秒每100ms扫描一次 const fastScanInterval = setInterval(() => { const removed = scanAndRemovePopups(); if (removed > 0) { console.debug(`${SCRIPT_PREFIX} Fast scan removed ${removed} popup(s)`); } }, 100); // 5秒后停止快速扫描,改为慢速扫描 setTimeout(() => { clearInterval(fastScanInterval); // 慢速定期扫描 - 接下来55秒每2秒扫描一次 slowScanInterval = setInterval(() => { scanAndRemovePopups(); }, 2000); console.debug(`${SCRIPT_PREFIX} Switched to slow scan mode`); }, 5000); // 1分钟后停止所有定期扫描 setTimeout(() => { clearInterval(fastScanInterval); if (slowScanInterval) { clearInterval(slowScanInterval); } console.debug(`${SCRIPT_PREFIX} Periodic scanning completed, relying on MutationObserver only`); }, 60000); } /** * 初始化脚本 */ function main() { console.debug(`${SCRIPT_PREFIX} Script initialized`); // 立即扫描现有内容 scanAndRemovePopups(document.documentElement); // 启动定期扫描 startPeriodicScan(); // 设置MutationObserver监听新增内容 const observer = new MutationObserver(handleMutations); // 等待body可用后开始观察 const startObserver = () => { if (document.body) { observer.observe(document.body, { childList: true, subtree: true }); console.debug(`${SCRIPT_PREFIX} MutationObserver started`); } else { setTimeout(startObserver, 10); } }; startObserver(); // 页面完全加载后再次扫描 window.addEventListener('load', () => { setTimeout(() => { scanAndRemovePopups(); console.debug(`${SCRIPT_PREFIX} Post-load scan completed`); }, 100); }); // 页面可见性变化时扫描 document.addEventListener('visibilitychange', () => { if (!document.hidden) { setTimeout(() => scanAndRemovePopups(), 100); } }); // URL变化监听(针对SPA应用) let lastUrl = location.href; new MutationObserver(() => { const url = location.href; if (url !== lastUrl) { lastUrl = url; setTimeout(() => scanAndRemovePopups(), 500); console.debug(`${SCRIPT_PREFIX} URL changed, rescanning`); } }).observe(document, {subtree: true, childList: true}); } // 立即开始执行 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', main); // 同时也立即开始扫描,不等待DOMContentLoaded setTimeout(main, 0); } else { main(); } })();