您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
智能跳过/弱化 YouTube 广告:强化新倒计时检测、按钮重试、速率钉住、失败兜底时间恢复刷新,并添加音量恢复功能
// ==UserScript== // @name Smart Auto Skip YouTube Ads (Enhanced+ 8.5.0 with Volume Control) // @namespace https://github.com/tientq64/userscripts // @version 8.7.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 isMusic = location.hostname === 'music.youtube.com' const isShorts = location.pathname.startsWith('/shorts/') const isVideoPage = !isMusic // 音量状态保存变量 let originalVolume = 1 let originalMuted = false let hasSavedVolumeState = false // 软跳转节流键与状态 const RELOAD_FLAG_KEY = '__yt_ad_reload_flag__' const RELOAD_TS_KEY = '__yt_ad_reload_ts__' const RELOAD_URL_KEY = '__yt_ad_reload_url__' const RELOAD_VOL_KEY = '__yt_ad_reload_volume__' // 新增:存储音量状态 const RELOAD_MUTE_KEY = '__yt_ad_reload_muted__' // 新增:存储静音状态 const RELOAD_COOLDOWN_MS = 60_000 // 1 分钟内不重复触发软跳转 // 进入页面若存在软跳转状态,恢复时间点和音量 ;(function restoreAfterBypass() { try { const flag = sessionStorage.getItem(RELOAD_FLAG_KEY) const ts = Number(sessionStorage.getItem(RELOAD_TS_KEY) || 0) const url = sessionStorage.getItem(RELOAD_URL_KEY) if (flag === '1' && url && Math.abs(Date.now() - ts) < 20_000) { // 恢复后立刻清理,防止回环 sessionStorage.removeItem(RELOAD_FLAG_KEY) sessionStorage.removeItem(RELOAD_TS_KEY) sessionStorage.removeItem(RELOAD_URL_KEY) // 获取保存的音量状态 const savedVolume = sessionStorage.getItem(RELOAD_VOL_KEY) const savedMuted = sessionStorage.getItem(RELOAD_MUTE_KEY) if (savedVolume) originalVolume = parseFloat(savedVolume) if (savedMuted) originalMuted = savedMuted === 'true' sessionStorage.removeItem(RELOAD_VOL_KEY) sessionStorage.removeItem(RELOAD_MUTE_KEY) // 等播放就绪后 seek const trySeek = () => { const v = document.querySelector('video.html5-main-video') if (v && !isNaN(v.duration) && v.duration > 1) { v.currentTime = Math.max(0, (new URL(url)).searchParams.get('t') ? v.currentTime : v.currentTime) // 用存储进度恢复 const target = Number((window.__yt_ad_restore_time__ || 0)) if (target > 0) v.currentTime = target + 1 delete window.__yt_ad_restore_time__ // 恢复原始音量状态 v.volume = originalVolume v.muted = originalMuted } else { requestAnimationFrame(trySeek) } } requestAnimationFrame(trySeek) } else { // 清理过期 sessionStorage.removeItem(RELOAD_FLAG_KEY) sessionStorage.removeItem(RELOAD_TS_KEY) sessionStorage.removeItem(RELOAD_URL_KEY) sessionStorage.removeItem(RELOAD_VOL_KEY) sessionStorage.removeItem(RELOAD_MUTE_KEY) } } catch {} })() 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', '.ytp-ad-skip-button-modern', // 新样式 'button[aria-label*="Skip"], button[aria-label*="跳过"]', ] 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 && getComputedStyle(btn).visibility !== 'hidden' if (visible) buttons.push(btn) }) }) return buttons } function tryClickAdButton(retries = 30, delay = 400) { const buttons = findVisibleAdButtons() if (buttons.length > 0) { buttons.forEach(btn => simulateClick(btn)) console.log(`[Ad Skipped] ${buttons.length} button(s) clicked`) // 广告跳过,恢复音量状态 restoreVolumeState() } else if (retries > 0) { setTimeout(() => tryClickAdButton(retries - 1, delay), delay) } } function removeResidualAdUI() { const selectors = [ '.ytp-ad-skip-button', '.ytp-skip-ad-button', '.ytp-ad-player-overlay-layout__skip-or-preview-container', '.ytp-ad-player-overlay', '.ytp-ad-image-overlay', '.ytp-ad-overlay-container', '.ytp-ad-end-screen', '.ytp-ad-text-overlay', '.ytp-ad-preview-container', '.ytp-ad-spinner', '.ytp-ad-duration-remaining', '.ytp-ad-timed-pie-countdown-container', '[class*="ad-countdown"]', '.ytp-paid-content-overlay' ] selectors.forEach(sel => { document.querySelectorAll(sel).forEach(el => el.remove()) }) } function isAdPlaying() { // 覆盖新的计时器/沙漏/广告态 return document.querySelector( '.ad-showing, .ytp-ad-player-overlay, .ytp-ad-spinner, .ytp-ad-duration-remaining, .ytp-ad-timed-pie-countdown-container, [class*="ad-countdown"]' ) } // 保存当前音量状态 function saveVolumeState() { if (hasSavedVolumeState) return const v = document.querySelector('video.html5-main-video') if (v) { originalVolume = v.volume originalMuted = v.muted hasSavedVolumeState = true console.log('[Volume State] Saved:', { volume: originalVolume, muted: originalMuted }) } } // 恢复原始音量状态 function restoreVolumeState() { const v = document.querySelector('video.html5-main-video') if (v && hasSavedVolumeState) { v.volume = originalVolume v.muted = originalMuted console.log('[Volume State] Restored:', { volume: originalVolume, muted: originalMuted }) hasSavedVolumeState = false } } // 层1:速率钉住器(持续对抗回滚/钳制) let ratePinTimer = null function pinAdPlaybackRate(enable) { if (!enable) { if (ratePinTimer) cancelAnimationFrame(ratePinTimer), ratePinTimer = null // 停止加速时恢复音量 restoreVolumeState() return } // 开始加速前保存音量状态 saveVolumeState() const loop = () => { const v = document.querySelector('video.html5-main-video') if (v) { // 多属性联动,尽量绕过限制 try { v.muted = true // 广告期间保持静音 v.defaultPlaybackRate = 16 if (v.playbackRate !== 16) v.playbackRate = 16 // 关闭音高保持,规避卡速 if ('preservesPitch' in v) v.preservesPitch = false if ('mozPreservesPitch' in v) v.mozPreservesPitch = false if ('webkitPreservesPitch' in v) v.webkitPreservesPitch = false // 避免暂停状态 if (v.paused) v.play().catch(()=>{}) } catch {} } ratePinTimer = requestAnimationFrame(loop) } if (!ratePinTimer) ratePinTimer = requestAnimationFrame(loop) } // 层2:常规跳过流程 function attemptSkipPath() { if (isShorts) return if (!isAdPlaying()) { // 如果没有广告,确保音量状态已恢复 restoreVolumeState() return } const moviePlayerEl = document.querySelector('#movie_player') // 优先调用原生 skip if (moviePlayerEl?.skipAd) { try { moviePlayerEl.skipAd() console.log('[Ad Skipped] via moviePlayer.skipAd()') // 广告跳过,恢复音量状态 restoreVolumeState() return } catch {} } // 点击按钮 + 速率钉住 tryClickAdButton() pinAdPlaybackRate(true) // 有些场景 seekTo 在广告期被拦截,但我们保持速率钉住直到广告态消失 removeResidualAdUI() } // 层3:兜底软跳转(进度恢复刷新) // 触发条件:广告状态持续一段时间仍未解除,且不在冷却期 let adStartAt = 0 function considerSoftBypass() { const adNow = !!isAdPlaying() const v = document.querySelector('video.html5-main-video') if (adNow && adStartAt === 0) adStartAt = Date.now() if (!adNow) { // 广告结束,恢复音量状态 restoreVolumeState() pinAdPlaybackRate(false) adStartAt = 0 return } // 广告持续超过阈值,尝试软跳转 const AD_STUCK_MS = 5000 // 5s 认为被"困住" if (adNow && adStartAt > 0 && Date.now() - adStartAt > AD_STUCK_MS) { const lastReload = Number(sessionStorage.getItem(RELOAD_TS_KEY) || 0) if (Date.now() - lastReload < RELOAD_COOLDOWN_MS) return // 保存当前音量状态 saveVolumeState() // 记录进度与 URL,刷新后恢复 try { let currentTime = 0 if (v && !isNaN(v.currentTime)) currentTime = Math.floor(v.currentTime || 0) window.__yt_ad_restore_time__ = currentTime sessionStorage.setItem(RELOAD_FLAG_KEY, '1') sessionStorage.setItem(RELOAD_TS_KEY, String(Date.now())) sessionStorage.setItem(RELOAD_URL_KEY, location.href) sessionStorage.setItem(RELOAD_VOL_KEY, String(originalVolume)) sessionStorage.setItem(RELOAD_MUTE_KEY, String(originalMuted)) console.log('[Ad Bypass] soft-reload with time restore', currentTime) // 使用 history.replaceState + location.reload,避免新开 tab history.replaceState(null, '', location.href) location.reload() } catch (e) { console.warn('Soft bypass failed', e) } finally { adStartAt = 0 pinAdPlaybackRate(false) } } } 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) setInterval(removeResidualAdUI, 1000) removeInlineAds() removeResidualAdUI() } // 监听 DOM 变化:尝试跳过 + 兜底判定 const observer = new MutationObserver(() => { setTimeout(() => { attemptSkipPath() considerSoftBypass() removeResidualAdUI() }, 150) }) observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['class', 'style', 'hidden', 'aria-hidden'] }) // 周期性心跳,防止遗漏 setInterval(() => { attemptSkipPath() considerSoftBypass() }, 400) })()