Greasy Fork

自动跳过 YouTube 广告

自动立即跳过 YouTube 广告。删除广告拦截器警告弹出窗口。非常轻量且高效。

目前为 2024-10-16 提交的版本。查看 最新版本

// ==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:ru            Автоматический Пропуск Рекламы На YouTube
// @name:id            Lewati Otomatis Iklan YouTube
// @name:hi            YouTube विज्ञापन स्वचालित रूप से छोड़ें
// @namespace          https://github.com/tientq64/userscripts
// @version            4.6.3
// @description        Automatically skip YouTube ads instantly. Remove the ad blocker warning pop-up. Very lightweight and efficient.
// @description:vi     Tự động bỏ qua quảng cáo YouTube 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. Rất nhẹ và hiệu quả.
// @description:zh-CN  自动立即跳过 YouTube 广告。删除广告拦截器警告弹出窗口。非常轻量且高效。
// @description:zh-TW  立即自動跳過 YouTube 廣告。刪除廣告攔截器警告彈出視窗。非常輕巧且高效。
// @description:ja     YouTube 広告を即座に自動的にスキップします。広告ブロッカーの警告ポップアップを削除します。非常に軽量で効率的です。
// @description:ko     YouTube 광고를 즉시 자동으로 건너뜁니다. 광고 차단 경고 팝업을 제거하세요. 매우 가볍고 효율적입니다.
// @description:es     Omita automáticamente los anuncios de YouTube al instante. Elimine la ventana emergente de advertencia del bloqueador de anuncios. Muy ligero y eficiente.
// @description:ru     Автоматически пропускайте рекламу YouTube мгновенно. Удалите всплывающее окно с предупреждением о блокировке рекламы. Очень легкий и эффективный.
// @description:id     Lewati iklan YouTube secara otomatis secara instan. Hapus pop-up peringatan pemblokir iklan. Sangat ringan dan efisien.
// @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')
    let hasAd = false

    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
        `)
        // 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)
    }

    // 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 (config.allowedReloadPage) {
            adBlockerWarningInner.remove()
            location.reload()
        }
    }

    // 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()
    }
}

/**
 * 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)
}

/**
 * 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 (fineScrub && fineScrub.style.display !== 'none') return
    if (video === null) return
    if (video.duration - video.currentTime < 0.1) return
    video.play()
}

/**
 * Handle both keyboard press or release events.
 */
function handleGlobalKeyDownAndKeyUp(event) {
    if (isYouTubeMusic) return
    if (document.activeElement?.matches('input, textarea, select')) 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() {
    GM_registerMenuCommand(
        `Reload page if ad cannot be skipped: ${config.allowedReloadPage ? 'Yes' : 'No'}`,
        () => {
            config.allowedReloadPage = !config.allowedReloadPage
            saveConfig()
            registerMenuCommands()
        },
        {
            id: 0,
            autoClose: false
        }
    )
}

/**
 * Default configuration.
 */
const defaultConfig = {
    allowedReloadPage: 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
/**
 * Is the video paused by the user, not paused by YouTube's ad blocker warning dialog?
 */
let pausedByUser = 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)
}
skipAd()

window.addEventListener('keydown', handleGlobalKeyDownAndKeyUp)
window.addEventListener('keyup', handleGlobalKeyDownAndKeyUp)

// CSS hides some ad elements on the page.
const style = document.createElement('style')
style.textContent = `
    #player-ads,
    #masthead-ad,
    #panels:has(ytd-ads-engagement-panel-content-renderer),
    ytd-ad-slot-renderer,
    ytd-rich-item-renderer:has(.ytd-ad-slot-renderer),
    ytd-rich-section-renderer:has(.ytd-statement-banner-renderer),
    ytd-reel-video-renderer:has(.ytd-ad-slot-renderer),
    tp-yt-paper-dialog:has(#feedback.ytd-enforcement-message-view-model),
    .ytp-suggested-action,
    .yt-mealbar-promo-renderer,
    ytmusic-mealbar-promo-renderer {
        display: none !important;
    }`
document.head.appendChild(style)

registerMenuCommands()