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.2.5
// @description         Automatically skip ads on YouTube with advanced options, performance improvements, and best video quality selection.
// @description:en      Automatically skip ads on YouTube with advanced options, performance improvements, and best video quality selection.
// @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.
// @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.
// @description:ru      Автоматически пропускает рекламу на YouTube с расширенными настройками, улучшением производительности и выбором наилучшего качества видео.
// @description:id      Lewati iklan secara otomatis di YouTube dengan opsi lanjutan, peningkatan kinerja, dan pemilihan kualitas video terbaik.
// @description:hi      YouTube पर विज्ञापनों को स्वचालित रूप से छोड़ें, उन्नत विकल्पों, प्रदर्शन में सुधार और सर्वोत्तम वीडियो गुणवत्ता चयन के साथ।
// @author           Jann
// @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: 250,
        observerThrottle: 500,
        muteAds: false,
        hideAdOverlays: true,
        removeAdSlots: true,
        autoHideAnnotations: true,
        disableAutoplay: false,
        improvePerformance: true,
        autoSelectBestQuality: true,
        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');

        // Improved ad detection - More selectors and checks
        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"]',
            // Add more potential selectors here
        ];

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

        // Check for video ad duration
        if (video && video.duration > 0 && video.currentTime < video.duration) {
            isAdPresent = true;
        }

        if (isAdPresent) {
            // Prioritize clicking the skip button
            document.querySelectorAll('.ytp-ad-skip-button, .ytp-ad-skip-button-modern').forEach(button => button.click());

            // Less aggressive seeking - Seek a few seconds forward
            if (video && document.querySelector('.ad-showing')) {
                video.currentTime += 5; // Seek 5 seconds forward
                video.muted = config.muteAds;
            }

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

            // Remove ad slots
            if (config.removeAdSlots) {
                document.querySelectorAll('ytd-ad-slot-renderer, ytm-promoted-video-renderer').forEach(slot => {
                    slot.style.display = 'none';
                });
            }
        }

        // Auto-hide annotations
        if (config.autoHideAnnotations) {
            document.querySelectorAll('.ytp-ce-covering-overlay').forEach(overlay => overlay.style.display = 'none');
        }

        // Disable autoplay
        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 init() {
        setInterval(skipAds, config.skipInterval);
        setInterval(setMaxVideoQuality, 5000);
        applyPerformanceImprovements();
        createSettingsMenu();
    }

    init();
    })();