YT Speed Controller

Lightweight speed control for YouTube with persistence

// ==UserScript==
// @name         YT Speed Controller
// @namespace    -/-
// @version      1.0.1
// @description  Lightweight speed control for YouTube with persistence
// @author       Dataraj
// @match        https://www.youtube.com/*
// @license     MIT
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    const STORAGE_KEY = 'youtube-speed-value';
    const SPEED_STEP = 0.1;
    let speedDisplay = null;
    let isInitialized = false;

    // Debounce function to limit execution frequency
    function debounce(func, wait) {
        let timeout;
        return function executedFunction(...args) {
            const later = () => {
                clearTimeout(timeout);
                func(...args);
            };
            clearTimeout(timeout);
            timeout = setTimeout(later, wait);
        };
    }

    function initSpeedControl() {
        if (isInitialized) return;

        const video = document.querySelector('video');
        const timeDisplay = document.querySelector('.ytp-time-display');
        if (!video || !timeDisplay) return;

        // Create speed display if not exists
        if (!speedDisplay) {
            speedDisplay = document.createElement('span');
            speedDisplay.style.marginLeft = '10px';
            timeDisplay.appendChild(speedDisplay);
        }

        // Apply saved speed
        const savedSpeed = parseFloat(localStorage.getItem(STORAGE_KEY)) || 1.0;
        video.playbackRate = savedSpeed;
        updateDisplay(savedSpeed);

        // Show guide only once per session
        if (!sessionStorage.getItem('guide-shown')) {
            showTemporaryGuide();
            sessionStorage.setItem('guide-shown', 'true');
        }

        isInitialized = true;
    }

    // Simplified speed display update
    function updateDisplay(speed) {
        if (speedDisplay) {
            speedDisplay.textContent = speed === 1 ? '' : `${speed.toFixed(1)}x`;
        }
    }

    // Optimized speed indicator
    function showSpeedIndicator(speed) {
        const bezel = document.querySelector('.ytp-bezel-text');
        if (!bezel) return;

        const container = bezel.parentNode.parentNode;
        bezel.textContent = `${speed.toFixed(1)}x`;
        container.style.display = '';

        setTimeout(() => container.style.display = 'none', 500);
    }

    // Simplified guide
    function showTemporaryGuide() {
        const guide = document.createElement('div');
        guide.innerHTML = `
            <div style="position:fixed;bottom:20px;right:20px;background:rgba(0,0,0,0.8);
                color:white;padding:12px;border-radius:6px;z-index:9999;font-family:Arial">
                <div>Speed Controls:</div>
                <div>] - Speed up</div>
                <div>[ - Slow down</div>
                <div>p - Reset to 1x</div>
            </div>
        `;
        document.body.appendChild(guide);
        setTimeout(() => guide.remove(), 5000);
    }

    // Efficient keyboard handler
    function handleKeypress(e) {
        if (document.activeElement.tagName === 'INPUT') return;

        const video = document.querySelector('video');
        if (!video) return;

        let newSpeed = video.playbackRate;

        switch(e.key) {
            case ']':
                newSpeed = Math.min(16, newSpeed + SPEED_STEP);
                break;
            case '[':
                newSpeed = Math.max(0.1, newSpeed - SPEED_STEP);
                break;
            case 'p':
                newSpeed = 1.0;
                break;
            default:
                return;
        }

        video.playbackRate = newSpeed;
        localStorage.setItem(STORAGE_KEY, newSpeed.toString());
        updateDisplay(newSpeed);
        showSpeedIndicator(newSpeed);
    }

    // Optimized initialization
    function init() {
        // Remove any existing event listeners
        document.removeEventListener('keydown', handleKeypress);

        // Add single event listener for keyboard controls
        document.addEventListener('keydown', handleKeypress);

        // Debounced initialization for navigation changes
        const debouncedInit = debounce(() => {
            if (window.location.pathname.includes('/watch')) {
                isInitialized = false;
                initSpeedControl();
            }
        }, 1000);

        // Listen for URL changes
        let lastUrl = location.href;
        new MutationObserver(() => {
            if (location.href !== lastUrl) {
                lastUrl = location.href;
                debouncedInit();
            }
        }).observe(document, {subtree: true, childList: true});

        // Initial setup
        debouncedInit();
    }

    // Start the script
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址