Greasy Fork

Video Element Rate Controller Re-dux

Add keyboard shortcuts that will increase/decrease the playback rate for video elements.

目前为 2018-08-11 提交的版本。查看 最新版本

// ==UserScript==
// @name         Video Element Rate Controller Re-dux
// @namespace    https://github.com/mirnhoj/video-element-playbackrate-setter
// @version      2.1
// @description  Add keyboard shortcuts that will increase/decrease the playback rate for video elements.
// @include      http*://*.youtube.com/*
// @include      http*://*.gfycat.com/*
// @include      http*://*.vimeo.com/*
// @include      https://www.facebook.com/video.php*
// @include      https://www.facebook.com/*/videos/*
// @include      https://www.kickstarter.com/*
// @require      https://cdnjs.cloudflare.com/ajax/libs/big.js/5.1.2/big.js
// @grant        GM_registerMenuCommand

// ==/UserScript==
/* jshint esversion: 6 */
//
// if you want to extend the functionality of this script to other sites
// besides youtube, add additional @include keys to the metadata block.
//
// if you want to change the default playback rate from 1x, change the line
// "var currentPlaybackRate = 1;" to equal something other than 1, like 1.3 to
// have all videos start playing at an increased speed, or 0.7 to have all
// videos start playing at a decreased speed.
//
// if you want change the granularity of the playback rate adjustment, change
// the line "var speedStep = 0.1;" to equal something other than 0.1, like 0.01
// for more granular adjustments, or 0.25 for less granular adjustments.


// These values are the default values for initialization
const speedStep = 0.1;
const displayTimeMilliSec = 1500;
const enhancerExtention = true;
const pref = "preference-changed";
const eventName = "speed";

var timeoutID;
var infobox = document.createElement("h1");
var showDisplay = false;
main();

function GMsetup() {
    if (GM_registerMenuCommand) {
        GM_registerMenuCommand('Video Rate Re-dux: Set adjustment rate', function() {
            var curEntry = getVal("speedStep");
            var speedStep = prompt('New adjustment rate:\n(e.g., 0.1 = 10% faster)', curEntry);
            if (speedStep != null) {
                while (isNaN(speedStep)) {
                    speedStep = prompt('Please input a valid number!\n\nNew adjustment rate:\n(e.g., 0.1 = 10% faster)', curEntry);
                }
                setVal("speedStep", speedStep);
            }
        });
        GM_registerMenuCommand('Video Rate Re-dux: Set display timeout', function() {
            var curEntry = getVal("displayTimeMilliSec");
            var displayTimeMilliSec = prompt('New display timeout length (in milliseconds):', curEntry);
            if (displayTimeMilliSec != null) {
                while (isNaN(displayTimeMilliSec)) {
                    displayTimeMilliSec = prompt('Please input a valid number!\n\nNew display timeout length (in milliseconds):', curEntry);
                }
                setVal("displayTimeMilliSec", displayTimeMilliSec);
            }
        });
        GM_registerMenuCommand('Video Rate Re-dux: Set extention usage', function() {
            var curEntry = getVal("enhancerExtention");
            curEntry = curEntry === true ? "Yes" : "No";
            var enhancerExtention = prompt('Are you using Enhancer for YouTube?:', curEntry);
            if (enhancerExtention != null) {
                if (typeof(enhancerExtention) === "string") {
                    var regex = /ye?s?|true|i am/i;
                    enhancerExtention = regex.test(enhancerExtention);
                    var enhancerExtentionOutput = regex.test(enhancerExtention) === true ? "Yes" : "No";
                    alert('Extention use has been set to: "' + enhancerExtentionOutput + '"\n');
                } else {
                    enhancerExtention = false;
                }
                setVal("enhancerExtention", enhancerExtention);
            }
        });
    }
}

function init() {
    var VERCRspeedStep = localStorage.getItem("VERCRspeedStep",);
    var VERCRdisplayTimeMS = localStorage.getItem("VERCRdisplayTimeMS");
    var VERCRenhancerExtention = localStorage.getItem("VERCRenhancerExtention");
    var VERCRpref = localStorage.getItem("VERCRpref");
    var VERCReventName = localStorage.getItem("VERCReventName");
    if (!VERCRspeedStep) {
        VERCRspeedStep = speedStep;
        localStorage.setItem("VERCRspeedStep", Number(VERCRspeedStep));
    }
    if (!VERCRdisplayTimeMS) {
        VERCRdisplayTimeMS = displayTimeMilliSec;
        localStorage.setItem("VERCRdisplayTimeMS", Number(VERCRdisplayTimeMS));
    }
    if (!VERCRenhancerExtention) {
        VERCRenhancerExtention = enhancerExtention;
        localStorage.setItem("VERCRenhancerExtention", VERCRenhancerExtention);
    }
    if (!VERCRpref) {
        VERCRpref = pref;
        localStorage.setItem("VERCRpref", VERCRpref);
    }
    if (!VERCReventName) {
        VERCReventName = "speed";
        localStorage.setItem("VERCReventName", VERCReventName);
    }
}

function getVal(variable) {
    var value;
    var storage = (localStorage ? localStorage : (sessionStorage ? sessionStorage : (window.content.localStorage ? window.content.localStorage : null)));
    try {
        switch (variable) {
            case "speedStep":
                value = storage.getItem("VERCRspeedStep");
                //                 console.log('Variable "' + variable + '" is ' + value);
                return Number(value);
            case "displayTimeMilliSec":
                value = storage.getItem("VERCRdisplayTimeMS");
                //                 console.log('Variable "' + variable + " is " + value);
                return Number(value);
            case "enhancerExtention":
                value = storage.getItem("VERCRenhancerExtention");
                //                 console.log('Variable "' + variable + " is " + value);
                return value;
            case "pref":
                value = storage.getItem("VERCRpref");
                //                 console.log('Variable "' + variable + " is " + value);
                return value;
            case "eventName":
                value = storage.getItem("VERCReventName");
                //                 console.log('Variable "' + variable + " is " + value);
                return value;
            default:
                return null;
        }
    }
    catch (e) {
        if (e.name == "NS_ERROR_FILE_CORRUPTED") {
            storage = sessionStorage ? sessionStorage : null; //set the new storage if fails
            storage.setItem("VERCRspeedStep", speedStep);
            storage.setItem("VERCRdisplayTimeMS", displayTimeMilliSec);
            storage.setItem("VERCRenhancerExtention", enhancerExtention);
            storage.setItem("VERCRpref", pref);
            storage.setItem("VERCReventName", eventName);
        }
    }
}

function setVal(variable, value) {
    var storage = (localStorage ? localStorage : (sessionStorage ? sessionStorage : (window.content.localStorage ? window.content.localStorage : null)));
    try {
        switch (variable) {
            case "speedStep":
                storage.setItem("VERCRspeedStep", Number(value));
                console.log('Setting "' + variable + '" to ' + value);
                return value;
            case "displayTimeMilliSec":
                storage.setItem("VERCRdisplayTimeMS", Number(value));
                console.log('Setting "' + variable + '" to ' + value);
                return value;
            case "enhancerExtention":
                storage.setItem("VERCRenhancerExtention", value);
                console.log('Setting "' + variable + '" to ' + value);
                return value;
            case "pref":
                storage.setItem("VERCRpref", value);
                console.log('Setting "' + variable + '" to ' + value);
                return value;
            case "eventName":
                storage.setItem("VERCReventName", value);
                console.log('Setting "' + variable + '" to ' + value);
                return value;
            default:
                return null;
        }
    }
    catch (e) {
        if (e.name == "NS_ERROR_FILE_CORRUPTED") {
            storage = sessionStorage ? sessionStorage : null; //set the new storage if fails
            storage.setItem("VERCRspeedStep", speedStep);
            storage.setItem("VERCRdisplayTimeMS", displayTimeMilliSec);
            storage.setItem("VERCRenhancerExtention", enhancerExtention);
            storage.setItem("VERCRpref", pref);
            storage.setItem("VERCReventName", eventName);
        }
    }
}

function setPlaybackRate(rate, shouldShowInfobox) {
    showDisplay = false;
    // grab the video elements and set their playback rate.
    var videoElement = document.getElementsByTagName("video")[0];
    videoElement.playbackRate = rate;
    // add infobox to dom if it doesn't already exist.
    if (videoElement && !document.getElementById("playbackrate-indicator")) {
        videoElement.parentElement.appendChild(infobox);
    }
    if (shouldShowInfobox) {
        showInfobox(rate);

    }
}
function showInfobox(rate) {
    rate = new Big(rate);
    // update rate indicator.
    infobox.innerHTML = rate + "x";
    // show infobox
    infobox.style.visibility = "visible";
    // clear out any previous timers and have the infobox hide after the pre-set time period
    window.clearTimeout(timeoutID);
    timeoutID = window.setTimeout(function() {
        infobox.style.visibility = "hidden";
    }, getVal("displayTimeMilliSec"));
}

// mimic vlc keyboard shortcuts
function addKeyListener() {
    window.addEventListener('keydown', function(event) {
        var key = event.key;
        var videoElement = document.getElementsByTagName("video")[0];
        var currentPlaybackRate = new Big(videoElement.playbackRate);
        var speedStep = new Big(getVal("speedStep"));
        switch (key) {
                // decrease playback rate if '[' is pressed
            case "[":
                //                 currentPlaybackRate -= speedStep;
                currentPlaybackRate = currentPlaybackRate.minus(speedStep);
                //                 console.log('Setting "currentPlaybackRate" to ' + currentPlaybackRate);
                showDisplay = true;
                setPlaybackRate(currentPlaybackRate, showDisplay);
                break;
                // increase playback rate if ']' is pressed
            case "]":
                //                 currentPlaybackRate += speedStep;
                currentPlaybackRate = currentPlaybackRate.add(speedStep);
                //                 console.log('Setting "currentPlaybackRate" to ' + currentPlaybackRate);
                showDisplay = true;
                setPlaybackRate(currentPlaybackRate, showDisplay);
                break;
            default:
                return null;
        }
        if (getVal("enhancerExtention") === true) {
            var pref = getVal("pref");
            var eventName = getVal("eventName");
            window.postMessage({
                enhancerforyoutube: pref,
                name: eventName,
                value: currentPlaybackRate
            }, "*");
        }
    });
}

// show the current speed display on the video when mouse wheel is rolled on the speed element if using Enhancer For Youtube
function addWheelListener() {
    var enhancerToolbar = document.getElementById("enhancer-for-youtube-toolbar");
    var enhancerToolbarChildren = enhancerToolbar.children[0].children;
    var eventName = getVal("eventName");
    for (var i = 0; i < enhancerToolbarChildren.length; i++) {
        if (enhancerToolbarChildren[i].dataset.name === eventName) {
            var speedChild = enhancerToolbarChildren[i];
        }
    }
    if (speedChild) {
        speedChild.addEventListener('wheel', function(event) {
            var wDelta = event.wheelDelta < 0 ? 'down' : 'up';
            var videoElement = document.getElementsByTagName("video")[0];
            var currentPlaybackRate = videoElement.playbackRate;
            switch (wDelta) {
                case "down":
                    // currentPlaybackRate -= speedStep; // uncomment to actually modify the playback speed
                    showInfobox(currentPlaybackRate);
                    break;
                case "up":
                    // currentPlaybackRate += speedStep; // uncomment to actually modify the playback speed
                    showInfobox(currentPlaybackRate);
                    break;
            }
        });
    }
}

async function onReady() {
    addKeyListener();
    if (getVal("enhancerExtention") === true) {
        var i = 0;
        do {
            await wait(200);
            i++
        } while (!document.getElementById("enhancer-for-youtube-toolbar") && i < 50);
        onExtentionReady(); // Or setTimeout(onReady, 0); if you want it consistently async
    }
}

function wait(time) {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve();
        }, time);
    });
}

function onExtentionReady() {
    addWheelListener();
}

function Ext_Detect_NotInstalled(ExtName, ExtID, obj) {
    console.log(ExtName + ' Not Installed');
    setVal("enhancerExtention", false);
    obj.parentNode.removeChild(obj);
}

function Ext_Detect_Installed(ExtName, ExtID, obj) {
    console.log(ExtName + ' Installed');
    setVal("enhancerExtention", true);
    obj.parentNode.removeChild(obj);
}

var Ext_Detect = function(ExtName, ExtID) {
    var is_firefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
    var is_chrome = navigator.userAgent.toLowerCase().indexOf('chrome') > -1;
    if (is_firefox == true) {
        var sMoz = document.createElement('script');
        sMoz.onload = function() { Ext_Detect_Installed(ExtName, ExtID, sMoz); };
        sMoz.onerror = function() { Ext_Detect_NotInstalled(ExtName, ExtID, sMoz); };
        sMoz.src = 'moz-extension://' + ExtID + '/resources/youtube-polymer.js';
        document.body.appendChild(sMoz);
    } else if (is_chrome == true) {
        var sChrome = document.createElement('script');
        sChrome.onload = function() { Ext_Detect_Installed(ExtName, ExtID); };
        sChrome.onerror = function() { Ext_Detect_NotInstalled(ExtName, ExtID); };
        sChrome.src = 'chrome-extension://' + ExtID + '/resources/youtube-polymer.js';
        document.body.appendChild(sChrome);
    }
}

function main() {
    //     const addonID = "397abcaa-dabc-4cd2-b808-0df445170b78";
    //     const addonID = "815f3dd0-c7e5-4c5a-b049-c6a74b325a7a";
    init();
    //     window.onload = function() { Ext_Detect("Enhancer for YouTube", addonID); };
    GMsetup();
    infobox.setAttribute("id", "playbackrate-indicator");
    infobox.style.position = "absolute";
    infobox.style.top = "10%";
    infobox.style.right = "10%";
    infobox.style.color = "rgba(255, 0, 0, 1)";
    infobox.style.zIndex = "99999"; // ensures that it shows above other elements.
    infobox.style.visibility = "hidden";
    infobox.style.marginTop = "3%";
    if (document.readyState !== "loading") {
        onReady(); // Or setTimeout(onReady, 0); if you want it consistently async
    } else {
        document.addEventListener("DOMContentLoaded", onReady);
    }
}