YouTube 播放速度记忆

自动切换到你预先设定的播放速度。

目前为 2024-08-15 提交的版本。查看 最新版本

// ==UserScript==
// @name                Youtube Remember Speed
// @name:zh-TW          YouTube 播放速度記憶
// @name:zh-CN          YouTube 播放速度记忆
// @name:ja             YouTube 再生速度メモリー
// @icon                data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAAXNSR0IArs4c6QAAAqhJREFUaEPtmc9rE0EUxz+DjSh6UAQRxP4F9uhBRKjipef+FwqtoZdYEk3U4jGn0FJ6KrQnj6X0EKVKKIi9tAotPZSCYilFoq0/sK1Z92V329XGENiZSRZ2LtllZ9+8z/e9ncy8UcS8qZj7TwLQ7ggmEUgiEFGB/6aQAxeBq8Al4GxonDPAydD9+dB1qkFfefy9iZ9fgRrwC/jh96v6vz+Bj8B7BduNbBwDcOA6UABuAyciCqTr9d/ACxf0oYI3YaOHAA71KfWpq8QDF6BTP27H9/GRArk+ctSBZ0BGl2SG7YwoyB4COF66lDtY+X/1EPVvKXhVTxUHKsANw6rpNl9RcFM50A1sxEj9QAiJQrcA9LvT5XPd8liy1y8Ad4GSpQF1D3NPAO4DRd2WLdlL6wUYH4dKBSYnLfmPZoDZWejrg/l5GByE5WXTIIYAxO1aDaamYGgIthsuY3TAGQQI3KtWoVCAUgkODnQ4HbZhASAYbnUV0mmYm9MJYREgcHtmxvs+1td1gLQBQNze24OxMchmYXc3CkibAOQDl6k2k4GtrZgBLC56KbSwEMXx4F2LEdjchHweJia8KVZPswCwvw+jo5DLwc6OHrePrBgGKJdhYABWVnQ7bjiF1ta8OV+WFmab5ghMT8PSEhSL3lRpvmkGSKVAct5eqwPEfkMT+y3lZeBDbDf1kq6xLqv4AL3AyxhFQUoqvQpeh2ujI+46cdjeBBJppL9Li34UBCYP5Do4ErKIeiLV82PF3UAPB64Bj4E7biW4K5JO+l6WvajUbqW8/jZsttkBxwWgB7gCnPZfCg4z5P6UH6lzTfyUgxGp7ctBRdBkBxNsjiWXv4Seyd93+DDkG/AJeKfgc6NxOvUcoOXYJQAtS2WoYxIBQ8K2bDaJQMtSGer4B8aT1sve/dr7AAAAAElFTkSuQmCC
// @author              ElectroKnight22
// @namespace           electroknight22_youtube_remember_playback_rate_namespace
// @version             1.0.1
// @match               *://www.youtube.com/*
// @exclude             *://www.youtube.com/live_chat*
// @grant               GM.getValue
// @grant               GM.setValue
// @grant               GM.deleteValue
// @grant               GM.listValues
// @grant               GM_registerMenuCommand
// @grant               GM_unregisterMenuCommand
// @license             MIT
// @description         Automcatically switches to your pre-selected speed.
// @description:zh-TW   自動切換到你預先設定的播放速度。
// @description:zh-CN   自动切换到你预先设定的播放速度。
// @description:ja      自動的に設定した再生速度に替わります。
// ==/UserScript==

/*jshint esversion: 11 */

(function() {
    "use strict";

    const DEBUG = false;

    const DEFAULT_SETTINGS = {
        targetSpeed: 1
    };

    const speeds = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2];

    let userSettings = { ...DEFAULT_SETTINGS };
    let menuCommandIds = [];

    let doc = document, win = window;

    // --------------------
    // --- FUNCTIONS ------
    // --------------------

    function debugLog(message, shouldShow = true) {
        if (DEBUG && shouldShow) {
            console.log("YTRS DEBUG | " + message);
        }
    }

    // --------------------
    // Attempt to set the video resolution to target quality or the next best quality
    function setSpeed(targetSpeed) {

        let ytPlayer = doc.getElementById("movie_player") || doc.getElementsByClassName("html5-video-player")[0];

        if (!isValidVideo(ytPlayer)) return;

        ytPlayer.setPlaybackRate(targetSpeed);
        debugLog("Trying to set speed to: " + targetSpeed + "x");

    }

    function isValidVideo(ytPlayer, force) {

        if (!ytPlayer?.getAvailableQualityLabels()[0]) {
            debugLog("Video data missing");
            return false;
        }

        if (win.location.href.startsWith("https://www.youtube.com/shorts/")) {
            debugLog("Skipping Youtube Shorts");
            return false;
        }

        return true;
    }

    // --------------------
    // Functions for the speed selection menu

    function createSpeedMenu() {
        GM_registerMenuCommand("Set Speed (show/hide)", () => {
            menuCommandIds.length ? removeSpeedMenuItems() : showSpeedMenuItems();
        }, {
            autoClose: false
        });
    }

    function showSpeedMenuItems() {
        removeSpeedMenuItems();
        speeds.forEach((speed) => {
            let speedText = speed + "x";
            if (speed === userSettings.targetSpeed) {
                speedText += " (selected)";
            }
            let menuCommandId = GM_registerMenuCommand(speedText, () => {
                setSelectedSpeed(speed);
            }, {
                autoClose: false,
            });
            menuCommandIds.push(menuCommandId);
        });
    }

    function removeSpeedMenuItems() {
        while (menuCommandIds.length) {
            GM_unregisterMenuCommand(menuCommandIds.pop());
        }
    }

    function setSelectedSpeed(speed) {
        if (userSettings.targetSpeed == speed) return;
        userSettings.targetSpeed = speed;
        GM.setValue('targetSpeed', speed);
        removeSpeedMenuItems();
        showSpeedMenuItems();
        setSpeed(speed);
    }

    // --------------------
    // Sync settings with locally stored values
    async function applySettings() {
        try {
            // Get all keys from GM
            const storedValues = await GM.listValues();

            // Write any missing key-value pairs from DEFAULT_SETTINGS to GM
            await Promise.all(Object.entries(DEFAULT_SETTINGS).map(async ([key, value]) => {
                if (!storedValues.includes(key)) {
                    await GM.setValue(key, value);
                }
            }));

            // Delete any extra keys in GM that are not in DEFAULT_SETTINGS
            await Promise.all(storedValues.map(async key => {
                if (!(key in DEFAULT_SETTINGS)) {
                    await GM.deleteValue(key);
                }
            }));

            // Retrieve and update user settings from GM
            await Promise.all(
                storedValues.map(key => GM.getValue(key).then(value => [key, value]))
            ).then(keyValuePairs => keyValuePairs.forEach(([newKey, newValue]) => {
                userSettings[newKey] = newValue;
            }));

            debugLog(Object.entries(userSettings).map(([key, value]) => key + ": " + value).join(", "));
        } catch (error) {
            debugLog("Error when applying settings: " + error.message);
        }
    }

    // --------------------
    // Main function
    function main() {
        if (win.self == win.top) { createSpeedMenu(); }
        setSpeed(userSettings.targetSpeed);
        win.addEventListener("loadstart", () => { setSpeed(userSettings.targetSpeed); }, true);
    }

    // --------------------
    // Entry Point
    applySettings().then(main);
})();