Greasy Fork 支持简体中文。

YouTube 廣告跳過器

自動跳過 YouTube 上的廣告,提供進階選項、效能改進和最佳影片品質選擇。 包括隱藏廣告覆蓋層、移除廣告位和封鎖廣告網域的功能。

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

// ==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          2.4.1
// @description         Skip ads on YouTube automatically with advanced options, performance improvements, and best video quality selection. Includes features for hiding ad overlays, removing ad slots, and blocking ad domains.
// @description:en      Skip ads on YouTube automatically with advanced options, performance improvements, and best video quality selection. Includes features for hiding ad overlays, removing ad slots, and blocking ad domains.
// @description:vi      Tự động bỏ qua quảng cáo trên YouTube với các tùy chọn nâng cao, cải thiện hiệu suất và lựa chọn chất lượng video tốt nhất. Bao gồm các tính năng ẩn lớp phủ quảng cáo, xóa vị trí quảng cáo và chặn miề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 con opciones avanzadas, mejoras de rendimiento y selección de la mejor calidad de video. Incluye funciones para ocultar superposiciones de anuncios, eliminar espacios publicitarios y bloquear dominios de anuncios.
// @description:ru      Автоматически пропускает рекламу на YouTube с расширенными настройками, улучшением производительности и выбором наилучшего качества видео. Включает функции для скрытия рекламных наложений, удаления рекламных блоков и блокировки рекламных доменов.
// @description:id      Lewati iklan secara otomatis di YouTube dengan opsi lanjutan, peningkatan kinerja, dan pemilihan kualitas video terbaik. Termasuk fitur untuk menyembunyikan overlay iklan, menghapus slot iklan, dan memblokir domain iklan.
// @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
// @grant            GM_addStyle
// ==/UserScript==

(function() {
    'use strict';

    const DEFAULT_CONFIG = {
        skipInterval: 150, // Increased interval
        observerThrottle: 300, // Increased throttle
        muteAds: false,
        hideAdOverlays: true,
        removeAdSlots: true,
        autoHideAnnotations: true,
        disableAutoplay: true,
        improvePerformance: true,
        autoSelectBestQuality: true,
        blockAdDomains: true,
        skipNonVideoAds: false, // Disabled by default
        aggressiveAdRemoval: false, // Disabled by default
        performance: {
            hideComments: true,
            hideSidebar: true,
            disableAnimations: true,
            lazyLoadImages: true,
            reduceScriptExecution: true
        }
    };

    let config = { ...DEFAULT_CONFIG, ...GM_getValue('AdSkipperConfig', {}) };

    function saveConfig() {
        GM_setValue('AdSkipperConfig', config);
    }

    function createSettingsMenu() {
        GM_registerMenuCommand("Configure Ad Skipper", () => {
            const newConfig = prompt("Enter new configuration (JSON):", JSON.stringify(config, null, 2));
            if (newConfig) {
                try {
                    config = { ...DEFAULT_CONFIG, ...JSON.parse(newConfig) };
                    saveConfig();
                    alert("Configuration updated successfully!");
                    location.reload();
                } catch (e) {
                    alert("Error: Invalid configuration format!");
                }
            }
        });
    }

    function skipAds() {
        const video = document.querySelector('video');

        const adIndicators = [
            '.ytp-ad-skip-button',
            '.ytp-ad-skip-button-modern',
            '.ad-showing',
            '.ytp-ad-overlay-close-button',
            '.ytp-ad-overlay-image',
            'ytd-ad-slot-renderer',
            'ytm-promoted-video-renderer',
            '[id^="ad-text"]',
            '[id^="ad-image"]',
            '.ytp-ad-text',
            '.ytp-ad-preview-text',
            '.ytp-ad-preview-image',
            'ytd-promoted-sparkles-web-renderer',
            'ytd-promoted-video-renderer',
            'ytd-player-legacy-desktop-watch-ads-renderer',
            'ytd-engagement-panel-section-list-renderer[target-id="engagement-panel-ads"]',
            '.ytd-video-masthead-ad-v3-renderer',
            'ytd-in-feed-ad-layout-renderer',
            'ytd-ad-slot-renderer',
            'ytd-popup-container > .ytd-popup-container > .ytd-mealbar-promo-renderer',
            '.ytd-display-ad-renderer',
            '.ytd-statement-banner-renderer',
            '.ytd-in-feed-ad-layout-renderer',
            '.ytd-banner-promo-renderer',
            '[layout="display-ad-layout-rendered"]',
            'ytd-ad-break-title-card',
            'ytd-ad-break-preview-renderer',
            'ytd-ad-test-renderer',
            'ytd-ad-legacy-billboard-renderer',
            'ytd-ad-legacy-companion-renderer',
            'ytd-ad-legacy-enhanced-renderer',
            'ytd-in-line-ad-viewer-renderer',
            'ytd-info-panel-container-renderer[style-type="MOVIE_AD_TYPE"]'
        ];

        let isAdPresent = adIndicators.some(selector => document.querySelector(selector));

        // Check for video ad duration and other characteristics
        if (video && video.duration > 0) {
            if (video.duration < 60 && video.currentTime < video.duration) {
                isAdPresent = true;
            }
            
            // Check for ad characteristics in the video URL
            const videoUrl = video.src;
            if (videoUrl.includes('googlevideo.com/videoplayback') && videoUrl.includes('oad=')) {
                isAdPresent = true;
            }
        }

        if (isAdPresent) {
            // Click all available skip buttons
            document.querySelectorAll('.ytp-ad-skip-button, .ytp-ad-skip-button-modern, .videoAdUiSkipButton, [id^="visit-advertiser"]').forEach(button => button.click());

            if (video && document.querySelector('.ad-showing')) {
                video.currentTime = video.duration;
                video.muted = config.muteAds;
            }

            if (config.hideAdOverlays) {
                document.querySelectorAll('.ytp-ad-overlay-close-button, .close-button').forEach(button => button.click());
                document.querySelectorAll('.ytp-ad-overlay-image, .ytd-overlay-ad-renderer').forEach(overlay => overlay.style.display = 'none');
            }

            if (config.removeAdSlots) {
                adIndicators.forEach(selector => {
                    document.querySelectorAll(selector).forEach(adElement => {
                        adElement.style.display = 'none';
                    });
                });
            }

            if (config.skipNonVideoAds) {
                document.querySelectorAll('.ytp-ad-text, .ytp-ad-preview-text, .ytp-ad-preview-image, .ytd-companion-slot-renderer').forEach(element => {
                    const container = element.closest('.ytp-ad-overlay-container, ytd-companion-slot-renderer');
                    if (container) container.style.display = 'none';
                });
            }

            if (config.aggressiveAdRemoval) {
                // Remove ad-related elements from the DOM
                adIndicators.forEach(selector => {
                    document.querySelectorAll(selector).forEach(adElement => {
                        adElement.remove();
                    });
                });

                // Attempt to remove inline ad styling
                const style = document.createElement('style');
                style.textContent = `
                    .ad-showing { display: none !important; }
                    .ytp-ad-overlay-container { display: none !important; }
                    .ytd-companion-slot-renderer { display: none !important; }
                `;
                document.head.appendChild(style);
            }
        }

        if (config.autoHideAnnotations) {
            document.querySelectorAll('.ytp-ce-covering-overlay, .ytp-ce-element, .iv-promo, .annotation').forEach(overlay => overlay.style.display = 'none');
        }

        if (config.disableAutoplay) {
            const autoplayToggle = document.querySelector('.ytp-autonav-toggle-button[aria-checked="true"]');
            if (autoplayToggle) autoplayToggle.click();
        }
    }

    const throttle = (func, limit) => {
        let lastFunc;
        let lastRan;
        return function() {
            const context = this;
            const args = arguments;
            if (!lastRan) {
                func.apply(context, args);
                lastRan = Date.now();
            } else {
                clearTimeout(lastFunc);
                lastFunc = setTimeout(function() {
                    if ((Date.now() - lastRan) >= limit) {
                        func.apply(context, args);
                        lastRan = Date.now();
                    }
                }, limit - (Date.now() - lastRan));
            }
        }
    };

    const throttledSkipAds = throttle(skipAds, config.observerThrottle);

    const observer = new MutationObserver(throttledSkipAds);

    observer.observe(document.body, {
        childList: true,
        subtree: true
    });

    function setMaxVideoQuality() {
        if (!config.autoSelectBestQuality) return;

        const qualityMenu = document.querySelector('.ytp-settings-button');
        if (qualityMenu) {
            qualityMenu.click();
            setTimeout(() => {
                const qualitySettings = document.querySelector('.ytp-panel-menu');
                if (qualitySettings) {
                    const qualityOptions = Array.from(qualitySettings.querySelectorAll('.ytp-menuitem'));
                    const highestQuality = qualityOptions.reduce((highest, current) => {
                        const resolution = current.textContent.trim().match(/(\d+)p/);
                        const currentResolution = resolution ? parseInt(resolution[1]) : 0;
                        const highestResolution = highest.textContent.trim().match(/(\d+)p/);
                        return currentResolution > (highestResolution ? parseInt(highestResolution[1]) : 0) ? current : highest;
                    }, qualityOptions[0]);

                    if (highestQuality) {
                        highestQuality.click();
                    }
                }
                qualityMenu.click(); // Close the menu
            }, 100);
        }
    }

    function applyPerformanceImprovements() {
        if (!config.improvePerformance) return;

        const styles = `
            ${config.performance.hideSidebar ? `
                .ytd-watch-flexy:not([theater]) #secondary.ytd-watch-flexy,
                ytd-watch-next-secondary-results-renderer {
                    display: none !important;
                }
            ` : ''}
            ${config.performance.hideComments ? `
                #comments {
                    display: none !important;
                }
            ` : ''}
            ${config.performance.disableAnimations ? `
                * {
                    transition: none !important;
                    animation: none !important;
                }
            ` : ''}
            ${config.performance.reduceScriptExecution ? `
                .ytp-ce-element {
                    display: none !important;
                }
            ` : ''}
        `;

        GM_addStyle(styles);

        if (config.performance.lazyLoadImages) {
            document.addEventListener('DOMContentLoaded', () => {
                document.querySelectorAll('img').forEach(img => {
                    img.loading = 'lazy';
                });
            });
        }

        if (config.performance.reduceScriptExecution) {
            let ticking = false;
            window.addEventListener('scroll', () => {
                if (!ticking) {
                    window.requestAnimationFrame(() => {
                        // Handle scroll event here
                        ticking = false;
                    });
                    ticking = true;
                }
            });

            let resizeTimer;
            window.addEventListener('resize', () => {
                clearTimeout(resizeTimer);
                resizeTimer = setTimeout(() => {
                    // Handle resize event here
                }, 250);
            });
        }
    }

    function blockAdDomains() {
        if (!config.blockAdDomains) return;

        const adDomains = [
            'doubleclick.net',
            'googlesyndication.com',
            'googleadservices.com',
            'google-analytics.com',
            'googletagmanager.com',
            'googletagservices.com',
            'youtube.com/api/stats/ads',
            'youtube.com/pagead',
            'youtube.com/ptracking'
        ];

        const originalFetch = window.fetch;
        window.fetch = function(...args) {
            if (adDomains.some(domain => args[0].includes(domain))) {
                return new Promise(() => {});
            }
            return originalFetch.apply(this, args);
        };

        const originalXHR = window.XMLHttpRequest.prototype.open;
        window.XMLHttpRequest.prototype.open = function(...args) {
            if (adDomains.some(domain => args[1].includes(domain))) {
                return;
            }
            return originalXHR.apply(this, args);
        };
    }

    function init() {
        setInterval(skipAds, config.skipInterval);
        setInterval(setMaxVideoQuality, 5000);
        applyPerformanceImprovements();
        blockAdDomains();
        createSettingsMenu();

        // Add an event listener to re-apply the script if the page navigates
        window.addEventListener('DOMContentLoaded', () => {
            skipAds(); // Run the script initially
        });
    }

    init();
})();