// ==UserScript== // @name Enhanced Video Speed Controller // @name:zh-CN 增强视频速度控制器 // @name:zh-TW 增強影片速度控制器 // @name:ja 拡張ビデオスピードコントローラー // @name:ko 향상된 비디오 속도 컨트롤러 // @name:es Controlador de Velocidad de Video Mejorado // @name:fr Contrôleur de Vitesse Vidéo Amélioré // @name:de Erweiterte Video-Geschwindigkeitskontrolle // @name:pt Controlador de Velocidade de Vídeo Aprimorado // @name:pt-BR Controlador de Velocidade de Vídeo Aprimorado // @name:ru Расширенный Контроллер Скорости Видео // @name:it Controllore di Velocità Video Avanzato // @name:nl Verbeterde Video Snelheidscontroller // @name:ar تحكم محسن في سرعة الفيديو // @name:hi उन्नत वीडियो गति नियंत्रक // @name:th ตัวควบคุมความเร็ววิดีโอขั้นสูง // @name:vi Bộ Điều Khiển Tốc Độ Video Nâng Cao // @name:id Pengontrol Kecepatan Video Tingkat Lanjut // @name:tr Gelişmiş Video Hız Kontrolcüsü // @name:pl Zaawansowany Kontroler Prędkości Wideo // @name:cs Pokročilý Ovladač Rychlosti Videa // @name:hu Fejlett Videó Sebesség Vezérlő // @name:sv Avancerad Videohastighetscontroller // @name:da Avanceret Videohastigheds Controller // @name:no Avansert Videohastighets Kontroller // @name:fi Edistynyt Videonopeus Ohjain // @name:he בקר מהירות וידאו מתקדם // @name:fa کنترل کننده پیشرفته سرعت ویدیو // @name:uk Розширений Контролер Швидкості Відео // @name:bg Разширен Контролер на Скоростта на Видеото // @name:ro Controler Avansat de Viteză Video // @name:hr Napredni Kontroler Brzine Videa // @name:sk Pokročilý Ovládač Rýchlosti Videa // @name:sl Napreden Nadzornik Hitrosti Videa // @name:et Täiustatud Video Kiiruse Kontroller // @name:lv Uzlabots Video Ātruma Kontrolieris // @name:lt Išplėstinis Vaizdo Įrašo Greičio Valdiklis // @name:el Προηγμένος Ελεγκτής Ταχύτητας Βίντεο // @namespace http://tampermonkey.net/ // @version 1.0.1 // @description Universal video speed control for HTML5, Video.js, JW Player, Plyr, HLS.js, YouTube, and more with intelligent detection // @description:zh-CN 为HTML5、Video.js、JW Player、Plyr、HLS.js、YouTube等提供通用视频速度控制,具有智能检测功能 // @description:zh-TW 為HTML5、Video.js、JW Player、Plyr、HLS.js、YouTube等提供通用影片速度控制,具有智慧檢測功能 // @description:ja HTML5、Video.js、JW Player、Plyr、HLS.js、YouTubeなどに対応したインテリジェント検出機能付きユニバーサル動画速度制御 // @description:ko HTML5, Video.js, JW Player, Plyr, HLS.js, YouTube 등을 위한 지능형 감지 기능이 있는 범용 비디오 속도 제어 // @description:es Control universal de velocidad de video para HTML5, Video.js, JW Player, Plyr, HLS.js, YouTube y más con detección inteligente // @description:fr Contrôle universel de vitesse vidéo pour HTML5, Video.js, JW Player, Plyr, HLS.js, YouTube et plus avec détection intelligente // @description:de Universelle Videogeschwindigkeitskontrolle für HTML5, Video.js, JW Player, Plyr, HLS.js, YouTube und mehr mit intelligenter Erkennung // @description:pt Controle universal de velocidade de vídeo para HTML5, Video.js, JW Player, Plyr, HLS.js, YouTube e mais com detecção inteligente // @description:pt-BR Controle universal de velocidade de vídeo para HTML5, Video.js, JW Player, Plyr, HLS.js, YouTube e mais com detecção inteligente // @description:ru Универсальное управление скоростью видео для HTML5, Video.js, JW Player, Plyr, HLS.js, YouTube и других с интеллектуальным обнаружением // @description:it Controllo universale della velocità video per HTML5, Video.js, JW Player, Plyr, HLS.js, YouTube e altri con rilevamento intelligente // @description:nl Universele videosnelheidscontrole voor HTML5, Video.js, JW Player, Plyr, HLS.js, YouTube en meer met intelligente detectie // @description:ar تحكم عالمي في سرعة الفيديو لـ HTML5، Video.js، JW Player، Plyr، HLS.js، YouTube والمزيد مع الكشف الذكي // @description:hi HTML5, Video.js, JW Player, Plyr, HLS.js, YouTube और अन्य के लिए बुद्धिमान पहचान के साथ सार्वभौमिक वीडियो गति नियंत्रण // @description:th การควบคุมความเร็ววิดีโอสากลสำหรับ HTML5, Video.js, JW Player, Plyr, HLS.js, YouTube และอื่น ๆ พร้อมการตรวจจับอัจฉริยะ // @description:vi Điều khiển tốc độ video phổ quát cho HTML5, Video.js, JW Player, Plyr, HLS.js, YouTube và nhiều hơn nữa với tính năng phát hiện thông minh // @description:id Kontrol kecepatan video universal untuk HTML5, Video.js, JW Player, Plyr, HLS.js, YouTube dan lainnya dengan deteksi cerdas // @description:tr HTML5, Video.js, JW Player, Plyr, HLS.js, YouTube ve daha fazlası için akıllı algılamalı evrensel video hız kontrolü // @description:pl Uniwersalna kontrola prędkości wideo dla HTML5, Video.js, JW Player, Plyr, HLS.js, YouTube i innych z inteligentnym wykrywaniem // @description:cs Univerzální ovládání rychlosti videa pro HTML5, Video.js, JW Player, Plyr, HLS.js, YouTube a další s inteligentní detekcí // @description:hu Univerzális videó sebesség vezérlés HTML5, Video.js, JW Player, Plyr, HLS.js, YouTube és mások számára intelligens felismeréssel // @description:sv Universell videohastighetscontrol för HTML5, Video.js, JW Player, Plyr, HLS.js, YouTube och mer med intelligent upptäckt // @description:da Universal videohastigheds kontrol for HTML5, Video.js, JW Player, Plyr, HLS.js, YouTube og mere med intelligent detektering // @description:no Universell videohastighets kontroll for HTML5, Video.js, JW Player, Plyr, HLS.js, YouTube og mer med intelligent deteksjon // @description:fi Universaali videonopeus hallinta HTML5, Video.js, JW Player, Plyr, HLS.js, YouTube ja muille älykkäällä tunnistuksella // @description:he בקרת מהירות וידאו אוניברסלית עבור HTML5, Video.js, JW Player, Plyr, HLS.js, YouTube ועוד עם זיהוי חכם // @description:fa کنترل سرعت ویدیوی جهانی برای HTML5، Video.js، JW Player، Plyr، HLS.js، YouTube و بیشتر با تشخیص هوشمند // @description:uk Універсальне керування швидкістю відео для HTML5, Video.js, JW Player, Plyr, HLS.js, YouTube та інших з інтелектуальним виявленням // @description:bg Универсален контрол на скоростта на видеото за HTML5, Video.js, JW Player, Plyr, HLS.js, YouTube и други с интелигентно откриване // @description:ro Control universal al vitezei video pentru HTML5, Video.js, JW Player, Plyr, HLS.js, YouTube și altele cu detecție inteligentă // @description:hr Univerzalna kontrola brzine videa za HTML5, Video.js, JW Player, Plyr, HLS.js, YouTube i ostalo s inteligentnim otkrivanjem // @description:sk Univerzálne ovládanie rýchlosti videa pre HTML5, Video.js, JW Player, Plyr, HLS.js, YouTube a ďalšie s inteligentnou detekciou // @description:sl Univerzalen nadzor hitrosti videa za HTML5, Video.js, JW Player, Plyr, HLS.js, YouTube in druge z inteligentnim zaznavanjem // @description:et Universaalne video kiiruse kontroll HTML5, Video.js, JW Player, Plyr, HLS.js, YouTube ja teiste jaoks nutika tuvastamisega // @description:lv Universāla video ātruma kontrole HTML5, Video.js, JW Player, Plyr, HLS.js, YouTube un citiem ar inteliģentu noteikšanu // @description:lt Universalus vaizdo įrašo greičio valdymas HTML5, Video.js, JW Player, Plyr, HLS.js, YouTube ir kitiems su protingu aptikimu // @description:el Καθολικός έλεγχος ταχύτητας βίντεο για HTML5, Video.js, JW Player, Plyr, HLS.js, YouTube και άλλα με έξυπνη ανίχνευση // @author aspen138 (using Claude code) // @match *://*/* // @grant GM_setValue // @grant GM_getValue // @grant GM_addStyle // @grant GM_registerMenuCommand // @run-at document-idle // @license MIT // @downloadURL none // ==/UserScript== (function() { 'use strict'; // Configuration const CONFIG = { version: '2.0.0', defaultSpeed: 1.0, speedPresets: [0.5, 0.75, 1, 1.25, 1.5, 2], minSpeed: 0.25, maxSpeed: 4.0, speedStep: 0.25, detectionDelay: 1000, retryDelay: 500, persistKey: 'videoSpeedController', uiPosition: { right: '20px', top: '20px' } }; // Enhanced Video Speed Manager Class class EnhancedVideoSpeedManager { constructor() { this.detectedPlayers = []; this.currentSpeed = CONFIG.defaultSpeed; this.isInitialized = false; this.contentObserver = null; this.detectionTimer = null; this.ui = null; this.isUIHidden = true; // Start hidden by default // Player type definitions and detection patterns this.playerTypes = { HTML5: { selector: 'video', priority: 1, name: 'HTML5 Video' }, VIDEOJS: { selector: '.video-js', priority: 3, name: 'Video.js' }, JWPLAYER: { selector: '.jw-video, .jwplayer', priority: 3, name: 'JW Player' }, PLYR: { selector: '.plyr', priority: 2, name: 'Plyr' }, HLSJS: { selector: 'video[src*=".m3u8"], video[data-hls]', priority: 2, name: 'HLS.js' }, YOUTUBE: { selector: '#movie_player, .html5-video-player', priority: 4, name: 'YouTube' }, VIMEO: { selector: '.vp-player, [data-vimeo-player]', priority: 4, name: 'Vimeo' }, TWITCH: { selector: '.video-player, [data-a-target="video-player"]', priority: 4, name: 'Twitch' } }; this.init(); } async init() { console.log('Enhanced Video Speed Controller: Initializing...'); // Load saved settings await this.loadSettings(); // Wait for DOM to be ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => this.setup()); } else { this.setup(); } } setup() { // Create UI this.createUI(); // Initial player detection this.detectAllPlayers(); // Set up observers and event listeners this.setupContentObserver(); this.setupKeyboardShortcuts(); this.setupMenuCommands(); // Apply saved speed if (this.currentSpeed !== CONFIG.defaultSpeed) { setTimeout(() => this.setAllPlayersSpeed(this.currentSpeed), CONFIG.detectionDelay); } this.isInitialized = true; console.log('Enhanced Video Speed Controller: Initialization complete'); } // Player Detection System detectAllPlayers() { console.log('Enhanced Video Speed Controller: Scanning for video players...'); this.detectedPlayers = []; Object.keys(this.playerTypes).forEach(playerType => { const players = this.detectPlayerType(playerType); this.detectedPlayers.push(...players); }); // Sort by priority (higher priority first) this.detectedPlayers.sort((a, b) => b.priority - a.priority); const playerSummary = this.getPlayerSummary(); console.log('Enhanced Video Speed Controller: Detected players:', playerSummary); // Update UI this.updateUI(); return this.detectedPlayers; } detectPlayerType(playerType) { const config = this.playerTypes[playerType]; const elements = document.querySelectorAll(config.selector); const players = []; elements.forEach((element, index) => { const player = this.createPlayerInstance(playerType, element, index); if (player) { players.push(player); } }); if (players.length > 0) { console.log(`Enhanced Video Speed Controller: Found ${players.length} ${playerType} player(s)`); } return players; } createPlayerInstance(type, element, index) { const basePlayer = { type, element, index, id: `${type}_${index}`, priority: this.playerTypes[type].priority, name: this.playerTypes[type].name, currentSpeed: CONFIG.defaultSpeed }; switch (type) { case 'HTML5': return this.createHTML5Player(basePlayer); case 'VIDEOJS': return this.createVideoJSPlayer(basePlayer); case 'JWPLAYER': return this.createJWPlayer(basePlayer); case 'PLYR': return this.createPlyrPlayer(basePlayer); case 'HLSJS': return this.createHLSJSPlayer(basePlayer); case 'YOUTUBE': return this.createYouTubePlayer(basePlayer); case 'VIMEO': return this.createVimeoPlayer(basePlayer); case 'TWITCH': return this.createTwitchPlayer(basePlayer); default: return null; } } // Player Type Implementations createHTML5Player(basePlayer) { const video = basePlayer.element; if (!(video instanceof HTMLVideoElement)) return null; return { ...basePlayer, setSpeed: (speed) => { try { video.playbackRate = speed; basePlayer.currentSpeed = speed; return true; } catch (error) { console.error(`Enhanced Video Speed Controller: HTML5 speed error:`, error); return false; } }, getSpeed: () => video.playbackRate || CONFIG.defaultSpeed, isReady: () => video.readyState >= 1 }; } createVideoJSPlayer(basePlayer) { const container = basePlayer.element; let vjsPlayer = null; // Try multiple methods to get Video.js instance if (typeof videojs !== 'undefined') { try { vjsPlayer = videojs(container); } catch (e) { // Fallback methods vjsPlayer = container.player || window.videojs?.getPlayer?.(container); } } // Fallback to HTML5 if Video.js API not available const videoElement = container.querySelector('video'); if (!vjsPlayer && videoElement) { return this.createHTML5Player({ ...basePlayer, element: videoElement, type: 'HTML5_VIDEOJS_FALLBACK' }); } if (!vjsPlayer) return null; return { ...basePlayer, vjsInstance: vjsPlayer, setSpeed: (speed) => { try { if (vjsPlayer.playbackRate) { vjsPlayer.playbackRate(speed); } else if (vjsPlayer.tech_?.el_) { vjsPlayer.tech_.el_.playbackRate = speed; } basePlayer.currentSpeed = speed; return true; } catch (error) { console.error(`Enhanced Video Speed Controller: Video.js speed error:`, error); return false; } }, getSpeed: () => { try { return vjsPlayer.playbackRate ? vjsPlayer.playbackRate() : CONFIG.defaultSpeed; } catch (e) { return CONFIG.defaultSpeed; } }, isReady: () => { try { return vjsPlayer.readyState() >= 1; } catch (e) { return true; } } }; } createJWPlayer(basePlayer) { const container = basePlayer.element; let jwPlayer = null; if (typeof jwplayer !== 'undefined') { try { jwPlayer = jwplayer(container); } catch (e) { // Try alternative detection jwPlayer = container.jwplayer || window.jwplayer?.getPlayer?.(container); } } // Fallback to HTML5 const videoElement = container.querySelector('video'); if (!jwPlayer && videoElement) { return this.createHTML5Player({ ...basePlayer, element: videoElement, type: 'HTML5_JW_FALLBACK' }); } if (!jwPlayer) return null; return { ...basePlayer, jwInstance: jwPlayer, setSpeed: (speed) => { try { if (jwPlayer.setPlaybackRate) { jwPlayer.setPlaybackRate(speed); } else if (jwPlayer.getContainer) { const video = jwPlayer.getContainer().querySelector('video'); if (video) video.playbackRate = speed; } basePlayer.currentSpeed = speed; return true; } catch (error) { console.error(`Enhanced Video Speed Controller: JW Player speed error:`, error); return false; } }, getSpeed: () => { try { return jwPlayer.getPlaybackRate ? jwPlayer.getPlaybackRate() : CONFIG.defaultSpeed; } catch (e) { return CONFIG.defaultSpeed; } }, isReady: () => { try { return jwPlayer.getState && jwPlayer.getState() !== 'idle'; } catch (e) { return true; } } }; } createPlyrPlayer(basePlayer) { const container = basePlayer.element; const plyrPlayer = container.plyr; // Fallback to HTML5 const videoElement = container.querySelector('video'); if (!plyrPlayer && videoElement) { return this.createHTML5Player({ ...basePlayer, element: videoElement, type: 'HTML5_PLYR_FALLBACK' }); } if (!plyrPlayer) return null; return { ...basePlayer, plyrInstance: plyrPlayer, setSpeed: (speed) => { try { if (plyrPlayer.speed) { plyrPlayer.speed = speed; } else if (plyrPlayer.media) { plyrPlayer.media.playbackRate = speed; } basePlayer.currentSpeed = speed; return true; } catch (error) { console.error(`Enhanced Video Speed Controller: Plyr speed error:`, error); return false; } }, getSpeed: () => { try { return plyrPlayer.speed || plyrPlayer.media?.playbackRate || CONFIG.defaultSpeed; } catch (e) { return CONFIG.defaultSpeed; } }, isReady: () => plyrPlayer.ready }; } createHLSJSPlayer(basePlayer) { // HLS.js uses standard HTML5 video elements return this.createHTML5Player({ ...basePlayer, type: 'HLSJS_HTML5' }); } createYouTubePlayer(basePlayer) { const container = basePlayer.element; return { ...basePlayer, setSpeed: (speed) => { const video = container.querySelector('video'); if (video) { try { video.playbackRate = speed; basePlayer.currentSpeed = speed; return true; } catch (error) { console.error(`Enhanced Video Speed Controller: YouTube speed error:`, error); return false; } } return false; }, getSpeed: () => { const video = container.querySelector('video'); return video ? video.playbackRate || CONFIG.defaultSpeed : CONFIG.defaultSpeed; }, isReady: () => true }; } createVimeoPlayer(basePlayer) { const container = basePlayer.element; return { ...basePlayer, setSpeed: (speed) => { const video = container.querySelector('video'); if (video) { try { video.playbackRate = speed; basePlayer.currentSpeed = speed; return true; } catch (error) { console.error(`Enhanced Video Speed Controller: Vimeo speed error:`, error); return false; } } return false; }, getSpeed: () => { const video = container.querySelector('video'); return video ? video.playbackRate || CONFIG.defaultSpeed : CONFIG.defaultSpeed; }, isReady: () => true }; } createTwitchPlayer(basePlayer) { const container = basePlayer.element; return { ...basePlayer, setSpeed: (speed) => { const video = container.querySelector('video'); if (video) { try { video.playbackRate = speed; basePlayer.currentSpeed = speed; return true; } catch (error) { console.error(`Enhanced Video Speed Controller: Twitch speed error:`, error); return false; } } return false; }, getSpeed: () => { const video = container.querySelector('video'); return video ? video.playbackRate || CONFIG.defaultSpeed : CONFIG.defaultSpeed; }, isReady: () => true }; } // Content Observer for Dynamic Detection setupContentObserver() { this.contentObserver = new MutationObserver((mutations) => { let shouldDetect = false; mutations.forEach((mutation) => { mutation.addedNodes.forEach((node) => { if (node.nodeType === Node.ELEMENT_NODE) { const hasVideo = node.tagName === 'VIDEO' || node.querySelector && Object.values(this.playerTypes).some(type => node.querySelector(type.selector) ); if (hasVideo) { shouldDetect = true; } } }); }); if (shouldDetect) { clearTimeout(this.detectionTimer); this.detectionTimer = setTimeout(() => { console.log('Enhanced Video Speed Controller: New content detected, re-scanning...'); this.detectAllPlayers(); }, CONFIG.retryDelay); } }); this.contentObserver.observe(document.body || document.documentElement, { childList: true, subtree: true, attributes: true, attributeFilter: ['class', 'id'] }); } // Speed Control Methods setAllPlayersSpeed(speed) { if (isNaN(speed) || speed <= 0 || speed > 10) { console.error('Enhanced Video Speed Controller: Invalid speed value:', speed); return false; } this.currentSpeed = speed; let successCount = 0; this.detectedPlayers.forEach(player => { try { if (player.setSpeed && player.setSpeed(speed)) { successCount++; } } catch (error) { console.error(`Enhanced Video Speed Controller: Failed to set speed for ${player.type}:`, error); } }); console.log(`Enhanced Video Speed Controller: Set speed to ${speed}x for ${successCount}/${this.detectedPlayers.length} players`); // Update UI this.updateUI(); // Save settings this.saveSettings(); // Re-detect and apply to new players setTimeout(() => { this.detectAllPlayers(); this.detectedPlayers.forEach(player => { if (player.currentSpeed !== speed && player.setSpeed) { player.setSpeed(speed); } }); }, CONFIG.retryDelay); return successCount > 0; } adjustSpeed(delta) { const newSpeed = Math.max(CONFIG.minSpeed, Math.min(CONFIG.maxSpeed, this.currentSpeed + delta)); this.setAllPlayersSpeed(parseFloat(newSpeed.toFixed(2))); } resetSpeed() { this.setAllPlayersSpeed(CONFIG.defaultSpeed); } // UI Creation and Management createUI() { if (this.ui) return; // Create main container this.ui = document.createElement('div'); this.ui.id = 'enhanced-video-speed-controller'; this.ui.innerHTML = `