YouTube Speedrun Timer

Adds quality of life features for YouTube frame timing

目前為 2019-06-21 提交的版本,檢視 最新版本

// ==UserScript==
// @name         YouTube Speedrun Timer
// @namespace    http://tampermonkey.net/
// @version      0.2.2
// @description  Adds quality of life features for YouTube frame timing
// @author       Michael "Msbmteam" Y.
// @match        https://*.youtube.com/*
// @match        http://*.youtube.com/*
// @grant        none
// ==/UserScript==

// This script is based on the "YouTube Detailed Timecode" script by Charlie Laabs (https://gf.qytechs.cn/en/scripts/40740-youtube-detailed-timecode)

(function() {
    'use strict';

    // console.log('Running YouTube Speedrun Timer');
    var ytPlayer;
    var ytPlayerUnwrapped;
    var ytpCurrentTime;
    var vidDuration;
    var timeDecimal = document.createElement('span');
    var frameCount = document.createElement('span');
    var lastDisplayedTime = 0;
    var playerInitialized = false;
    var ctrlIsDown = false;
    var shiftIsDown = false;
    var altIsDown = false;
    var fps;
    var frame;

    function getElements() {
        // get the YouTube player element from the page
        ytPlayer = document.getElementById("movie_player") || document.getElementsByClassName("html5-video-player")[0];
        ytPlayerUnwrapped = ytPlayer.wrappedJSObject;
        if (ytPlayer)
        {
            frameCount.innerHTML = "";
            ytPlayer.addEventListener("onStateChange", playerChanged, true );
            document.addEventListener("keyup", keyUp, true );
            document.addEventListener("keydown", keyDown, true);

            vidDuration = ytPlayer.getDuration();

            setFps();
        }
    }

    function playerStarted()
    {
        var timeDisp = ytPlayerUnwrapped.getElementsByClassName("ytp-time-display")[0];

        timeDecimal.className = 'us-time-dec';
        timeDecimal.style.paddingLeft = "0px";

        frameCount.className = 'us-frame-count';
        frameCount.style.paddingLeft = '20px';
        timeDisp.appendChild(frameCount);

        playerInitialized = true;

    }

    function playerChanged(state)
    {
        //console.log("stateChanged: ", state);
        if (state != -1 && !playerInitialized ) {
            playerStarted();
        }
        if (state == 2) {
            displayTime();
        }

    }

    function keyUp(e)
    {
        //console.log('keyUp:', e);
        if (e.keyCode == 16) shiftIsDown = false;
        if (e.keyCode == 17) ctrlIsDown = false;
        if (e.keyCode == 18) altIsDown = false;

        displayTime();
    }

    function keyDown(e)
    {
        if (e.keyCode == 16) shiftIsDown = true;
        if (e.keyCode == 17) ctrlIsDown = true;
        if (e.keyCode == 18) altIsDown = true;

        // CTRL + ALT + <number> -- advance <number> frames forward (0 is 10)
        if (ctrlIsDown && altIsDown && e.keyCode >= 48 && e.keyCode <= 57) {
            frameAdvance(e.keyCode, 0);
        }
        // CTRL + SHIFT + <number> -- advance <number> frames backward (0 is 10)
        if (ctrlIsDown && shiftIsDown && e.keyCode >= 48 && e.keyCode <= 57) {
            frameAdvance(e.keyCode, -1);
        }
        // CTRL + "." -- advance 1 second forward
        if (ctrlIsDown && e.keyCode == 190) {
            ytPlayerUnwrapped.seekTo(ytPlayerUnwrapped.getCurrentTime() + 1);
        }
        // CTRL + "," -- advance 1 second backward
        if (ctrlIsDown && e.keyCode == 188) {
            ytPlayerUnwrapped.seekTo(ytPlayerUnwrapped.getCurrentTime() - 1);
        }
    }

    function setFps()
    {
        // Find fps from Stats for Nerds (can get manually by right clicking on YT player)
        var resolution = ytPlayerUnwrapped.getStatsForNerds().resolution;
        fps = resolution.substr(resolution.search('@')+1, 2);
        fps = Number.parseInt(fps);
        frame = 1/fps;
    }

    function frameAdvance(keycode, dir)
    {
        var num = Number(String.fromCharCode(keycode));
        if (num === 0) num = 10;
        if (dir == -1) num *= -1;
        ytPlayerUnwrapped.seekTo(ytPlayerUnwrapped.getCurrentTime() + num * frame, true);
    }

    function displayTime()
    {
        ytpCurrentTime = document.getElementsByClassName("ytp-time-current")[0];
        var currentTime = ytPlayerUnwrapped.getCurrentTime().toFixed(3);
        var decimal = currentTime.substr(currentTime.indexOf('.'),4);
        // console.log(decimal);
        timeDecimal.innerHTML = decimal;
        ytpCurrentTime.appendChild(timeDecimal);

        setFps();

        var frameNum = Math.floor(ytPlayerUnwrapped.getCurrentTime() * fps);
        frameCount.innerHTML = 'Current frame: ' + frameNum + ' (' + fps + ' FPS)';
    }

    getElements(); //for embedded videos
    document.addEventListener("yt-navigate-finish", getElements, true ); //for YouTube


})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址