// ==UserScript== // @name Video Element Rate Controller Re-dux // @namespace https://github.com/mirnhoj/video-element-playbackrate-setter // @version 2.4 // @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/* // @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 the default values for initialization let speedStep = 0.1; let displayTimeMilliSec = 1500; let timeoutID = null; const infobox = document.createElement('h1'); let showValuesOnVideo = true; // true to show new value on the video let keyIncreaseSpeed = ']'; let keyReduceSpeed = '['; let keyResetSpeed = '\\'; function getVal(variable) { let value; let storage = (localStorage || (sessionStorage || (window.content.localStorage ? window.content.localStorage : null))); try { switch (variable) { case 'speedStep': value = storage.getItem('VERCRspeedStep'); return Number(value); case 'displayTimeMilliSec': value = storage.getItem('VERCRdisplayTimeMS'); return Number(value); case 'keyIncreaseSpeed': return value; case 'keyReduceSpeed': return value; case 'keyResetSpeed': return value; default: return null; } } catch (e) { if (e.name === 'NS_ERROR_FILE_CORRUPTED') { storage = sessionStorage || null; // set the new storage if fails storage.setItem('VERCRspeedStep', speedStep); storage.setItem('VERCRdisplayTimeMS', displayTimeMilliSec); } } //console.log('Variable "' + variable + '" is ' + value); } function setVal(variable, value) { let storage = (localStorage || (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; default: return null; } } catch (e) { if (e.name === 'NS_ERROR_FILE_CORRUPTED') { storage = sessionStorage || null; // set the new storage if fails storage.setItem('VERCRspeedStep', speedStep); storage.setItem('VERCRdisplayTimeMS', displayTimeMilliSec); } } } function GMsetup() { if (GM_registerMenuCommand) { GM_registerMenuCommand('Set adjustment rate', () => { const curEntry = getVal('speedStep'); 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 keyboard shortcuts', () => { // const curEntry = `${getVal('keyIncreaseSpeed')}, ${getVal('keyReduceSpeed')}, ${getVal('keyResetSpeed')}`; // // W.I.P. // }); GM_registerMenuCommand('Set display timeout', () => { const curEntry = getVal('displayTimeMilliSec'); 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); } }); } } function init() { let VERCRspeedStep = localStorage.getItem('VERCRspeedStep'); let VERCRdisplayTimeMS = localStorage.getItem('VERCRdisplayTimeMS'); if (!VERCRspeedStep) { VERCRspeedStep = speedStep; localStorage.setItem('VERCRspeedStep', Number(VERCRspeedStep)); } if (!VERCRdisplayTimeMS) { VERCRdisplayTimeMS = displayTimeMilliSec; localStorage.setItem('VERCRdisplayTimeMS', Number(VERCRdisplayTimeMS)); } } function showInfobox(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(() => { infobox.style.visibility = 'hidden'; }, getVal('displayTimeMilliSec')); } function setPlaybackRate(rate, shouldShowInfobox) { // grab the video elements and set their playback rate. const 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); } } // mimic vlc keyboard shortcuts function addKeyListener() { window.addEventListener('keydown', (event) => { const key = event.key; const videoElement = document.getElementsByTagName('video')[0]; let currentPlaybackRate = videoElement.playbackRate; speedStep = getVal('speedStep'); if (key === keyReduceSpeed) { currentPlaybackRate = parseFloat((currentPlaybackRate - speedStep).toFixed(3)); //console.log(`Raising "currentPlaybackRate" to ${currentPlaybackRate}`); setPlaybackRate(currentPlaybackRate, showValuesOnVideo); } else if (key === keyIncreaseSpeed) { currentPlaybackRate = parseFloat((currentPlaybackRate + speedStep).toFixed(3)); //console.log(`Lowering "currentPlaybackRate" to ${currentPlaybackRate}`); setPlaybackRate(currentPlaybackRate, showValuesOnVideo); } else if (key === keyResetSpeed) { currentPlaybackRate = 1; setPlaybackRate(currentPlaybackRate, showValuesOnVideo); } }); } function onReady() { addKeyListener(); } function wait(time) { return new Promise((resolve) => { setTimeout(() => { resolve(); }, time); }); } function main() { init(); 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); } } main();