Miigon's Youtube script

useful enhancements for my own youtube enjoyments

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Miigon's Youtube script
// @namespace    https://blog.miigon.net/
// @version      0.6
// @description  useful enhancements for my own youtube enjoyments
// @author       Miigon
// @match        https://www.youtube.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=youtube.com
// @grant        none
// @license      MIT
// ==/UserScript==

/* Long press right arrow key to speed up video playback */
const RIGHT_ARROW_SPEEDUP_PRESS_TIME_MS = 500; // key hold time before speeding up
const SPEED_UP_PLAYBACK_RATE = 3;
/* Seeking in youtube shorts */
const SHORTS_SEEK_SECS = 5;

(function() {
    'use strict';

    let mlog = (...args)=>{console.log("miigon's ytb script:", ...args)}

    let right_pressed = false;
    let is_speeding_up = false;
    let speeding_timeout = -1; // timeout waiting before starting speeding up
    let last_playback_rate = 1;

    let is_shorts = () => window.location.href.includes("/shorts/");
    let is_editing = () => document.activeElement.contentEditable == "true";

    // youtube uses javascript to navigate around.
    // so cached_player is only valid if the page is still the same.
    let cached_player_href = null;
    let cached_player = null;
    let getPlayerElement = () => {
        if(window.location.href == cached_player_href && cached_player) {
            return cached_player;
        }

        mlog("invalidate player element cache")
        cached_player = null;
        let ytd_player = [...document.getElementsByTagName("ytd-player")];
        if(is_shorts()) {
            cached_player = ytd_player.find(v=>v.className.indexOf("ytd-shorts")!=-1);
        } else { // normal full-length video
            cached_player = ytd_player.find(v=>
                v.className.indexOf("preview") == -1
                && v.className.indexOf("ytd-shorts") == -1
            );
        }
        if(cached_player != null) {
            cached_player_href = window.location.href;
        }
        return cached_player;
    }
    let getPlayer = () => getPlayerElement().getPlayer();
    let getPlayerVideoTag = () => getPlayerElement().getElementsByTagName("video")[0];



    document.addEventListener("keydown", (e)=>{
        let used = false;

        // skip hotkeys when editing comment etc.
        if(is_editing()) return;

        if(e.key === "ArrowLeft") {
            // enables left seeking in shorts
            // ArrowRight is handled in keyup instead of keydown.
            if(is_shorts()){
                getPlayer().seekBy(-SHORTS_SEEK_SECS);
            }
        }

        if(e.miigon_ignore) {
            return;
        }

        if(e.key === "ArrowRight") {
            if(right_pressed == false){
                right_pressed = true;
                mlog("right pressed");
                speeding_timeout = setTimeout(()=>{
                    speeding_timeout = -1;
                    is_speeding_up = true;
                    let v = getPlayerVideoTag();
                    last_playback_rate = v.playbackRate;
                    v.playbackRate = SPEED_UP_PLAYBACK_RATE;
                    mlog("speeding up");

                    used = true;
                }, RIGHT_ARROW_SPEEDUP_PRESS_TIME_MS);
            }
            used = true;
        }

        if(used){
            e.stopPropagation();
            e.preventDefault();
        }
    }, /*useCapture*/true);

    document.addEventListener("keyup", (e)=>{
        let used = false;

        // skip hotkeys when editing comment etc.
        if(is_editing()) return;

        if(e.key === "ArrowRight") {
            right_pressed = false;
            if(speeding_timeout != -1) { // keyup before speeding up begins
                clearTimeout(speeding_timeout);
                speeding_timeout = -1;

                if(is_shorts()){
                    // shorts, so simulating a right arrow keypress wont work
                    // tell the player to seek directly.
                    // this enables right seeking in shorts
                    getPlayer().seekBy(SHORTS_SEEK_SECS);
                } else { // normal video
                    // dispatch a normal right arrow keypress event.
                    let event = new KeyboardEvent("keydown", {keyCode: 39})
                    event.miigon_ignore = true; // the script should not process this event
                    document.dispatchEvent(event);
                }
            }
            if(is_speeding_up) { // keyup after speeding up begins
                is_speeding_up = false;
                mlog("stop speeding up");
                getPlayerVideoTag().playbackRate = last_playback_rate;
                used = true;
            }
        }

        if(used){
            e.stopPropagation();
            e.preventDefault();
        }
    }, /*useCapture*/true);
})();