YouTube Speed Toggle

Add a speed toggle button to YouTube player controls, switch between 1x and 2x with one click

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği yüklemek için Tampermonkey gibi bir uzantı yüklemeniz gerekir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği indirebilmeniz için ayrıca Tampermonkey gibi bir eklenti kurmanız gerekmektedir.

Bu komut dosyasını yüklemek için bir kullanıcı komut dosyası yöneticisi uzantısı yüklemeniz gerekecek.

(Zaten bir kullanıcı komut dosyası yöneticim var, kurmama izin verin!)

Advertisement:

Bu stili yüklemek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için Stylus gibi bir uzantı kurmanız gerekir.

Bu stili yükleyebilmek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı kurmanız gerekir.

Bu stili yükleyebilmek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

(Zateb bir user-style yöneticim var, yükleyeyim!)

Advertisement:

// ==UserScript==
// @name         YouTube Speed Toggle
// @namespace    https://github.com/ywtaoo
// @version      1.0.2
// @description  Add a speed toggle button to YouTube player controls, switch between 1x and 2x with one click
// @author       ywtaoo
// @license      MIT
// @match        https://www.youtube.com/*
// @icon         https://www.youtube.com/favicon.ico
// @homepageURL  https://github.com/ywtaoo/youtube_speed_toggle_shortcut
// @supportURL   https://github.com/ywtaoo/youtube_speed_toggle_shortcut/issues
// @grant        none
// @run-at       document-idle
// ==/UserScript==

(function() {
    'use strict';

    const BUTTON_ID = 'yt-speed-toggle-btn';
    const SPEEDS = [1, 2];
    let currentSpeedIndex = 0;
    let observedVideo = null;

    /**
     * Format playback speed for display
     */
    function formatSpeed(speed) {
        return parseFloat(speed.toFixed(2)) + 'x';
    }

    /**
     * Get YouTube player instance
     */
    function getPlayer() {
        return document.querySelector('#movie_player');
    }

    /**
     * Get current playback speed
     */
    function getPlaybackSpeed() {
        const player = getPlayer();
        if (player && typeof player.getPlaybackRate === 'function') {
            const playerSpeed = Number(player.getPlaybackRate());
            if (!Number.isNaN(playerSpeed)) {
                return playerSpeed;
            }
        }

        const video = document.querySelector('video');
        if (video) {
            return video.playbackRate;
        }

        return null;
    }

    /**
     * Set playback speed
     */
    function setPlaybackSpeed(speed) {
        const player = getPlayer();
        const video = document.querySelector('video');

        if (!player && !video) return false;

        if (player && typeof player.setPlaybackRate === 'function') {
            player.setPlaybackRate(speed);
        }

        if (video && video.playbackRate !== speed) {
            video.playbackRate = speed;
        }

        return true;
    }

    /**
     * Listen for speed changes on the current video element
     */
    function observeVideoRateChanges() {
        const video = document.querySelector('video');
        if (!video || video === observedVideo) return;

        if (observedVideo) {
            observedVideo.removeEventListener('ratechange', syncButtonWithVideo);
        }

        observedVideo = video;
        observedVideo.addEventListener('ratechange', syncButtonWithVideo);
    }

    /**
     * Create speed toggle button
     */
    function createSpeedButton() {
        const button = document.createElement('button');
        button.id = BUTTON_ID;
        button.className = 'ytp-button';
        button.title = 'Toggle playback speed';
        button.style.cssText = `
            display: flex;
            align-items: center;
            justify-content: center;
            width: auto;
            min-width: 40px;
            height: 100%;
            padding: 0 8px;
            font-size: 14px;
            font-weight: 500;
            color: #fff;
            opacity: 0.9;
            cursor: pointer;
            transition: opacity 0.1s ease;
            line-height: 1;
        `;
        button.textContent = '1x';

        button.addEventListener('mouseenter', () => {
            button.style.opacity = '1';
        });

        button.addEventListener('mouseleave', () => {
            button.style.opacity = '0.9';
        });

        button.addEventListener('click', (e) => {
            e.stopPropagation();
            toggleSpeed();
        });

        return button;
    }

    /**
     * Toggle playback speed
     */
    function toggleSpeed() {
        currentSpeedIndex = (currentSpeedIndex + 1) % SPEEDS.length;
        const newSpeed = SPEEDS[currentSpeedIndex];

        if (!setPlaybackSpeed(newSpeed)) return;

        updateButtonDisplay(newSpeed);
    }

    /**
     * Update button display
     */
    function updateButtonDisplay(speed) {
        const button = document.getElementById(BUTTON_ID);
        if (button) {
            const speedText = formatSpeed(speed);
            button.textContent = speedText;
            button.title = `Current speed: ${speedText} (click to toggle)`;
        }
    }

    /**
     * Sync button state with current video speed
     */
    function syncButtonWithVideo() {
        const button = document.getElementById(BUTTON_ID);

        if (button) {
            const currentRate = getPlaybackSpeed();
            if (currentRate === null) return;

            const speedIndex = SPEEDS.indexOf(currentRate);

            if (speedIndex !== -1) {
                currentSpeedIndex = speedIndex;
            } else {
                // If current speed is not in preset list, make the next click switch to 2x
                currentSpeedIndex = 0;
            }

            updateButtonDisplay(currentRate);
        }

        observeVideoRateChanges();
    }

    /**
     * Inject button into player controls
     */
    function injectButton() {
        // Check if button already exists
        if (document.getElementById(BUTTON_ID)) {
            syncButtonWithVideo();
            return;
        }

        // Find right controls area (Delhi player uses sub-containers)
        const targetContainer = document.querySelector('.ytp-right-controls-left')
                             || document.querySelector('.ytp-right-controls');
        if (!targetContainer) return;

        const button = createSpeedButton();

        // Insert at the first position of the controls container
        targetContainer.insertBefore(button, targetContainer.firstChild);

        // Sync current video speed
        syncButtonWithVideo();

        // Listen for video speed changes (user may change speed via other methods)
        observeVideoRateChanges();

        console.log('[YouTube Speed Toggle] Button injected');
    }

    /**
     * Initialize script
     */
    function init() {
        // Try to inject immediately
        injectButton();

        // Use MutationObserver to watch for DOM changes
        // YouTube is a SPA, content loads dynamically
        const observer = new MutationObserver((mutations) => {
            // Check if player exists but button doesn't
            const player = document.querySelector('#movie_player');
            const button = document.getElementById(BUTTON_ID);

            if (player && !button) {
                injectButton();
            }
        });

        observer.observe(document.body, {
            childList: true,
            subtree: true
        });

        // Listen for YouTube SPA navigation
        // YouTube uses History API for page navigation
        const originalPushState = history.pushState;
        history.pushState = function(...args) {
            originalPushState.apply(this, args);
            // Delay execution to wait for new page content to load
            setTimeout(injectButton, 1000);
        };

        const originalReplaceState = history.replaceState;
        history.replaceState = function(...args) {
            originalReplaceState.apply(this, args);
            setTimeout(injectButton, 1000);
        };

        window.addEventListener('popstate', () => {
            setTimeout(injectButton, 1000);
        });

        // Listen for YouTube's yt-navigate-finish event (more reliable)
        window.addEventListener('yt-navigate-finish', () => {
            setTimeout(injectButton, 500);
        });
    }

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

})();