Prioritize "Soft Skip" (Mute+Seek/Button), fallback to "Reload" with cooldown; uses MutationObserver; reduces detection risk. / 优先“软跳过”(按钮/静音+跳尾),失败时执行带冷却的“重载”;使用 MutationObserver + 兜底定时器;降低被风控概率。
اعتبارا من
// ==UserScript==
// @name YouTube Ad Auto-Skipper
// @version 2025.11.24
// @match https://www.youtube.com/*
// @match https://m.youtube.com/*
// @match https://music.youtube.com/*
// @exclude https://studio.youtube.com/*
// @grant none
// @license MIT
// @noframes
// @run-at document-idle
// @namespace
// @description Prioritize "Soft Skip" (Mute+Seek/Button), fallback to "Reload" with cooldown; uses MutationObserver; reduces detection risk. / 优先“软跳过”(按钮/静音+跳尾),失败时执行带冷却的“重载”;使用 MutationObserver + 兜底定时器;降低被风控概率。
// ==/UserScript==
(function () {
'use strict';
const DEBUG = false;
// CSS选择器:只隐藏明确的广告元素
const CSS_HIDE_SELECTORS = [
'#player-ads',
'#masthead-ad',
'#panels > ytd-engagement-panel-section-list-renderer[target-id="engagement-panel-ads"]',
'.ytp-ad-overlay-container',
'.ytp-ad-text-overlay',
'.ytp-ad-image-overlay',
'.ytp-paid-content-overlay',
'ytd-merch-shelf-renderer',
'ytmusic-mealbar-promo-renderer'
];
const CHECK_DEBOUNCE_MS = 100;
const INTERVAL_CHECK_MS = 1000; // 1秒检查一次保底
const SEEK_EPSILON = 0.1;
const state = {
checkTimer: null
};
const log = (...args) => { if (DEBUG) console.log('[ASYA]', ...args); };
const isMobile = location.hostname === 'm.youtube.com';
const isMusic = location.hostname === 'music.youtube.com';
function addCss() {
const sel = CSS_HIDE_SELECTORS.join(',');
if (!sel) return;
const style = document.createElement('style');
// opacity:0 防检测,pointer-events:none 防止点击穿透拦截
style.textContent = `${sel}{opacity:0 !important; pointer-events:none !important; z-index:-1 !important;}`;
document.head ? document.head.appendChild(style) : document.documentElement.appendChild(style);
}
// 【关键修复】获取播放器容器,限制查找范围
function getPlayerContainer() {
return document.querySelector('#movie_player') ||
document.querySelector('yt-music-player') ||
document.body;
}
function querySkipButton(container) {
// 1. 优先查找标准的广告跳过按钮 class
const highPriorityBtn = container.querySelector('.ytp-ad-skip-button, .ytp-ad-skip-button-modern, .ytp-ad-skip-button-slot, .ytp-ad-skip-button-container button');
if (highPriorityBtn) return highPriorityBtn;
// 2. 只有找不到标准 class 时,才遍历 button 文本
// 【关键修复】限制只在 container (播放器) 内部查找,不扫描整个 document
const buttons = container.querySelectorAll('button');
// 转换为数组进行查找
for (const b of buttons) {
// 忽略不可见按钮,提高性能并减少误判
if (b.offsetParent === null) continue;
const t = (b.getAttribute('aria-label') || b.textContent || '').trim();
// 【关键修复】移除了 'Skip', '繼續' 等太宽泛的词,只匹配明确的广告词
if (/skip ad|skip ads|跳过广告|跳過廣告/i.test(t)) {
return b;
}
}
return null;
}
function detectAdContext() {
const container = getPlayerContainer();
// 只在播放器内部查找元素
const adShowing = !!container.querySelector('.ad-showing');
const pieCountdown = !!container.querySelector('.ytp-ad-timed-pie-countdown-container');
const skipBtn = querySkipButton(container);
const adLikely = adShowing || pieCountdown || !!skipBtn;
return { adLikely, skipBtn, container };
}
function trySoftSkip(ctx) {
// 1. 点击跳过按钮
if (ctx.skipBtn) {
const delay = 300 + Math.random() * 500;
setTimeout(() => {
try {
if (ctx.skipBtn && ctx.skipBtn.click) {
ctx.skipBtn.click();
log('Clicked skip button');
// 点击后立即触发下一次检查(应对连续广告)
setTimeout(() => scheduleCheck(0), 50);
}
} catch (_) {}
}, delay);
return true;
}
// 2. 强制跳过(加速+静音)
if (ctx.adLikely) {
const adVideo = ctx.container.querySelector('video.html5-main-video');
if (adVideo && !Number.isNaN(adVideo.duration) && adVideo.duration > 0) {
// 只有当确实是广告时才操作
// 简单的判断:通常广告视频时间很短,或者有 ad-showing 类
// 为了安全,我们依赖 detectAdContext 的 adLikely 判断
try {
const targetTime = Math.max(0, adVideo.duration - SEEK_EPSILON);
if (adVideo.currentTime < targetTime) {
adVideo.muted = true;
adVideo.playbackRate = 16;
adVideo.currentTime = targetTime;
log('Accelerated ad');
return true;
}
} catch (_) {}
}
}
return false;
}
function skipAd() {
const ctx = detectAdContext();
if (ctx.adLikely) {
log('Ad detected');
const handled = trySoftSkip(ctx);
if (handled) {
scheduleCheck(1000);
}
}
}
function scheduleCheck(delayMs = CHECK_DEBOUNCE_MS) {
if (state.checkTimer) clearTimeout(state.checkTimer);
state.checkTimer = setTimeout(() => {
skipAd();
state.checkTimer = null;
}, delayMs);
}
function setupObserver() {
// 【关键修复】只监视播放器区域,而不是整个 Body
// 这避免了点击菜单修改 Body 属性时触发脚本,也减少了性能消耗
const targetNode = document.querySelector('#movie_player') || document.body;
const observer = new MutationObserver((mutations) => {
// 简单防抖
scheduleCheck(50);
});
observer.observe(targetNode, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ['class', 'style', 'src'] // 限制监控的属性
});
}
// === 启动 ===
addCss();
// 尝试等待播放器加载后再启动 Observer
const waitTimer = setInterval(() => {
if (document.querySelector('#movie_player') || document.querySelector('video')) {
clearInterval(waitTimer);
setupObserver();
log('Observer attached to player');
}
}, 500);
// 定时保底
setInterval(() => scheduleCheck(), INTERVAL_CHECK_MS);
// 立即检查一次
scheduleCheck(1000);
})();