您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
智能跳过 YouTube 广告,模拟结束事件并清除残留界面(增强按钮检测)
// ==UserScript== // @name Smart Auto Skip YouTube Ads (Enhanced+) // @namespace https://github.com/tientq64/userscripts // @version 8.1.0 // @description 智能跳过 YouTube 广告,模拟结束事件并清除残留界面(增强按钮检测) // @author tientq64 + enhanced by yy // @match https://www.youtube.com/* // @match https://m.youtube.com/* // @match https://music.youtube.com/* // @exclude https://studio.youtube.com/* // @grant none // @license MIT // ==/UserScript== (function () { 'use strict' const isMobile = location.hostname === 'm.youtube.com' const isMusic = location.hostname === 'music.youtube.com' const isShorts = location.pathname.startsWith('/shorts/') const isVideoPage = !isMusic function simulateClick(el) { if (!el) return ['mouseover', 'mousedown', 'mouseup', 'click'].forEach(type => { el.dispatchEvent(new MouseEvent(type, { bubbles: true, cancelable: true })) }) } function findVisibleAdButtons() { const selectors = [ '.ytp-ad-skip-button', '.ytp-skip-ad-button', '[id^="skip-button"]', '.ytp-ad-player-overlay-layout__skip-or-preview-container button', '.ytp-ad-player-overlay-layout__skip-or-preview-container .ytp-skip-ad-button' ] const buttons = [] selectors.forEach(sel => { document.querySelectorAll(sel).forEach(btn => { const rect = btn.getBoundingClientRect() const visible = btn.offsetParent !== null && rect.width > 0 && rect.height > 0 if (visible) buttons.push(btn) }) }) return buttons } function skipAd() { if (isShorts) return const adShowing = document.querySelector('.ad-showing') const pieCountdown = document.querySelector('.ytp-ad-timed-pie-countdown-container') const surveyQuestions = document.querySelector('.ytp-ad-survey-questions') const skipButton = document.querySelector('.ytp-ad-skip-button') if (!adShowing && !pieCountdown && !surveyQuestions && !skipButton) return const moviePlayerEl = document.querySelector('#movie_player') const player = moviePlayerEl?.getPlayer?.() || moviePlayerEl const adVideo = document.querySelector('video.html5-main-video') // 优先点击所有可见广告按钮 const buttons = findVisibleAdButtons() if (buttons.length > 0) { buttons.forEach(btn => simulateClick(btn)) console.log(`[Ad Skipped] ${buttons.length} button(s) clicked`) return } // fallback:点击单个跳过按钮 if (skipButton) { skipButton.click() console.log('[Ad Skipped] via skip button click') return } // 快进广告视频并触发 ended 事件 if (adVideo && !adVideo.paused && !isNaN(adVideo.duration)) { adVideo.muted = true adVideo.currentTime = adVideo.duration adVideo.dispatchEvent(new Event('ended')) console.log('[Ad Skipped] via fast-forward + ended event') } else { const currentTime = Math.floor(player.getCurrentTime?.() || 0) player.seekTo?.(currentTime, true) console.log('[Ad Skipped] via seekTo fallback') } // 清除残留广告容器 document.querySelectorAll( '.ytp-ad-player-overlay, .ytp-ad-image-overlay, .ytp-ad-overlay-container' ).forEach(el => el.remove()) // 保持字幕开启状态 if (moviePlayerEl?.isSubtitlesOn?.()) { setTimeout(() => moviePlayerEl.toggleSubtitlesOn?.(), 1000) } } function hideAdElements() { const selectors = [ '#player-ads', '#masthead-ad', '.ytp-featured-product', '.yt-mealbar-promo-renderer', 'ytd-merch-shelf-renderer', 'ytmusic-mealbar-promo-renderer', 'ytmusic-statement-banner-renderer', '#panels > ytd-engagement-panel-section-list-renderer[target-id="engagement-panel-ads"]' ] const style = document.createElement('style') style.textContent = selectors.join(',') + '{ display: none !important; }' document.head.appendChild(style) } function removeInlineAds() { const adSelectors = [ ['ytd-reel-video-renderer', '.ytd-ad-slot-renderer'] ] for (const [container, child] of adSelectors) { const el = document.querySelector(container) if (el?.querySelector(child)) el.remove() } } hideAdElements() if (isVideoPage) { setInterval(removeInlineAds, 1000) removeInlineAds() } const observer = new MutationObserver(() => { setTimeout(skipAd, 300) }) observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['class'] }) })()