Greasy Fork

Youtube HD Premium

自动切换到你预先设定的画质。会优先使用Premium比特率。

目前为 2024-07-01 提交的版本。查看 最新版本

// ==UserScript==
// @name          Youtube HD Premium
// @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_hd_namespace
// @version       2024.07.01.2
// @match         *://www.youtube.com/*
// @grant         GM.getValue
// @grant         GM.setValue
// @license       MIT
// @description      Automcatically switches to your pre-selected resolution. Enables premium when possible.
// @description:zh-TW   自動切換到你預先設定的畫質。會優先使用Premium位元率。
// @description:zh-CN   自动切换到你预先设定的画质。会优先使用Premium比特率。
// @description:ja      自動的に設定した画質に替わります。Premiumのビットレートを優先的に選択します。

// ==/UserScript==

/*jshint esversion: 11 */

(function() {
    "use strict";

    // --- SETTINGS -------

    // PLEASE NOTE:
    // Settings will be saved the first time the script is loaded so that your changes aren't undone by an update.
    // If you want to make adjustments, please set "overwriteStoredSettings" to true.
    // Otherwise, your settings changes will NOT have an effect because it will use the saved settings.
    // After the script has next been run by loading a video with "overwriteStoredSettings" as true, your settings will be updated.
    // Then after that, you can set it to false again to prevent your settings from being changed by an update.

    let settings = {
        targetRes: "hd2160",
        // Target Resolution to always set to. If not available, the next best resolution will be used.
        // Choices for targetRes are currently:
        //   "highres" >= ( 8K / 4320p / QUHD  )
        //   "hd2880"   = ( 5K / 2880p /  UHD+ )
        //   "hd2160"   = ( 4K / 2160p /  UHD  )
        //   "hd1440"   = (      1440p /  QHD  )
        //   "hd1080"   = (      1080p /  FHD  )
        //   "hd720"    = (       720p /   HD  )
        //   "large"    = (       480p         )
        //   "medium"   = (       360p         )
        //   "small"    = (       240p         )
        //   "tiny"     = (       144p         )
        //   "auto"     = (       auto         )

        // This make it so the scripts uses the settings coded here instead of a seperate save file.
        overwriteStoredSettings: false
    };

    // --------------------
    // --- GLOBALS --------
    // --------------------

    const DEBUG = false;
    const resolutions = ['highres', 'hd2880', 'hd2160', 'hd1440', 'hd1080', 'hd720', 'large', 'medium', 'small', 'tiny', 'auto'];
    const ranks = {
        highres: "10",
        hd2880:  "9",
        hd2160:  "8",
        hd1440:  "7",
        hd1080:  "6",
        hd720:   "5",
        large:   "4",
        medium:  "3",
        small:   "2",
        tiny:    "1",
        auto:    "0"
    };

    let doc = document, win = window;
    let vidId = null;

    // --------------------

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

    // --------------------
    // Attempt to set the video resolution to target quality or the next best quality
    function setResolution() {
        let ytPlayer = doc.getElementById("movie_player") || doc.getElementsByClassName("html5-video-player")[0];
        if (!isValidVideo(ytPlayer)) return;

        vidId = ytPlayer.getVideoData().video_id;

        let target = settings.targetRes.toLowerCase();
        if (target == "auto") {
            ytPlayer.setPlaybackQualityRange('auto');
            return;
        }

        let limitList = ytPlayer.getAvailableQualityLevels();
        let limit = limitList[0];
        if (ranks[target] > ranks[limit]) {
            target = limit;
        }

        let premiumIndicator = "Premium";
        let premiumData = ytPlayer.getAvailableQualityData().find(q => q.quality == target && q.qualityLabel.includes(premiumIndicator) && q.isPlayable);
        ytPlayer.setPlaybackQualityRange(target, target, premiumData?.formatId);
        debugLog("Set quality to: " + target + (premiumData ? " Premium" : ""));
    }

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

        if (vidId == ytPlayer.getVideoData().video_id) {
            debugLog("Duplicate load");
            return false;
        }
        return true;
    }

    async function applySettings() {
        if (typeof GM != 'undefined' && GM.getValue && GM.setValue) {
            let settingsSaved = await GM.getValue("SettingsSaved");
            if (settings.overwriteStoredSettings || !settingsSaved) {
                Object.entries(settings).forEach(([k, v]) => GM.setValue(k, v));
                await GM.setValue("SettingsSaved", true);
            } else {
                await Promise.all(
                    Object.keys(settings).map(k => { let newval = GM.getValue(k); return newval.then(v => [k, v]); })
                ).then((c) => c.forEach(([nk, nv]) => {
                    if (settings[nk] !== null && nk !== "overwriteStoredSettings") {
                        settings[nk] = nv;
                    }
                }));
            }
            debugLog(Object.entries(settings).map(([k, v]) => k + ": " + v).join(", "));
        }
    }

    win.addEventListener("loadstart", setResolution, true);
    applySettings().then(setResolution);
})();