HTML5 Audio/Video Keyboard Shortcuts With OSD

Adds keyboard shortcuts for controlling HTML5 media player (audio/video) with OSD support. Seek media to 0%, 5%, 10%, ..., or 95%. Rewind and fast fordward media by 30 seconds, 1 minute, and 5 minutes. Change media speed even beyond YouTube's speed limit. Change audio volume to 20%, 40%, 60%, 80%, or 100%. Change video aspect ratio for TV and letterbox content (for widescreen monitors). This script is designed for US keyboards. Non US keyboard users will need to edit keys in the script.

当前为 2019-09-26 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name HTML5 Audio/Video Keyboard Shortcuts With OSD
  3. // @namespace https://greasyfork.org/en/users/85671-jcunews
  4. // @version 1.0.1
  5. // @license AGPLv3
  6. // @author jcunews
  7. // @description Adds keyboard shortcuts for controlling HTML5 media player (audio/video) with OSD support. Seek media to 0%, 5%, 10%, ..., or 95%. Rewind and fast fordward media by 30 seconds, 1 minute, and 5 minutes. Change media speed even beyond YouTube's speed limit. Change audio volume to 20%, 40%, 60%, 80%, or 100%. Change video aspect ratio for TV and letterbox content (for widescreen monitors). This script is designed for US keyboards. Non US keyboard users will need to edit keys in the script.
  8. // @match *://*/*
  9. // @grant none
  10. // @run-at document-start
  11. // ==/UserScript==
  12.  
  13. /*
  14. Keyboard Shortcuts:
  15.  
  16. SHIFT+LEFT = Rewind media by 30 seconds
  17. SHIFT+RIGHT = Fast forward media by 30 seconds
  18. CTRL+LEFT = Rewind media by 1 minute
  19. CTRL+RIGHT = Fast forward media by 1 minute
  20. CTRL+SHIFT+LEFT = Rewind media by 5 minutes
  21. CTRL+SHIFT+RIGHT = Fast forward media by 5 minutes
  22. CTRL+/ = Fast forward media by 1.5 minutes
  23. 0 to 9 = Seek media to 0%, 10%, 20%,...90%
  24. SHIFT+0 to SHIFT+9 = Seek media to 5%, 15%, 25%,...95%
  25. CTRL+1 to CTRL+5 = Change audio volume to 20%, 40%, 60$, 80%, 100%
  26. CTRL+[ = Decrease media speed by 0.2x (by default)
  27. CTRL+] = Increase media speed by 0.2x (by default)
  28. CTRL+; = Reset media speed
  29. CTRL+' = Change custom media speed
  30. CTRL+\ = Change unit of media speed increment/decrement
  31. CTRL+7 = Change video aspect ratio for letterbox content. Fix 4:3 letterbox content stretched to widescreen format.
  32. CTRL+8 = Change video aspect ratio for TV content. Fix 4:3 TV content stretched to widescreen format.
  33. CTRL+9 = Reset video aspect ratio
  34. */
  35.  
  36. ((eleOSD, osdTimer) => {
  37.  
  38. /*
  39. Notes:
  40. - In YouTube, if the video speed is below 0.25x or above 2x, the YouTube setting display will be capped to 0.1x or 2x.
  41. - Web browser video speeds: Firefox = 0.25 to 5.0; Chrome = 0.1 to 16.0.
  42. */
  43.  
  44. //=== CONFIGURATION BEGIN ===
  45.  
  46. //Video speed increment/decrement unit.
  47. var incrementUnit = 0.2;
  48.  
  49. //Duration (in milliseconds) to display On Screen Display (OSD) when changing playback rate. Set to zero or less to disable.
  50. var osdTimeout = 3000;
  51.  
  52. //keyboard shortcuts.
  53. //key = key name. string type if single shortcut, or array of string if multiple shortcut (for single function multiple shortcuts).
  54. //modifiers = any combinations of "C", "S", and "A", for Ctrl, Shift, and Alt keys.
  55. var keys = [
  56. { //ctrl+space: seek media to next frame (only when paused. firefox only)
  57. key: " ", modifiers: "C",
  58. func: (key, ele) => ele.seekToNextFrame && ele.seekToNextFrame()
  59. },
  60. { //0 to 9: seek media to 0%,10%,20%,...90%
  61. key: ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"], modifiers: "",
  62. func: (ele, key, keyIndex) => ele.currentTime = keyIndex / 10 * ele.duration
  63. },
  64. { //shift+0 to shift+9: seek media to 5%,15%,25%,...95%
  65. key: [")", "!", "@", "#", "$", "%", "^", "&", "*", "("], modifiers: "S",
  66. func: (ele, key, keyIndex) => ele.currentTime = (keyIndex + 0.5) / 10 * ele.duration
  67. },
  68. { //ctrl+1 to ctrl+5: set audio volume to 20%,40%,60%,80%,100%
  69. key: ["1", "2", "3", "4", "5"], modifiers: "C",
  70. func: (ele, key, keyIndex) => updAudioVolume(ele, (parseInt(key) * 2) / 10)
  71. },
  72. { //shift+left: rewind media by 30 seconds
  73. key: "ArrowLeft", modifiers: "S",
  74. func: (ele, key) => ele.currentTime -= 30
  75. },
  76. { //ctrl+left: rewind media by 1 minute
  77. key: "ArrowLeft", modifiers: "C",
  78. func: (ele, key) => ele.currentTime -= 60
  79. },
  80. { //ctrl+shift+left: rewind media by 5 minutes
  81. key: "ArrowLeft", modifiers: "CS",
  82. func: (ele, key) => ele.currentTime -= 300
  83. },
  84. { //shift+right: fast forward media by 30 seconds
  85. key: "ArrowRight", modifiers: "S",
  86. func: (ele, key) => ele.currentTime += 30
  87. },
  88. { //ctrl+right: fast forward media by 1 minute
  89. key: "ArrowRight", modifiers: "C",
  90. func: (ele, key) => ele.currentTime += 60
  91. },
  92. { //ctrl+shift+right: fast forward media by 5 minutes
  93. key: "ArrowRight", modifiers: "CS",
  94. func: (ele, key) => ele.currentTime += 300
  95. },
  96. { //ctrl+/: fast forward media by 1.5 minutes
  97. key: "/", modifiers: "CS",
  98. func: (ele, key) => ele.currentTime += 90
  99. },
  100. { //ctrl+[: decrease media speed
  101. key: "[", modifiers: "C",
  102. func: (ele, key) => {
  103. key = ele.playbackRate - incrementUnit;
  104. if (key < 0.1) {
  105. key = 0.1;
  106. } else if ((key < 1) && (ele.playbackRate > 1)) key = 1;
  107. updVideoSpeed(ele, key);
  108. }
  109. },
  110. { //ctrl+]: increase media speed
  111. key: "]", modifiers: "C",
  112. func: (ele, key) => {
  113. key = ele.playbackRate + incrementUnit;
  114. if (key > 16) {
  115. key = 16;
  116. } else if ((key > 1) && (ele.playbackRate < 1)) key = 1;
  117. updVideoSpeed(ele, key);
  118. }
  119. },
  120. { //ctrl+;: reset media speed to 1x
  121. key: ";", modifiers: "C",
  122. func: (ele, key) => updVideoSpeed(ele, 1)
  123. },
  124. { //ctrl+': use custom media speed
  125. key: "'", modifiers: "C",
  126. func: (ele, key) => {
  127. if ((key = prompt("Enter media speed from 0.1 to 16 (inclusive).\ne.g.: 1 = Normal, 0.5 = Half, 2 = Double, 3 = Triple, etc.", ele.playbackRate)) === null) return;
  128. if (isNaN(key = parseFloat(key.trim()))) {
  129. alert("Input must be a number.");
  130. return;
  131. }
  132. updVideoSpeed(ele, (key = parseFloat(key.toFixed(1))) < 0.1 ? 0.1 : (key > 16 ? 16 : key));
  133. }
  134. },
  135. { //ctrl+\: change unit of media speed increment/decrement
  136. key: "\\", modifiers: "C",
  137. func: (ele, key) => {
  138. if ((key = prompt("Enter unit of media speed increment/decrement from 0.1 to 4 (inclusive).", incrementUnit)) === null) return;
  139. if (!isNaN(key = parseFloat(key.trim()))) {
  140. incrementUnit = (key = parseFloat(key.toFixed(1))) < 0.1 ? 0.1 : (key > 4 ? 4 : key);
  141. } else alert("Input must be a number.");
  142. }
  143. },
  144. { //ctrl+7: Letterbox aspect ratio
  145. key: "7", modifiers: "C", videoOnly: true,
  146. func: (ele, key) => updVideoAspect("scaleY(1.3333)", "Letterbox")
  147. },
  148. { //ctrl+8: TV aspect ratio
  149. key: "8", modifiers: "C", videoOnly: true,
  150. func: (ele, key) => updVideoAspect("scaleX(0.75)", "TV")
  151. },
  152. { //ctrl+9: reset video aspect ratio
  153. key: "9", modifiers: "C", videoOnly: true,
  154. func: (ele, key) => updVideoAspect("", "Reset")
  155. }
  156. ];
  157. keys.forEach((k, s, m) => {
  158. s = k.modifiers.toUpperCase();
  159. k.modifiers = {ctrl: s.includes("C"), shift: s.includes("S"), alt: s.includes("A")};
  160. });
  161.  
  162. //=== CONFIGURATION END ===
  163.  
  164. function showOSD(s) {
  165. if (osdTimeout < 0) return;
  166. if (eleOSD) {
  167. eleOSD.textContent = s;
  168. } else {
  169. eleOSD = document.createElement("DIV");
  170. eleOSD.style.cssText = "position:fixed;z-index:999999999;right:.5rem;bottom:.5rem;margin:0;padding:.2rem .5rem .1rem .5rem;width:auto;height:auto;font:normal 16pt/normal sans-serif;background:#444;color:#fff";
  171. eleOSD.textContent = s;
  172. document.body.appendChild(eleOSD);
  173. }
  174. clearTimeout(osdTimer);
  175. osdTimer = setTimeout(() => {
  176. eleOSD.remove();
  177. eleOSD = null;
  178. }, osdTimeout);
  179. }
  180.  
  181. function stopEvent(ev) {
  182. ev.preventDefault();
  183. ev.stopPropagation();
  184. ev.stopImmediatePropagation();
  185. }
  186.  
  187. function updVideoSpeed(ele, spd, e) {
  188. if ((location.hostname === "www.youtube.com") && (e = ele.parentNode.parentNode).setPlaybackRate && (spd >= 0.25) && (spd <= 2)) {
  189. e.setPlaybackRate(spd = parseFloat(spd.toFixed(1)));
  190. } else ele.playbackRate = spd = parseFloat(spd.toFixed(1));
  191. showOSD("Speed " + spd + "x");
  192. }
  193.  
  194. function updVideoAspect(asp, label, s) {
  195. if (!(s = document.getElementById("vidAspOvr"))) document.body.appendChild(s = document.createElement("STYLE")).id = "vidAspOvr";
  196. s.innerHTML = asp ? `video{transform:${asp}!important}` : "";
  197. showOSD("Aspect " + label);
  198. }
  199.  
  200. function updAudioVolume(ele, vol) {
  201. if ((location.hostname === "www.youtube.com") && (e = ele.parentNode.parentNode).setVolume) {
  202. e.setVolume(vol * 100);
  203. } else ele.volume = vol;
  204. showOSD("Audio " + (vol * 100) + "%");
  205. }
  206.  
  207. incrementUnit = parseFloat((incrementUnit < 0.1 ? 0.1 : (incrementUnit > 1 ? 1 : incrementUnit)).toFixed(1));
  208. addEventListener("keydown", function(ev, ele) {
  209. if ((!(ele = document.activeElement) || !["BUTTON", "INPUT", "SELECT", "TEXTAREA"].includes(ele.tagName)) && (ele = document.querySelector("video,audio"))) {
  210. keys.some(k => {
  211. if (
  212. ((Array.isArray(k.key) && k.key.includes(ev.key)) || (!Array.isArray(k.key) && (k.key === ev.key))) &&
  213. (k.modifiers.ctrl === ev.ctrlKey) && (k.modifiers.shift === ev.shiftKey) && (k.modifiers.alt === ev.altKey) &&
  214. (!k.videoOnly || (ele.tagName === "VIDEO"))
  215. ) {
  216. stopEvent(ev);
  217. k.func(ele, ev.key, Array.isArray(k.key) ? k.key.indexOf(ev.key) : null);
  218. return true;
  219. }
  220. });
  221. }
  222. }, true);
  223.  
  224. })();