Greasy Fork 支持简体中文。

YouTube 廣告跳過器

自動跳過 YouTube 上的廣告。隱藏廣告元素,在播放過程中將廣告靜音,移除廣告位,並提供一個選項來追蹤已跳過的廣告總時間。 包括反廣告封鎖偵測對策。

目前為 2024-08-28 提交的版本,檢視 最新版本

// ==UserScript==
// @name              YouTube Ad Skipper
// @name:en           YouTube Ad Skipper
// @name:vi           YouTube Ad Skipper
// @name:zh-cn         YouTube 广告跳过器
// @name:zh-tw         YouTube 廣告跳過器
// @name:ja           YouTube 広告スキッパー
// @name:ko           YouTube 광고 건너뛰기
// @name:es           YouTube Ad Skipper
// @name:ru           Пропускатель рекламы YouTube
// @name:id           YouTube Ad Skipper
// @name:hi           YouTube विज्ञापन स्किपर
// @namespace        http://tampermonkey.net/
// @version          4.0.0
// @description         Skip ads on YouTube automatically. Hides ad elements, mutes ads during playback, removes ad slots, and provides an option to track total skipped ad time. Includes anti-adblock detection countermeasures.
// @description:en      Skip ads on YouTube automatically. Hides ad elements, mutes ads during playback, removes ad slots, and provides an option to track total skipped ad time. Includes anti-adblock detection countermeasures.
// @description:vi      Tự động bỏ qua quảng cáo trên YouTube. Ẩn các thành phần quảng cáo, tắt tiếng quảng cáo trong khi phát lại, xóa vị trí quảng cáo và cung cấp tùy chọn để theo dõi tổng thời gian quảng cáo đã bỏ qua. Bao gồm các biện pháp đối phó với tính năng phát hiện 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 en YouTube. Oculta los elementos publicitarios, silencia los anuncios durante la reproducción, elimina los espacios publicitarios y ofrece una opción para realizar un seguimiento del tiempo total de anuncios omitidos. Incluye contramedidas de detección antibloqueo de anuncios.
// @description:ru      Автоматически пропускает рекламу на YouTube. Скрывает рекламные элементы, отключает звук рекламы во время воспроизведения, удаляет рекламные блоки и предоставляет возможность отслеживать общее время пропущенной рекламы. Включает контрмеры для обнаружения блокировки рекламы.
// @description:id      Lewati iklan secara otomatis di YouTube. Sembunyikan elemen iklan, nonaktifkan suara iklan selama pemutaran, hapus slot iklan, dan berikan opsi untuk melacak total waktu iklan yang dilewati. Termasuk tindakan pencegahan deteksi anti-adblock.
// @description:hi      YouTube पर विज्ञापनों को स्वचालित रूप से छोड़ें। विज्ञापन तत्वों को छुपाता है, प्लेबैक के दौरान विज्ञापनों को म्यूट करता है, विज्ञापन स्लॉट को हटाता है, और स्किप किए गए कुल विज्ञापन समय को ट्रैक करने का विकल्प प्रदान करता है। इसमें विज्ञापन-रोकथाम का पता लगाने के उपाय शामिल हैं।
// @author           RenjiYuusei
// @license          MIT
// @icon             https://www.google.com/s2/favicons?sz=64&domain=youtube.com
// @match            https://*.youtube.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @run-at       document-start
// ==/UserScript==

(function() {
    'use strict';

    const DEFAULT_CONFIG = {
        skipAds: true,
        hideAds: true,
        muteAds: true,
        trackSkippedTime: true,
        skipInterval: 500,
        hideMethod: 'display' // 'display' or 'remove'
    };

    let config = {...DEFAULT_CONFIG, ...GM_getValue('adSkipperConfig', {})};
    let totalSkippedTime = GM_getValue('totalSkippedTime', 0);
    let lastAdCheckTime = 0;
    let adObserver = null;

    const AD_SELECTORS = {
        clickable: [
            '.ytp-ad-skip-button',
            '.ytp-ad-skip-button-modern',
            '.videoAdUiSkipButton',
            '[id^="visit-advertiser"]',
            '.ytp-ad-overlay-close-button'
        ],
        hideable: [
            '.ad-showing',
            '.ytp-ad-overlay-container',
            'ytd-ad-slot-renderer',
            'ytd-promoted-video-renderer',
            'ytd-display-ad-renderer',
            'ytd-banner-promo-renderer',
            '[id^="ad-text"]',
            '[id^="ad-image"]',
            '.ytd-watch-next-secondary-results-renderer.ytd-item-section-renderer',
            'ytd-in-feed-ad-layout-renderer',
            'ytd-promoted-sparkles-web-renderer'
        ]
    };

    const skipAds = (mutations) => {
        if (!config.skipAds) return;

        const now = performance.now();
        if (now - lastAdCheckTime < config.skipInterval) return;
        lastAdCheckTime = now;

        let adSkipped = false;
        AD_SELECTORS.clickable.some(selector => {
            const button = document.querySelector(selector);
            if (button && button.offsetParent !== null) {
                button.click();
                adSkipped = true;
                return true;
            }
            return false;
        });

        const video = document.querySelector('video');
        if (video && isAdPlaying(video)) {
            if (video.duration > 0 && video.duration < 60 && !video.paused) {
                video.currentTime = video.duration;
                adSkipped = true;
            }
            if (config.muteAds) {
                video.muted = true;
            }
        } else if (video) {
            video.muted = false;
        }

        if (adSkipped && config.trackSkippedTime) {
            totalSkippedTime += config.skipInterval / 1000;
            GM_setValue('totalSkippedTime', totalSkippedTime);
        }
    };

    const isAdPlaying = (video) => {
        return document.querySelector('.ad-showing') !== null || 
               (video.src && video.src.includes('googlevideo.com/videoplayback') && video.src.includes('oad='));
    };

    const hideAds = () => {
        if (!config.hideAds) return;

        const style = document.createElement('style');
        const hideRule = config.hideMethod === 'remove' ? 'display: none !important; visibility: hidden !important; opacity: 0 !important;' : 'display: none !important;';
        style.textContent = AD_SELECTORS.hideable.map(selector => `${selector} { ${hideRule} }`).join('\n');
        (document.head || document.documentElement).appendChild(style);

        if (config.hideMethod === 'remove') {
            const removeAds = () => {
                AD_SELECTORS.hideable.forEach(selector => {
                    document.querySelectorAll(selector).forEach(el => el.remove());
                });
            };
            removeAds();
            new MutationObserver(removeAds).observe(document.body, { childList: true, subtree: true });
        }
    };

    const initAdSkipper = () => {
        hideAds();
        adObserver = new MutationObserver(skipAds);
        adObserver.observe(document.body, { childList: true, subtree: true });
    };

    const saveConfig = () => {
        GM_setValue('adSkipperConfig', config);
    };

    const toggleFeature = (feature) => {
        config[feature] = !config[feature];
        saveConfig();
        location.reload();
    };

    const setHideMethod = (method) => {
        config.hideMethod = method;
        saveConfig();
        location.reload();
    };

    const formatTime = (seconds) => {
        const hrs = Math.floor(seconds / 3600);
        const mins = Math.floor((seconds % 3600) / 60);
        const secs = Math.floor(seconds % 60);
        return `${hrs}h ${mins}m ${secs}s`;
    };

    GM_registerMenuCommand('Toggle Ad Skipping', () => toggleFeature('skipAds'));
    GM_registerMenuCommand('Toggle Ad Hiding', () => toggleFeature('hideAds'));
    GM_registerMenuCommand('Toggle Ad Muting', () => toggleFeature('muteAds'));
    GM_registerMenuCommand('Toggle Time Tracking', () => toggleFeature('trackSkippedTime'));
    GM_registerMenuCommand('Set Hide Method (Display)', () => setHideMethod('display'));
    GM_registerMenuCommand('Set Hide Method (Remove)', () => setHideMethod('remove'));
    GM_registerMenuCommand('Show Total Skipped Time', () => alert(`Total ad time skipped: ${formatTime(totalSkippedTime)}`));

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initAdSkipper);
    } else {
        initAdSkipper();
    }

    // Anti-detection measures
    const originalSetInterval = window.setInterval;
    window.setInterval = function(func, delay) {
        if (delay === 1000 && func.toString().includes('adTimeout')) {
            return originalSetInterval.call(this, () => {}, delay);
        }
        return originalSetInterval.apply(this, arguments);
    };
    })();