Video Element Rate Controller Re-dux

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

当前为 2018-08-11 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Video Element Rate Controller Re-dux
  3. // @namespace https://github.com/mirnhoj/video-element-playbackrate-setter
  4. // @version 2.0
  5. // @description Add keyboard shortcuts that will increase/decrease the playback rate for video elements.
  6. // @include http*://*.youtube.com/*
  7. // @include http*://*.gfycat.com/*
  8. // @include http*://*.vimeo.com/*
  9. // @include https://www.facebook.com/video.php*
  10. // @include https://www.facebook.com/*/videos/*
  11. // @include https://www.kickstarter.com/*
  12. // @require https://cdnjs.cloudflare.com/ajax/libs/big.js/5.1.2/big.js
  13. // @grant GM_getValue
  14. // @grant GM_setValue
  15. // @grant GM_registerMenuCommand
  16.  
  17. // ==/UserScript==
  18. /* jshint esversion: 6 */
  19. //
  20. // if you want to extend the functionality of this script to other sites
  21. // besides youtube, add additional @include keys to the metadata block.
  22. //
  23. // if you want to change the default playback rate from 1x, change the line
  24. // "var currentPlaybackRate = 1;" to equal something other than 1, like 1.3 to
  25. // have all videos start playing at an increased speed, or 0.7 to have all
  26. // videos start playing at a decreased speed.
  27. //
  28. // if you want change the granularity of the playback rate adjustment, change
  29. // the line "var speedStep = 0.1;" to equal something other than 0.1, like 0.01
  30. // for more granular adjustments, or 0.25 for less granular adjustments.
  31.  
  32. // These values are now set up elsewhere and are configurable in the extention settings
  33. // var currentPlaybackRate = 1; // default playback rate.
  34. // var speedStep = 0.125; // how much to modify speed for each keypress
  35. // var displayTimeMilliSec = 1500; // how long to show the current speed indicator in milliseconds
  36. // var enhancerExtention = true; // set to false if you aren't using Enhancer for Youtube
  37.  
  38. var infobox = document.createElement("h1");
  39. var showDisplay = false;
  40. main();
  41.  
  42. function GMsetup() {
  43. if (GM_registerMenuCommand) {
  44. GM_registerMenuCommand('Video Rate Re-dux: Set adjustment rate', function() {
  45. var curEntry = getVal("speedStep") || "";
  46. var speedStep = prompt('New adjustment rate:', curEntry);
  47. if (speedStep != null) {
  48. while (isNaN(speedStep)) {
  49. speedStep = prompt('Please input a valid number!\n\nNew adjustment rate:', curEntry);
  50. }
  51. setVal("speedStep", speedStep);
  52. }
  53. });
  54. GM_registerMenuCommand('Video Rate Re-dux: Set display timeout', function() {
  55. var curEntry = getVal("displayTimeMilliSec") || "";
  56. var displayTimeMilliSec = prompt('New display timeout length (in milliseconds):', curEntry);
  57. if (displayTimeMilliSec != null) {
  58. while (isNaN(displayTimeMilliSec)) {
  59. displayTimeMilliSec = prompt('Please input a valid number!\n\nNew display timeout length (in milliseconds):', curEntry);
  60. }
  61. setVal("displayTimeMilliSec", displayTimeMilliSec);
  62. }
  63. });
  64. GM_registerMenuCommand('Video Rate Re-dux: Set extention usage', function() {
  65. var curEntry = getVal("enhancerExtention") || "";
  66. curEntry = curEntry === true ? "Yes" : "No";
  67. var enhancerExtention = prompt('Are you using Enhancer for YouTube?:', curEntry);
  68. if (enhancerExtention != null) {
  69. if (typeof(enhancerExtention) === "string") {
  70. var regex = /ye?s?|true|i am/i;
  71. enhancerExtention = regex.test(enhancerExtention);
  72. var enhancerExtentionOutput = regex.test(enhancerExtention) === true ? "Yes" : "No";
  73. alert('Extention use has been set to: "' + enhancerExtentionOutput + '"\n');
  74. } else {
  75. enhancerExtention = false;
  76. }
  77. setVal("enhancerExtention", enhancerExtention);
  78. } else {
  79. setVal("enhancerExtention", "No");
  80. }
  81. });
  82. }
  83. }
  84.  
  85. function init() {
  86. var VERCRspeedStep = getVal("speedstep");
  87. var VERCRdisplayTimeMS = getVal("displayTimeMilliSec");
  88. var VERCRenhancerExtention = getVal("enhancerExtention");
  89. var VERCRpref = getVal("pref");
  90. var VERCReventName = getVal("eventName");
  91. if (!VERCRspeedStep) {
  92. VERCRspeedStep = 0.125;
  93. setVal("speedstep", VERCRspeedStep);
  94. }
  95. if (!VERCRdisplayTimeMS) {
  96. VERCRdisplayTimeMS = 1500;
  97. setVal("displayTimeMilliSec", VERCRdisplayTimeMS);
  98. }
  99. if (!VERCRenhancerExtention) {
  100. VERCRenhancerExtention = true;
  101. setVal("enhancerExtention", VERCRenhancerExtention);
  102. }
  103. if (!VERCRpref) {
  104. VERCRpref = "preference-changed";
  105. setVal("pref", VERCRpref);
  106. }
  107. if (!VERCReventName) {
  108. VERCReventName = "speed";
  109. setVal("eventName", VERCReventName);
  110. }
  111. }
  112.  
  113. function getVal(variable) {
  114. var storage = (localStorage ? localStorage : (window.content.localStorage ? window.content.localStorage : null));
  115. try {
  116. switch (variable) {
  117. case "speedstep":
  118. var speedStep = storage.getItem("VERCRspeedStep");
  119. return speedStep;
  120. case "displayTimeMilliSec":
  121. var displayTimeMilliSec = storage.getItem("VERCRdisplayTimeMS");
  122. return displayTimeMilliSec;
  123. case "enhancerExtention":
  124. var enhancerExtention = storage.getItem("VERCRenhancerExtention");
  125. return enhancerExtention;
  126. case "pref":
  127. var pref = storage.getItem("VERCRpref");
  128. return pref;
  129. case "eventName":
  130. var eventName = storage.getItem("VERCReventName");
  131. return eventName;
  132. default:
  133. return null;
  134. }
  135. }
  136. catch (e) {
  137. if (e.name == "NS_ERROR_FILE_CORRUPTED") {
  138. storage = sessionStorage ? sessionStorage : null;//set the new storage if fails
  139. }
  140. }
  141. }
  142.  
  143. function setVal(variable, value) {
  144. var storage = (localStorage ? localStorage : (window.content.localStorage ? window.content.localStorage : null));
  145. try {
  146. switch (variable) {
  147. case "speedstep":
  148. storage.setItem("VERCRspeedStep", Big(value));
  149. break;
  150. case "displayTimeMilliSec":
  151. storage.setItem("VERCRdisplayTimeMS", value);
  152. break;
  153. case "enhancerExtention":
  154. storage.setItem("VERCRenhancerExtention", value);
  155. break;
  156. case "pref":
  157. storage.setItem("VERCRpref", value);
  158. break;
  159. case "eventName":
  160. storage.setItem("VERCReventName", value);
  161. break;
  162. default:
  163. break;
  164. }
  165. }
  166. catch (e) {
  167. if (e.name == "NS_ERROR_FILE_CORRUPTED") {
  168. storage = sessionStorage ? sessionStorage : null;//set the new storage if fails
  169. }
  170. }
  171. }
  172.  
  173. function setPlaybackRate(rate, showInfobox) {
  174. rate = new Big(rate);
  175. // grab the video elements and set their playback rate.
  176. var videoElement = document.getElementsByTagName("video")[0];
  177. videoElement.playbackRate = rate;
  178. infobox.innerHTML = rate + "x";
  179. // add infobox to dom if it doesn't already exist.
  180. if (videoElement && !document.getElementById("playbackrate-indicator")) {
  181. videoElement.parentElement.appendChild(infobox);
  182. }
  183. // show infobox and update rate indicator.
  184. if (showInfobox) {
  185. infobox.style.visibility = "visible";
  186. // clear out any previous timers and have the infobox hide after the pre-set time period
  187. var timeoutID;
  188. window.clearTimeout(timeoutID);
  189. timeoutID = window.setTimeout(function() {
  190. infobox.style.visibility = "hidden";
  191. }, getVal("displayTimeMilliSec"));
  192. showDisplay = false;
  193. }
  194. }
  195.  
  196. // mimic vlc keyboard shortcuts
  197. function addKeyListener() {
  198. window.addEventListener('keydown', function(event) {
  199. var key = event.key;
  200. var videoElement = document.getElementsByTagName("video")[0];
  201. var currentPlaybackRate = videoElement.playbackRate;
  202. var speedStep = getVal("speedStep");
  203. switch (key) {
  204. // decrease playback rate if '[' is pressed
  205. case "[":
  206. currentPlaybackRate -= speedStep;
  207. showDisplay = true;
  208. setPlaybackRate(currentPlaybackRate, showDisplay);
  209. break;
  210. // increase playback rate if ']' is pressed
  211. case "]":
  212. currentPlaybackRate += speedStep;
  213. showDisplay = true;
  214. setPlaybackRate(currentPlaybackRate, showDisplay);
  215. break;
  216. default:
  217. return null;
  218. }
  219. if (getVal("enhancerExtention") === true) {
  220. var pref = getVal("pref");
  221. var eventName = getVal("eventName");
  222. window.postMessage({
  223. enhancerforyoutube: pref,
  224. name: eventName,
  225. value: currentPlaybackRate
  226. }, "*");
  227. }
  228. });
  229. }
  230.  
  231. // show the current speed display on the video when mouse wheel is rolled on the speed element if using Enhancer For Youtube
  232. function addWheelListener() {
  233. var enhancerToolbar = document.getElementById("enhancer-for-youtube-toolbar");
  234. var enhancerToolbarChildren = enhancerToolbar.children[0].children;
  235. var eventName = getVal("eventName");
  236. for (var i = 0; i < enhancerToolbarChildren.length; i++) {
  237. if (enhancerToolbarChildren[i].dataset.name === eventName) {
  238. var speedChild = enhancerToolbarChildren[i];
  239. }
  240. }
  241. if (speedChild) {
  242. speedChild.addEventListener('wheel', function(event) {
  243. var wDelta = event.wheelDelta < 0 ? 'down' : 'up';
  244. var videoElement = document.getElementsByTagName("video")[0];
  245. var currentPlaybackRate = videoElement.playbackRate;
  246. switch (wDelta) {
  247. case "down":
  248. // currentPlaybackRate -= speedStep; // uncomment to actually modify the playback speed
  249. showDisplay = true;
  250. setPlaybackRate(currentPlaybackRate, showDisplay);
  251. break;
  252. case "up":
  253. // currentPlaybackRate += speedStep; // uncomment to actually modify the playback speed
  254. showDisplay = true;
  255. setPlaybackRate(currentPlaybackRate, showDisplay);
  256. break;
  257. }
  258. });
  259. }
  260. }
  261.  
  262. async function onReady() {
  263. addKeyListener();
  264. if (getVal("enhancerExtention") === true) {
  265. var i = 0;
  266. do {
  267. await wait(200);
  268. i++
  269. } while (!document.getElementById("enhancer-for-youtube-toolbar") && i < 50);
  270. onExtentionReady(); // Or setTimeout(onReady, 0); if you want it consistently async
  271. }
  272. }
  273.  
  274. function wait(time) {
  275. return new Promise(resolve => {
  276. setTimeout(() => {
  277. resolve();
  278. }, time);
  279. });
  280. }
  281.  
  282. function onExtentionReady() {
  283. addWheelListener();
  284. }
  285.  
  286. function Ext_Detect_NotInstalled(ExtName, ExtID, obj) {
  287. console.log(ExtName + ' Not Installed');
  288. setVal("enhancerExtention", false);
  289. obj.parentNode.removeChild(obj);
  290. }
  291.  
  292. function Ext_Detect_Installed(ExtName, ExtID, obj) {
  293. console.log(ExtName + ' Installed');
  294. setVal("enhancerExtention", true);
  295. obj.parentNode.removeChild(obj);
  296. }
  297.  
  298. var Ext_Detect = function(ExtName, ExtID) {
  299. var is_firefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
  300. var is_chrome = navigator.userAgent.toLowerCase().indexOf('chrome') > -1;
  301. if (is_firefox == true) {
  302. var sMoz = document.createElement('script');
  303. sMoz.onload = function() { Ext_Detect_Installed(ExtName, ExtID, sMoz); };
  304. sMoz.onerror = function() { Ext_Detect_NotInstalled(ExtName, ExtID, sMoz); };
  305. sMoz.src = 'moz-extension://' + ExtID + '/resources/youtube-polymer.js';
  306. document.body.appendChild(sMoz);
  307. } else if (is_chrome == true) {
  308. var sChrome = document.createElement('script');
  309. sChrome.onload = function() { Ext_Detect_Installed(ExtName, ExtID); };
  310. sChrome.onerror = function() { Ext_Detect_NotInstalled(ExtName, ExtID); };
  311. sChrome.src = 'chrome-extension://' + ExtID + '/resources/youtube-polymer.js';
  312. document.body.appendChild(sChrome);
  313. }
  314. }
  315.  
  316. function main() {
  317. const addonID = "397abcaa-dabc-4cd2-b808-0df445170b78";
  318. init();
  319. window.onload = function() { Ext_Detect("Enhancer for YouTube", addonID); };
  320. GMsetup();
  321. infobox.setAttribute("id", "playbackrate-indicator");
  322. infobox.style.position = "absolute";
  323. infobox.style.top = "10%";
  324. infobox.style.right = "10%";
  325. infobox.style.color = "rgba(255, 0, 0, 1)";
  326. infobox.style.zIndex = "99999"; // ensures that it shows above other elements.
  327. infobox.style.visibility = "hidden";
  328. infobox.style.marginTop = "3%";
  329. if (document.readyState !== "loading") {
  330. onReady(); // Or setTimeout(onReady, 0); if you want it consistently async
  331. } else {
  332. document.addEventListener("DOMContentLoaded", onReady);
  333. }
  334. }