// ==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
// ==/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();