// ==UserScript== // @name Video Element Rate Controller Re-dux // @namespace https://github.com/mirnhoj/video-element-playbackrate-setter // @version 2.0 // @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_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @downloadURL none // ==/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 now set up elsewhere and are configurable in the extention settings // var currentPlaybackRate = 1; // default playback rate. // var speedStep = 0.125; // how much to modify speed for each keypress // var displayTimeMilliSec = 1500; // how long to show the current speed indicator in milliseconds // var enhancerExtention = true; // set to false if you aren't using Enhancer for Youtube 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:', curEntry); if (speedStep != null) { while (isNaN(speedStep)) { speedStep = prompt('Please input a valid number!\n\nNew adjustment rate:', 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); } else { setVal("enhancerExtention", "No"); } }); } } function init() { var VERCRspeedStep = getVal("speedstep"); var VERCRdisplayTimeMS = getVal("displayTimeMilliSec"); var VERCRenhancerExtention = getVal("enhancerExtention"); var VERCRpref = getVal("pref"); var VERCReventName = getVal("eventName"); if (!VERCRspeedStep) { VERCRspeedStep = 0.125; setVal("speedstep", VERCRspeedStep); } if (!VERCRdisplayTimeMS) { VERCRdisplayTimeMS = 1500; setVal("displayTimeMilliSec", VERCRdisplayTimeMS); } if (!VERCRenhancerExtention) { VERCRenhancerExtention = true; setVal("enhancerExtention", VERCRenhancerExtention); } if (!VERCRpref) { VERCRpref = "preference-changed"; setVal("pref", VERCRpref); } if (!VERCReventName) { VERCReventName = "speed"; setVal("eventName", VERCReventName); } } function getVal(variable) { var storage = (localStorage ? localStorage : (window.content.localStorage ? window.content.localStorage : null)); try { switch (variable) { case "speedstep": var speedStep = storage.getItem("VERCRspeedStep"); return speedStep; case "displayTimeMilliSec": var displayTimeMilliSec = storage.getItem("VERCRdisplayTimeMS"); return displayTimeMilliSec; case "enhancerExtention": var enhancerExtention = storage.getItem("VERCRenhancerExtention"); return enhancerExtention; case "pref": var pref = storage.getItem("VERCRpref"); return pref; case "eventName": var eventName = storage.getItem("VERCReventName"); return eventName; default: return null; } } catch (e) { if (e.name == "NS_ERROR_FILE_CORRUPTED") { storage = sessionStorage ? sessionStorage : null;//set the new storage if fails } } } function setVal(variable, value) { var storage = (localStorage ? localStorage : (window.content.localStorage ? window.content.localStorage : null)); try { switch (variable) { case "speedstep": storage.setItem("VERCRspeedStep", Big(value)); break; case "displayTimeMilliSec": storage.setItem("VERCRdisplayTimeMS", value); break; case "enhancerExtention": storage.setItem("VERCRenhancerExtention", value); break; case "pref": storage.setItem("VERCRpref", value); break; case "eventName": storage.setItem("VERCReventName", value); break; default: break; } } catch (e) { if (e.name == "NS_ERROR_FILE_CORRUPTED") { storage = sessionStorage ? sessionStorage : null;//set the new storage if fails } } } function setPlaybackRate(rate, showInfobox) { rate = new Big(rate); // grab the video elements and set their playback rate. var videoElement = document.getElementsByTagName("video")[0]; videoElement.playbackRate = rate; infobox.innerHTML = rate + "x"; // add infobox to dom if it doesn't already exist. if (videoElement && !document.getElementById("playbackrate-indicator")) { videoElement.parentElement.appendChild(infobox); } // show infobox and update rate indicator. if (showInfobox) { infobox.style.visibility = "visible"; // clear out any previous timers and have the infobox hide after the pre-set time period var timeoutID; window.clearTimeout(timeoutID); timeoutID = window.setTimeout(function() { infobox.style.visibility = "hidden"; }, getVal("displayTimeMilliSec")); showDisplay = false; } } // mimic vlc keyboard shortcuts function addKeyListener() { window.addEventListener('keydown', function(event) { var key = event.key; var videoElement = document.getElementsByTagName("video")[0]; var currentPlaybackRate = videoElement.playbackRate; var speedStep = getVal("speedStep"); switch (key) { // decrease playback rate if '[' is pressed case "[": currentPlaybackRate -= speedStep; showDisplay = true; setPlaybackRate(currentPlaybackRate, showDisplay); break; // increase playback rate if ']' is pressed case "]": currentPlaybackRate += speedStep; 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 showDisplay = true; setPlaybackRate(currentPlaybackRate, showDisplay); break; case "up": // currentPlaybackRate += speedStep; // uncomment to actually modify the playback speed showDisplay = true; setPlaybackRate(currentPlaybackRate, showDisplay); 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"; 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); } }