- // ==UserScript==
- // @name Auto Skip YouTube Ads
- // @name:vi Tự Động Bỏ Qua Quảng Cáo YouTube
- // @name:zh-CN 自动跳过 YouTube 广告
- // @name:zh-TW 自動跳過 YouTube 廣告
- // @name:ja YouTube 広告を自動スキップ
- // @name:ko YouTube 광고 자동 건너뛰기
- // @name:es Saltar Automáticamente Anuncios De YouTube
- // @name:pt-BR Pular Automaticamente Anúncios Do YouTube
- // @name:ru Автоматический Пропуск Рекламы На YouTube
- // @name:id Lewati Otomatis Iklan YouTube
- // @name:hi YouTube विज्ञापन स्वचालित रूप से छोड़ें
- // @namespace https://github.com/tientq64/userscripts
- // @version 4.8.0
- // @description Automatically skip YouTube ads almost instantly. Remove the ad blocker warning pop-up.
- // @description:vi Tự động bỏ qua quảng cáo YouTube gần như ngay lập tức. Loại bỏ cửa sổ bật lên cảnh báo trình chặn quảng cáo.
- // @description:zh-CN 几乎立即自动跳过 YouTube 广告。删除广告拦截器警告弹出窗口。
- // @description:zh-TW 幾乎立即自動跳過 YouTube 廣告。刪除廣告攔截器警告彈出視窗。
- // @description:ja YouTube 広告をほぼ瞬時に自動的にスキップします。広告ブロッカーの警告ポップアップを削除します。
- // @description:ko YouTube 광고를 거의 즉시 자동으로 건너뜁니다. 광고 차단 경고 팝업을 제거합니다.
- // @description:es Omite automáticamente los anuncios de YouTube casi al instante. Elimina la ventana emergente de advertencia del bloqueador de anuncios.
- // @description:pt-BR Pule automaticamente os anúncios do YouTube quase instantaneamente. Remova o pop-up de aviso do bloqueador de anúncios.
- // @description:ru Автоматически пропускайте рекламу YouTube почти мгновенно. Уберите всплывающее предупреждение о блокировщике рекламы.
- // @description:id Lewati iklan YouTube secara otomatis hampir seketika. Hapus pop-up peringatan pemblokir iklan.
- // @description:hi YouTube विज्ञापनों को लगभग तुरंत ही स्वचालित रूप से छोड़ दें। विज्ञापन अवरोधक चेतावनी पॉप-अप हटाएँ।
- // @author tientq64
- // @icon https://cdn-icons-png.flaticon.com/64/2504/2504965.png
- // @match https://www.youtube.com/*
- // @match https://music.youtube.com/*
- // @grant GM_getValue
- // @grant GM_setValue
- // @grant GM_registerMenuCommand
- // @license MIT
- // @compatible firefox
- // @compatible chrome
- // @compatible opera
- // @compatible safari
- // @compatible edge
- // @noframes
- // @homepage https://github.com/tientq64/userscripts/tree/main/scripts/Auto-Skip-YouTube-Ads
- // ==/UserScript==
-
- /**
- * Skip ads. Remove ad blocker warning.
- */
- function skipAd() {
- video = null
- fineScrub = document.querySelector('.ytp-fine-scrubbing')
-
- // Check if the current URL is a YouTube Shorts URL and exit the function if true.
- if (window.location.pathname.startsWith('/shorts/')) return
-
- const player = document.querySelector('#movie_player')
-
- if (player) {
- hasAd = player.classList.contains('ad-showing')
- video = player.querySelector('video.html5-main-video')
- }
-
- if (hasAd) {
- const skipButton = document.querySelector(`
- .ytp-skip-ad-button,
- .ytp-ad-skip-button,
- .ytp-ad-skip-button-modern,
- .ytp-ad-survey-answer-button
- `)
- // Click the skip ad button if available.
- if (skipButton) {
- skipButton.click()
- skipButton.remove()
- }
- // Otherwise, fast forward to the end of the ad video.
- // Use `9999` instead of `video.duration` to avoid errors when `duration`
- // is not a number.
- else if (video && video.src) {
- video.currentTime = 9999
- }
- }
-
- if (video) {
- video.addEventListener('pause', handleVideoPause)
- video.addEventListener('mouseup', allowPauseVideo)
- video.addEventListener('timeupdate', handleVideoTimeUpdate)
- }
-
- // Remove ad blocker warning dialog if it appears.
- const adBlockerWarningDialog = document.querySelector(
- 'tp-yt-paper-dialog:has(#feedback.ytd-enforcement-message-view-model)'
- )
- if (adBlockerWarningDialog) {
- adBlockerWarningDialog.remove()
- }
-
- // Handle when ad blocker warning appears inside video player.
- // Currently there is NO WAY TO REMOVE it.
- // Temporary workaround is to reload the page.
- const adBlockerWarningInner = document.querySelector(
- '.yt-playability-error-supported-renderers:has(.ytd-enforcement-message-view-model)'
- )
- if (adBlockerWarningInner) {
- if (checkCanReloadPage()) {
- adBlockerWarningInner.remove()
- const params = new URLSearchParams(location.search)
- params.set('t', Math.floor(currentVideoTime) + 's')
- const newUrl = location.origin + location.pathname + '?' + params.toString()
- location.replace(newUrl)
- }
- }
-
- // Video pause button.
- const playButton = document.querySelector('button.ytp-play-button')
- if (playButton) {
- playButton.addEventListener('click', allowPauseVideo)
- }
-
- // Remove short video ads.
- // Can't just use CSS to hide it, because it will cause problems when scrolling
- // to the next video.
- const adShortVideos = document.querySelectorAll(
- 'ytd-reel-video-renderer:has(.ytd-ad-slot-renderer)'
- )
- for (const adShortVideo of adShortVideos) {
- adShortVideo.remove()
- }
- }
-
- /**
- * Check if the user is focused on the input.
- */
- function checkEnteringInput() {
- if (document.activeElement === null) {
- return false
- }
- return document.activeElement.matches('input, textarea, select')
- }
-
- /**
- * Check if the page can be reloaded.
- */
- function checkCanReloadPage() {
- if (!config.allowedReloadPage) {
- return false
- }
- if (!config.dontReloadWhileBusy) {
- return true
- }
- if (checkEnteringInput()) {
- return false
- }
- // Do not reload while the user is reading comments.
- if (document.documentElement.scrollTop > 200) {
- return false
- }
- return true
- }
-
- /**
- * Temporarily allows the video to be paused, for a short period of time.
- */
- function allowPauseVideo() {
- pausedByUser = true
- window.clearTimeout(allowPauseVideoTimeoutId)
- allowPauseVideoTimeoutId = window.setTimeout(disallowPauseVideo, 500)
- }
-
- /**
- * Pausing the video is not allowed.
- * The purpose is to prevent video from being paused, against the behavior of pausing
- * video when YouTube ad blocking warning dialog appears. Unless certain conditions,
- * such as pausing by user, etc.
- */
- function disallowPauseVideo() {
- pausedByUser = false
- window.clearTimeout(allowPauseVideoTimeoutId)
- }
-
- function handleGlobalBlur() {
- isTabBlurred = true
- }
-
- function handleGlobalFocus() {
- isTabBlurred = false
- }
-
- /**
- * Handle when video is paused.
- * If certain conditions are not met, it will continue playing.
- */
- function handleVideoPause() {
- if (isYouTubeMusic) return
- if (pausedByUser) {
- disallowPauseVideo()
- return
- }
- if (document.hidden) return
- if (isTabBlurred) return
- if (fineScrub && fineScrub.style.display !== 'none') return
- if (video === null) return
- if (video.duration - video.currentTime < 0.1) return
- video.play()
- }
-
- function handleVideoTimeUpdate() {
- if (hasAd || video === null) return
- currentVideoTime = video.currentTime
- }
-
- /**
- * Handle both keyboard press or release events.
- */
- function handleGlobalKeyDownAndKeyUp(event) {
- if (isYouTubeMusic) return
- if (checkEnteringInput()) return
- const code = event.code
- if (event.type === 'keydown') {
- if (code === 'KeyK' || code === 'MediaPlayPause') {
- allowPauseVideo()
- }
- } else {
- if (code === 'Space') {
- allowPauseVideo()
- }
- }
- }
-
- /**
- * Save current configuration.
- */
- function saveConfig() {
- GM_setValue('config', config)
- }
-
- /**
- * Register menu commands, or update the menu.
- */
- function registerMenuCommands() {
- {
- const status = config.allowedReloadPage ? 'Yes' : 'No'
- GM_registerMenuCommand(
- `Reload page if ad cannot be skipped: ${status}`,
- () => {
- config.allowedReloadPage = !config.allowedReloadPage
- saveConfig()
- updateMenuCommands()
- },
- {
- id: 0,
- autoClose: false
- }
- )
- }
- {
- const status = config.dontReloadWhileBusy ? 'Yes' : 'No'
- GM_registerMenuCommand(
- `Don't reload while the user is busy: ${status}`,
- () => {
- config.dontReloadWhileBusy = !config.dontReloadWhileBusy
- saveConfig()
- updateMenuCommands()
- },
- {
- id: 1,
- autoClose: false
- }
- )
- }
- }
-
- /**
- * Update menu commands.
- * @alias registerMenuCommands
- */
- function updateMenuCommands() {
- registerMenuCommands()
- }
-
- /**
- * Add CSS hides some ad elements on the page.
- */
- function addCSSHideAds() {
- const selectors = [
- // Ad banner in the upper right corner, above the video playlist.
- '#player-ads',
- '#panels:has(ytd-engagement-panel-section-list-renderer[target-id="engagement-panel-ads"])',
- //
- '#masthead-ad',
- // Temporarily comment out this selector to fix issue [#265124](https://gf.qytechs.cn/en/scripts/498197-auto-skip-youtube-ads/discussions/265124).
- // '#panels:has(ytd-ads-engagement-panel-content-renderer)',
- 'ytd-ad-slot-renderer',
- // Sponsored ad video items on home page.
- 'ytd-rich-item-renderer:has(.ytd-ad-slot-renderer)',
- //
- 'ytd-rich-section-renderer:has(.ytd-statement-banner-renderer)',
- // Ad videos on YouTube Short.
- 'ytd-reel-video-renderer:has(.ytd-ad-slot-renderer)',
- // Ad blocker warning dialog.
- 'tp-yt-paper-dialog:has(#feedback.ytd-enforcement-message-view-model)',
- //
- '.ytp-suggested-action',
- '.yt-mealbar-promo-renderer',
- // YouTube Music Premium trial promotion dialog, bottom left corner.
- 'ytmusic-mealbar-promo-renderer',
- // YouTube Music Premium trial promotion banner on home page.
- 'ytmusic-statement-banner-renderer'
- ]
- const css = `${selectors.join(',')}{display:none!important}`
- const style = document.createElement('style')
- style.textContent = css
- document.head.appendChild(style)
- }
-
- /**
- * Default configuration.
- */
- const defaultConfig = {
- allowedReloadPage: true,
- dontReloadWhileBusy: true
- }
-
- // Load configuration.
- const config = GM_getValue('config', defaultConfig)
- for (const key in defaultConfig) {
- if (config[key] == null) {
- config[key] = defaultConfig[key]
- }
- }
-
- /**
- * Is the current page YouTube Music.
- */
- const isYouTubeMusic = location.hostname === 'music.youtube.com'
- /**
- * Current video element.
- */
- let video = null
- let fineScrub = null
- let hasAd = false
- let currentVideoTime = 0
- /**
- * Is the video paused by the user, not paused by YouTube's ad blocker warning dialog.
- */
- let pausedByUser = false
- /**
- * Is the current tab blurred.
- */
- let isTabBlurred = false
- let allowPauseVideoTimeoutId = 0
-
- // Observe DOM changes to detect ads.
- if (window.MutationObserver) {
- const observer = new MutationObserver(skipAd)
- observer.observe(document.body, {
- attributes: true,
- attributeFilter: ['class', 'src'],
- childList: true,
- subtree: true
- })
- }
- // If DOM observation is not supported. Detect ads every 500ms (2 times per second).
- else {
- window.setInterval(skipAd, 500)
- }
-
- window.addEventListener('blur', handleGlobalBlur)
- window.addEventListener('focus', handleGlobalFocus)
- window.addEventListener('keydown', handleGlobalKeyDownAndKeyUp)
- window.addEventListener('keyup', handleGlobalKeyDownAndKeyUp)
-
- addCSSHideAds()
- registerMenuCommands()
- skipAd()