YouTube mix next/previous buttons

Adds next/previous buttons to seek between tracks in a YouTube mix without having to scroll down.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         YouTube mix next/previous buttons
// @namespace    https://zachsaucier.com/
// @version      0.6
// @description  Adds next/previous buttons to seek between tracks in a YouTube mix without having to scroll down.
// @author       Zach Saucier
// @include      /^https://www.youtube.com/watch
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    function goToNextTrack() {
        var currTimeString = document.querySelector(".ytp-time-current").innerText,
            currTime = parseTimeString(currTimeString);

        for(var i = 0; i < timeLinks.length; i++) {
            var linkTimeString = timeLinks[i].innerText,
                linkTime = parseTimeString(linkTimeString);

            if(isLaterTime(currTime, linkTime)) {
                timeLinks[i].click();
                return;
            }
        }

        timeLinks[0].click();
    }

    function goToPreviousTrack() {
        var currTimeString = document.querySelector(".ytp-time-current").innerText,
            currTime = parseTimeString(currTimeString);

        for(var i = timeLinks.length - 1; i >= 0; i--) {
            var linkTimeString = timeLinks[i].innerText,
                linkTime = parseTimeString(linkTimeString);

            if(isLaterTime(linkTime, currTime)) {
                timeLinks[i].click();
                return;
            }
        }

        timeLinks[timeLinks.length - 1].click();
    }

    function parseTimeString(timeString) {
        var timeArr = timeString.match(/\d+/g);

        var h = 0,
            m = 0,
            s = 0;

        if(timeArr.length === 3) {
            h = parseInt(timeArr[0]);
            m = parseInt(timeArr[1]);
            s = parseInt(timeArr[2]);
        } else if(timeArr.length == 2) {
            m = parseInt(timeArr[0]);
            s = parseInt(timeArr[1]);
        }

        return [h, m, s];
    }

    function isLaterTime(firstTime, secondTime) {
        if(firstTime[0] < secondTime[0]
           || (firstTime[0] === secondTime[0] && firstTime[1] < secondTime[1])
           || (firstTime[0] === secondTime[0] && firstTime[1] === secondTime[1] && firstTime[2] < secondTime[2])
          )
            return true;

        else
            return false;
    }
    
    var description, 
        timeLinks;
    
    var url = window.location.href;
    var videoid = url.match(/(?:https?:\/{2})?(?:w{3}\.)?youtu(?:be)?\.(?:com|be)(?:\/watch\?v=|\/)([^\s&]+)/);
    
    function checkReady() {
        description = document.getElementById("description");
        
        if(description) { 
            timeLinks = description.querySelectorAll("a[href *= '/watch?v=" + videoid[1] + "'");

            // If true, it's (probably) a mix with tracklisting provided
            if(timeLinks.length > 0) {
                // Create the buttons
                var nextButton = document.createElement("button"),
                    prevButton = document.createElement("button"),
                    br = document.createElement("br");

                nextButton.innerText = "Next track";
                prevButton.innerText = "Previous track";
                nextButton.onclick = goToNextTrack;
                prevButton.onclick = goToPreviousTrack;

                nextButton.style = "cursor: pointer; background-color:#444244; color: white; padding: 4px 12px; border: none; margin-right: 5px; margin-bottom: 5px;";
                prevButton.style = "cursor: pointer; background-color:#444244; color: white; padding: 4px 12px; border: none; margin-right: 5px; margin-bottom: 5px;";

                nextButton.title = "Hotkey: u";
                prevButton.title = "Hotkey: i";

                description.insertBefore(br, description.childNodes[0]);
                description.insertBefore(nextButton, description.childNodes[0]);
                description.insertBefore(prevButton, description.childNodes[0]);

                // Create the key listeners
                document.addEventListener("keyup", function(e) {
                    if(e.keyCode === 80
                    && e.srcElement.tagName !== "INPUT"
                    && e.srcElement.tagName !== "TEXTAREA") { // p pressed
                        goToNextTrack();
                    } else if(e.keyCode === 79
                    && e.srcElement.tagName !== "INPUT"
                    && e.srcElement.tagName !== "TEXTAREA") { // o pressed
                        goToPreviousTrack();
                    }
                });
            }
        }
        else {
            setTimeout(checkReady, 100);
        }
    }
    setTimeout(checkReady, 100);

})();