您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Automatically speeds up YouTube ads, remembers playback speed, and displays current speed
// ==UserScript== // @name youtube-ad-speeder-and-playback-speed-saver // @name:en YouTube Ad Speeder & Playback Speed Saver // @name:tr YouTube Reklam Hızlandırıcı ve Oynatma Hızı Hatırlayıcı // @name:az YouTube Reklam Sürətləyicisi və Oynatma Sürətini Yadda Saxlamayan // @name:ru YouTube Ускоритель скорости рекламы и запоминания скорости воспроизведения // @description Automatically speeds up YouTube ads, remembers playback speed, and displays current speed // @description:tr YouTube reklamlarını otomatik hızlandırır, oynatma hızını hatırlar ve gösterir // @description:az YouTube reklamlarını avtomatik sürətləndirir, oxuma sürətini yadda saxlayır və göstərir // @description:ru Автоматически ускоряет рекламу на YouTube, запоминает и показывает скорость воспроизведения // @author Oruc Qafarov (Orr888) // @version 1.0.0 // @license MIT // @contributionURL https://github.com/orrstudio/youtube-ad-speeder-and-playback-speed-saver // @namespace https://github.com/orrstudio // @homepageURL https://github.com/orrstudio/youtube-ad-speeder-and-playback-speed-saver // @supportURL https://github.com/orrstudio/youtube-ad-speeder-and-playback-speed-saver/issues // @match *://*.youtube.com/* // @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com // @grant none // @run-at document-idle // ==/UserScript== (function() { 'use strict'; const DEBUG = false; // Set to true for development function log(...args) { if (DEBUG) console.log('[AdSkipper]', ...args); } let isAdPlaying = false; let videoElement = null; let mainVideoUrl = null; // Хранилище настроек const store = { get rate() { return parseFloat(localStorage.getItem("yt-playback-rate")) || 1.0; }, set rate(v) { localStorage.setItem("yt-playback-rate", v); } }; // Функция для ускорения рекламы function speedUpAd() { if (!videoElement) return; try { videoElement.playbackRate = 16; videoElement.muted = true; log('Ускоряем рекламу'); updateSpeedIndicator(16); } catch (e) { log('Ошибка при ускорении рекламы:', e); } } // Создаем и обновляем индикатор скорости function updateSpeedIndicator(rate) { let indicator = document.querySelector('.ytp-speed-indicator'); if (!indicator) { const controls = document.querySelector('.ytp-right-controls'); if (!controls) return null; indicator = document.createElement('div'); indicator.className = 'ytp-button ytp-speed-indicator'; indicator.style.minWidth = '40px'; indicator.style.textAlign = 'center'; indicator.style.fontSize = '12px'; indicator.style.fontWeight = '500'; indicator.style.color = '#fff'; indicator.style.cursor = 'default'; indicator.title = 'Текущая скорость воспроизведения'; // Вставляем перед первым элементом правой панели управления controls.insertBefore(indicator, controls.firstChild); } indicator.textContent = rate === 1 ? '1.0x' : rate.toFixed(1) + 'x'; return indicator; } // Функция сброса скорости function resetPlayback() { if (!videoElement) return; try { videoElement.playbackRate = store.rate; videoElement.muted = false; log(`Возвращаем сохраненную скорость: ${store.rate}x`); updateSpeedIndicator(store.rate); } catch (e) { log('Ошибка при сбросе скорости:', e); } } // Проверяем, является ли видео рекламой function isAdVideo(url) { if (!url) return false; // Основные признаки рекламного видео const adPatterns = [ /googlevideo\.com\/videoplayback\?.*?&(?!mime=video%2Fmp4|mime=audio%2Fmp4)/, /googlevideo\.com\/ptracking\?/, /doubleclick\.net\/.*?\/ad/, /youtube\.com\/api\/stats\/ads/, /youtube\.com\/api\/stats\/qoe\?/ ]; // Дополнительные проверки по DOM const adIndicators = [ '.video-ads.ytp-ad-module', '.ad-showing', '.ad-interrupting', '.ytp-ad-player-overlay', '.ytp-ad-skip-button', '.ytp-skip-ad-button', '.videoAdUiSkipButton' ]; // Проверяем URL на соответствие шаблонам рекламы const isAdByUrl = adPatterns.some(pattern => pattern.test(url)); // Проверяем наличие рекламных элементов в DOM const hasAdElements = adIndicators.some(selector => { const elements = document.querySelectorAll(selector); return Array.from(elements).some(el => el.offsetParent !== null && window.getComputedStyle(el).display !== 'none' ); }); return isAdByUrl || hasAdElements; } // Обработчик изменений function handleVideoChange() { if (!videoElement || !videoElement.src) return; const currentUrl = videoElement.src; const isAd = isAdVideo(currentUrl); log('Текущее видео:', { url: currentUrl, isAd: isAd, currentTime: videoElement.currentTime, duration: videoElement.duration }); // Если это основное видео if (!isAd) { if (mainVideoUrl !== currentUrl) { mainVideoUrl = currentUrl; log('Обновлено основное видео:', mainVideoUrl); } if (isAdPlaying) { log('Реклама закончилась, возвращаем нормальную скорость'); isAdPlaying = false; resetPlayback(); } return; } // Если это реклама log('Обнаружена реклама, ускоряем...'); isAdPlaying = true; speedUpAd(); } // Настройка наблюдателя за видео function setupVideoObserver() { if (!videoElement || videoElement.dataset.observerSet) return; videoElement.dataset.observerSet = 'true'; // Восстанавливаем сохраненную скорость if (store.rate && store.rate !== 1.0) { videoElement.playbackRate = store.rate; log(`Восстановлена сохраненная скорость: ${store.rate}x`); } // Обработчик изменения скорости videoElement.addEventListener('ratechange', () => { if (!isAdPlaying) { // Не сохраняем скорость для рекламы store.rate = videoElement.playbackRate; log(`Сохранена новая скорость: ${store.rate}x`); } updateSpeedIndicator(videoElement.playbackRate); }); // Инициализируем индикатор при загрузке updateSpeedIndicator(videoElement.playbackRate); // Наблюдаем за изменениями атрибутов видео const observer = new MutationObserver((mutations) => { mutations.forEach(mutation => { if (mutation.attributeName === 'src') { log('Изменен src видео:', videoElement.src); handleVideoChange(); } }); }); observer.observe(videoElement, { attributes: true, attributeFilter: ['src'] }); // Слушаем события воспроизведения ['play', 'playing', 'pause', 'seeking', 'seeked', 'timeupdate'].forEach(event => { videoElement.addEventListener(event, () => { log(`Событие ${event}`, { currentTime: videoElement.currentTime, duration: videoElement.duration, paused: videoElement.paused }); handleVideoChange(); }); }); } // Инициализация function init() { // Находим видеоэлемент const findVideo = () => { const videos = document.getElementsByTagName('video'); return videos.length > 0 ? videos[0] : null; }; // Настройка наблюдателя за появлением видео const setupObserver = () => { videoElement = findVideo(); if (videoElement) { setupVideoObserver(); // Первоначальная проверка с задержкой для загрузки метаданных setTimeout(handleVideoChange, 1000); } }; // Обработчик для SPA-навигации const handleNavigation = () => { videoElement = null; mainVideoUrl = null; isAdPlaying = false; setTimeout(setupObserver, 1000); // Даем время на загрузку новой страницы }; // Инициализация setupObserver(); document.addEventListener('yt-navigate-finish', handleNavigation); window.addEventListener('yt-navigate-finish', handleNavigation); } // Запуск if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();