Old Seek UI

Return the old YouTube video player seek UI, replacing the mobile-style ones they added circa 2021.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

// ==UserScript==
// @name         Old Seek UI
// @version      0.2
// @description  Return the old YouTube video player seek UI, replacing the mobile-style ones they added circa 2021.
// @author       Taniko Yamamoto
// @author       https://github.com/YukisCoffee/yt-player-classicifier
// @match        https://www.youtube.com/*
// @icon         https://www.youtube.com/favicon.ico
// @grant        none
// @run-at       document-start
// @namespace https://greatest.deepsurf.us/users/1132181
// ==/UserScript==

(function() {

    const ICONSET = {
        "back10": "M 18,11 V 7 l -5,5 5,5 v -4 c 3.3,0 6,2.7 6,6 0,3.3 -2.7,6 -6,6 -3.3,0 -6,-2.7 -6,-6 h -2 c 0,4.4 3.6,8 8,8 4.4,0 8,-3.6 8,-8 0,-4.4 -3.6,-8 -8,-8 z M 16.9,22 H 16 V 18.7 L 15,19 v -0.7 l 1.8,-0.6 h .1 V 22 z m 4.3,-1.8 c 0,.3 0,.6 -0.1,.8 l -0.3,.6 c 0,0 -0.3,.3 -0.5,.3 -0.2,0 -0.4,.1 -0.6,.1 -0.2,0 -0.4,0 -0.6,-0.1 -0.2,-0.1 -0.3,-0.2 -0.5,-0.3 -0.2,-0.1 -0.2,-0.3 -0.3,-0.6 -0.1,-0.3 -0.1,-0.5 -0.1,-0.8 v -0.7 c 0,-0.3 0,-0.6 .1,-0.8 l .3,-0.6 c 0,0 .3,-0.3 .5,-0.3 .2,0 .4,-0.1 .6,-0.1 .2,0 .4,0 .6,.1 .2,.1 .3,.2 .5,.3 .2,.1 .2,.3 .3,.6 .1,.3 .1,.5 .1,.8 v .7 z m -0.9,-0.8 v -0.5 c 0,0 -0.1,-0.2 -0.1,-0.3 0,-0.1 -0.1,-0.1 -0.2,-0.2 -0.1,-0.1 -0.2,-0.1 -0.3,-0.1 -0.1,0 -0.2,0 -0.3,.1 l -0.2,.2 c 0,0 -0.1,.2 -0.1,.3 v 2 c 0,0 .1,.2 .1,.3 0,.1 .1,.1 .2,.2 .1,.1 .2,.1 .3,.1 .1,0 .2,0 .3,-0.1 l .2,-0.2 c 0,0 .1,-0.2 .1,-0.3 v -1.5 z",
        "forward10": "m 10,19 c 0,4.4 3.6,8 8,8 4.4,0 8,-3.6 8,-8 h -2 c 0,3.3 -2.7,6 -6,6 -3.3,0 -6,-2.7 -6,-6 0,-3.3 2.7,-6 6,-6 v 4 l 5,-5 -5,-5 v 4 c -4.4,0 -8,3.6 -8,8 z m 6.8,3 H 16 V 18.7 L 15,19 v -0.7 l 1.8,-0.6 h .1 V 22 z m 4.3,-1.8 c 0,.3 0,.6 -0.1,.8 l -0.3,.6 c 0,0 -0.3,.3 -0.5,.3 C 20,21.9 19.8,22 19.6,22 19.4,22 19.2,22 19,21.9 18.8,21.8 18.7,21.7 18.5,21.6 18.3,21.5 18.3,21.3 18.2,21 18.1,20.7 18.1,20.5 18.1,20.2 v -0.7 c 0,-0.3 0,-0.6 .1,-0.8 l .3,-0.6 c 0,0 .3,-0.3 .5,-0.3 .2,0 .4,-0.1 .6,-0.1 .2,0 .4,0 .6,.1 .2,.1 .3,.2 .5,.3 .2,.1 .2,.3 .3,.6 .1,.3 .1,.5 .1,.8 v .7 z m -0.8,-0.8 v -0.5 c 0,0 -0.1,-0.2 -0.1,-0.3 0,-0.1 -0.1,-0.1 -0.2,-0.2 -0.1,-0.1 -0.2,-0.1 -0.3,-0.1 -0.1,0 -0.2,0 -0.3,.1 l -0.2,.2 c 0,0 -0.1,.2 -0.1,.3 v 2 c 0,0 .1,.2 .1,.3 0,.1 .1,.1 .2,.2 .1,.1 .2,.1 .3,.1 .1,0 .2,0 .3,-0.1 l .2,-0.2 c 0,0 .1,-0.2 .1,-0.3 v -1.5 z",
        "back5": "M 18,11 V 7 l -5,5 5,5 v -4 c 3.3,0 6,2.7 6,6 0,3.3 -2.7,6 -6,6 -3.3,0 -6,-2.7 -6,-6 h -2 c 0,4.4 3.6,8 8,8 4.4,0 8,-3.6 8,-8 0,-4.4 -3.6,-8 -8,-8 z m -1.3,8.9 .2,-2.2 h 2.4 v .7 h -1.7 l -0.1,.9 c 0,0 .1,0 .1,-0.1 0,-0.1 .1,0 .1,-0.1 0,-0.1 .1,0 .2,0 h .2 c .2,0 .4,0 .5,.1 .1,.1 .3,.2 .4,.3 .1,.1 .2,.3 .3,.5 .1,.2 .1,.4 .1,.6 0,.2 0,.4 -0.1,.5 -0.1,.1 -0.1,.3 -0.3,.5 -0.2,.2 -0.3,.2 -0.4,.3 C 18.5,22 18.2,22 18,22 17.8,22 17.6,22 17.5,21.9 17.4,21.8 17.2,21.8 17,21.7 16.8,21.6 16.8,21.5 16.7,21.3 16.6,21.1 16.6,21 16.6,20.8 h .8 c 0,.2 .1,.3 .2,.4 .1,.1 .2,.1 .4,.1 .1,0 .2,0 .3,-0.1 L 18.5,21 c 0,0 .1,-0.2 .1,-0.3 v -0.6 l -0.1,-0.2 -0.2,-0.2 c 0,0 -0.2,-0.1 -0.3,-0.1 h -0.2 c 0,0 -0.1,0 -0.2,.1 -0.1,.1 -0.1,0 -0.1,.1 0,.1 -0.1,.1 -0.1,.1 h -0.7 z",
        "forward5": "m 10,19 c 0,4.4 3.6,8 8,8 4.4,0 8,-3.6 8,-8 h -2 c 0,3.3 -2.7,6 -6,6 -3.3,0 -6,-2.7 -6,-6 0,-3.3 2.7,-6 6,-6 v 4 l 5,-5 -5,-5 v 4 c -4.4,0 -8,3.6 -8,8 z m 6.7,.9 .2,-2.2 h 2.4 v .7 h -1.7 l -0.1,.9 c 0,0 .1,0 .1,-0.1 0,-0.1 .1,0 .1,-0.1 0,-0.1 .1,0 .2,0 h .2 c .2,0 .4,0 .5,.1 .1,.1 .3,.2 .4,.3 .1,.1 .2,.3 .3,.5 .1,.2 .1,.4 .1,.6 0,.2 0,.4 -0.1,.5 -0.1,.1 -0.1,.3 -0.3,.5 -0.2,.2 -0.3,.2 -0.5,.3 C 18.3,22 18.1,22 17.9,22 17.7,22 17.5,22 17.4,21.9 17.3,21.8 17.1,21.8 16.9,21.7 16.7,21.6 16.7,21.5 16.6,21.3 16.5,21.1 16.5,21 16.5,20.8 h .8 c 0,.2 .1,.3 .2,.4 .1,.1 .2,.1 .4,.1 .1,0 .2,0 .3,-0.1 L 18.4,21 c 0,0 .1,-0.2 .1,-0.3 v -0.6 l -0.1,-0.2 -0.2,-0.2 c 0,0 -0.2,-0.1 -0.3,-0.1 h -0.2 c 0,0 -0.1,0 -0.2,.1 -0.1,.1 -0.1,0 -0.1,.1 0,.1 -0.1,.1 -0.1,.1 h -0.6 z",
        "backchapter": "m 16.436975,17.634454 c -0.573529,0 -1.191177,0.117647 -1.617647,0.441177 v 4.308938 c 0,0.191177 0.214706,0.132123 0.220588,0.132123 0.397059,-0.191176 0.970588,-0.323414 1.397059,-0.323414 0.57353,0 1.191177,0.117646 1.617647,0.441176 0.397059,-0.25 1.117648,-0.441176 1.617647,-0.441176 0.485295,0 0.985294,0.08846 1.397059,0.309053 0.120588,0.06177 0.220589,-0.05623 0.220589,-0.132698 v -4.294002 c -0.438235,-0.329412 -1.067647,-0.441177 -1.617648,-0.441177 -0.573529,0 -1.191176,0.117647 -1.617647,0.441177 -0.42647,-0.32353 -1.044117,-0.441177 -1.617647,-0.441177 z m 3.235294,0.588235 c 0.352942,0 0.705883,0.04411 1.029412,0.147059 v 3.382353 c -0.323529,-0.102941 -0.67647,-0.147059 -1.029412,-0.147059 -0.499999,0 -1.220588,0.191177 -1.617647,0.441177 v -3.382353 c 0.397059,-0.25 1.117648,-0.441177 1.617647,-0.441177 z m -0.674976,1.202322 v 1.303997 l 1.024241,-0.651999 z M 18,7 l -5,5 5,5 v -4 c 3.3,0 6,2.7 6,6 0,3.3 -2.7,6 -6,6 -3.3,0 -6,-2.7 -6,-6 h -2 c 0,4.4 3.6,8 8,8 4.4,0 8,-3.6 8,-8 0,-4.4 -3.6,-8 -8,-8 z",
        "forwardchapter": "m 16.436975,17.634454 c -0.573529,0 -1.191177,0.117647 -1.617647,0.441177 v 4.308938 c 0,0.191177 0.214706,0.132123 0.220588,0.132123 0.397059,-0.191176 0.970588,-0.323414 1.397059,-0.323414 0.57353,0 1.191177,0.117646 1.617647,0.441176 0.397059,-0.25 1.117648,-0.441176 1.617647,-0.441176 0.485295,0 0.985294,0.08846 1.397059,0.309053 0.120588,0.06177 0.220589,-0.05623 0.220589,-0.132698 v -4.294002 c -0.438235,-0.329412 -1.067647,-0.441177 -1.617648,-0.441177 -0.573529,0 -1.191176,0.117647 -1.617647,0.441177 -0.42647,-0.32353 -1.044117,-0.441177 -1.617647,-0.441177 z m 3.235294,0.588235 c 0.352942,0 0.705883,0.04411 1.029412,0.147059 v 3.382353 c -0.323529,-0.102941 -0.67647,-0.147059 -1.029412,-0.147059 -0.499999,0 -1.220588,0.191177 -1.617647,0.441177 v -3.382353 c 0.397059,-0.25 1.117648,-0.441177 1.617647,-0.441177 z m -0.674976,1.202322 v 1.303997 l 1.024241,-0.651999 z M 18,7 v 4 c -4.4,0 -8,3.6 -8,8 0,4.4 3.6,8 8,8 4.4,0 8,-3.6 8,-8 h -2 c 0,3.3 -2.7,6 -6,6 -3.3,0 -6,-2.7 -6,-6 0,-3.3 2.7,-6 6,-6 v 4 l 5,-5 z"
    };
    const DEBUG = false;

    // Player API reference
    var api;
    var bezel;

    var animationStartTimer = 0;
    var animationShouldEndTimer = 0;

    function log(a)
    {
        if (DEBUG) console.log(a);
    }

    function getAnimationDuration(elm)
    {
        var prop = window.getComputedStyle(elm).animationDuration;

        switch (true)
        {
            case "ms" == prop.substr(-2):
                return +prop.replace("ms", "");
            case "s" == prop.substr(-1):
                return +(prop.replace("s", "")) * 1000;
        }
    }

    async function attemptEndAnimateBezel()
    {
        while (animationShouldEndTimer > Date.now())
        {
            await new Promise(r => requestAnimationFrame(r));
        }

        animationStartTimer = 0;
        animationShouldEndTimer = 0;
        bezel.style.display = "none";
    }

    function animateBezel()
    {
        var animationDuration = getAnimationDuration(bezel.querySelector(".ytp-bezel"));

        bezel.style.display = "";

        if (0 == animationShouldEndTimer)
        {
            animationStartTimer = Date.now();
            animationShouldEndTimer = animationStartTimer;
        }

        animationShouldEndTimer += animationDuration;

        attemptEndAnimateBezel();
    }

    function waitToAnimate()
    {
        return new Promise(resolve => {
            setTimeout(() => {
                resolve();
            }, 10);
        });
    }

    function createBezel(direction, duration, text = "", chapter = false)
    {
        return new Promise(resolve => {
            log("Creating bezel");
            var bezelElm = api.querySelector(".ytp-bezel");
            bezel = bezelElm.parentNode;

            bezelElm.removeAttribute("aria-label");

            // Get the icon from the iconset
            var icon;
            if (chapter)
            {
                icon = ICONSET[direction + "chapter"];
            }
            else if (ICONSET[direction + duration])
            {
                icon = ICONSET[direction + duration];
            }
            else
            {
                icon = "";
            }

            var iconElm;
            if (iconElm = bezel.querySelector(".ytp-bezel-icon path"))
            {
                iconElm.setAttribute("d", icon);
            }
            else
            {
                bezel.querySelector(".ytp-bezel-icon").insertAdjacentHTML("beforeend",
                    `<svg height="100%" version="1.1" viewBox="0 0 36 36" width="100%">
                        <path class="ytp-svg-fill" d="${icon}"></path>
                    </svg>`
                );
            }

            if ("" === text)
            {
                bezel.setAttribute("class", "ytp-bezel-text-hide");
            }
            else
            {
                bezel.setAttribute("class", "");
                bezel.querySelector(".ytp-bezel-text").innerText = text;
            }

            bezel.style.display = "none";

            resolve();
        });
    }

    async function waitForElement(query, timeout = 500)
    {
        log("Waiting for element " + query + " with timeout in " + timeout + " ms.");
        var hasTimedOut = false;

        setTimeout(function() {
            log("Wait for element " + query + " has timed out.");
            hasTimedOut = true;
        }, timeout);

        while (null == document.querySelector(query) && !hasTimedOut)
        {
            await new Promise(r => requestAnimationFrame(r));
        }

        var a;
        if (a = document.querySelector(query))
        {
            return a;
        }
        else
        {
            return null;
        }
    }

    function handleSeekGui()
    {
        log("Handing seek GUI");
        var direction = this.dataset.side;
        var duration = this.querySelector(".ytp-doubletap-tooltip-label")
            .innerText.replace(/[\s|[A-Za-z]]*/g, "")
        ;
        var text = "";
        var isChapter = false;

        if (this.classList.contains("ytp-chapter-seek"))
        {
            var textContainer = this.querySelector(".ytp-chapter-seek-text-legacy")
            text = textContainer.innerText;
            duration = 0;
            isChapter = true;
        }

        createBezel(direction, duration, text, isChapter).then(waitToAnimate).then(animateBezel);
    }

    async function attemptHookPlayer()
    {
        log("Attempting to hook player");
        var playerApi = await waitForElement(".html5-video-player", 5000);

        if (playerApi)
        {
            log("Player API detected");
            api = playerApi;

            var doubleTapElm = api.querySelector(".ytp-doubletap-ui-legacy") ?? api.querySelector(".ytp-doubletap-ui") ?? null;

            if (doubleTapElm && !api.__oldSeekUi)
            {
                log("Doubletap detected: installing binding");
                (new MutationObserver(handleSeekGui.bind(doubleTapElm)))
                    .observe(doubleTapElm, {"subtree": true, "childList": true, "characterData": "true"});
                api.__oldSeekUi = true;
            }
        }
    }

    function insertContinuationEvent()
    {
        log("Inserting continuation events");
        if (window.ytspf && window.ytspf.enabled)
        {
            log("Inserted spf continuation events");
            document.addEventListener("spfdone", attemptHookPlayer);
        }
        else if (document.querySelector("ytd-app"))
        {
            log("Inserted kevlar continuation events");
            document.addEventListener("yt-page-data-updated", attemptHookPlayer);
        }
    }

    function installInitialStyles()
    {
        log("Installed initial styles");
        document.head.insertAdjacentHTML("beforeend",
            `<style>
                .ytp-doubletap-ui, .ytp-doubletap-ui-legacy
                {
                    display: none !important;
                }
            </style>`
        );
    }

    function handleDOMContentLoaded()
    {
        log("domcontentloaded event fired");
        installInitialStyles();
        document.removeEventListener("DOMContentLoaded", handleDOMContentLoaded);
    }

    async function main()
    {
        log("Old seek ui script loaded");

        document.addEventListener("DOMContentLoaded", handleDOMContentLoaded);

        // The player needs more time to init
        // So wait until a little while after page load to attempt
        // hooking the player
        window.addEventListener("load", function handleLoad() {
            log("load event fired");
            attemptHookPlayer();
            insertContinuationEvent();
            window.removeEventListener("load", handleLoad);
        });
    }

    main();

})();