Greasy Fork

Greasy Fork is available in English.

YouTube Ad Skipper

Automatically skip ads on YouTube with advanced options, performance improvements, and best video quality selection.

当前为 2024-08-11 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

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