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).

当前为 2019-11-29 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name HTML5 Audio/Video Keyboard Shortcuts With OSD
  3. // @namespace https://greasyfork.org/en/users/85671-jcunews
  4. // @version 1.1.10
  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).
  8. // @match *://*/*
  9. // @grant none
  10. // @run-at document-start
  11. // ==/UserScript==
  12.  
  13. /*
  14. Notes:
  15.  
  16. - This script is designed for US keyboards. Some shortcuts won't work on non US keyboard. Non US keyboard users will need to edit keys in the script.
  17.  
  18. - 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.
  19. - Web browser video speeds: Firefox = 0.25 to 5.0; Chrome = 0.1 to 16.0.
  20.  
  21.  
  22. Keyboard Shortcuts:
  23.  
  24. SHIFT+LEFT = Rewind media by 30 seconds
  25. SHIFT+RIGHT = Fast forward media by 30 seconds
  26. CTRL+LEFT = Rewind media by 1 minute
  27. CTRL+RIGHT = Fast forward media by 1 minute
  28. CTRL+SHIFT+LEFT = Rewind media by 5 minutes
  29. CTRL+SHIFT+RIGHT = Fast forward media by 5 minutes
  30. CTRL+/ = Fast forward media by 1.5 minutes
  31. 0 to 9 = Seek media to 0%, 10%, 20%,...90%
  32. SHIFT+0 to SHIFT+9 = Seek media to 5%, 15%, 25%,...95%
  33. CTRL+1 to CTRL+5 = Change audio volume to 20%, 40%, 60$, 80%, 100%
  34. CTRL+[ = Decrease media speed by 0.2x (by default)
  35. CTRL+] = Increase media speed by 0.2x (by default)
  36. CTRL+; = Reset media speed
  37. CTRL+' = Change custom media speed
  38. CTRL+\ = Change unit of media speed increment/decrement
  39.  
  40. For Widescreen Video Viewport:
  41. CTRL+6 = Zoom ultra widescreen content to remove top+bottom borders, but also remove left+right content a bit.
  42. CTRL+7 = Change video aspect ratio for letterbox content. Fix 4:3 letterbox content stretched to widescreen format.
  43. CTRL+8 = Change video aspect ratio for TV content. Fix 4:3 TV content stretched to widescreen format.
  44.  
  45. For 4:3 TV Video Viewport:
  46. CTRL+SHIFT+6 = Change video aspect ratio for ultra widescreen content. Fix ultra widescreen content compressed into 4:3 TV format.
  47. CTRL+SHIFT+7 = Zoom 4:3 letterbox content to remove half of top+bottom borders, but also remove left+right content a little.
  48. This can also be used to half-zoom ultra widescreen content on widescreen viewport. i.e. half-zoom of CTRL+6.
  49. CTRL+SHIFT+8 = Change video aspect ratio for widescreen content. Fix widescreen content compressed into 4:3 TV format.
  50.  
  51. For Any Video Viewport:
  52. CTRL+9 = Reset video aspect ratio
  53. */
  54.  
  55. ((eleOSD, osdTimer) => {
  56.  
  57. //=== CONFIGURATION BEGIN ===
  58.  
  59. //Video speed increment/decrement unit.
  60. var incrementUnit = 0.2;
  61.  
  62. //Duration (in milliseconds) to display On Screen Display (OSD) when changing playback rate. Set to zero or less to disable.
  63. var osdTimeout = 3000;
  64.  
  65. //Keyboard shortcuts.
  66. //key = Key name. String type if single shortcut, or array of string if multiple shortcut (for single function multiple shortcuts).
  67. // Each key name can either be the character which is produced by the key (e.g. `a`, `4`, `*`, etc.),
  68. // or the code name for the key (e.g. `Digit2`, `BracketLeft`, etc.). Both types are case sensitive.
  69. // When SHIFT modifier is used with keys which produces a character, key code name should be used.
  70. // A list of key code names can be found here: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code/code_values
  71. //modifiers = Any combinations of "C", "S", and "A", for Ctrl, Shift, and Alt keys.
  72. var keys = [
  73. { //ctrl+space: seek media to next frame (only when paused. firefox only)
  74. key: " ", modifiers: "C",
  75. func: (key, ele) => ele.seekToNextFrame && ele.seekToNextFrame()
  76. },
  77. { //0 to 9: seek media to 0%,10%,20%,...90%
  78. key: ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"], modifiers: "",
  79. func: (ele, key, keyIndex) => ele.currentTime = keyIndex / 10 * ele.duration
  80. },
  81. { //shift+0 to shift+9: seek media to 5%,15%,25%,...95%
  82. key: [")", "!", "@", "#", "$", "%", "^", "&", "*", "("], modifiers: "S",
  83. func: (ele, key, keyIndex) => ele.currentTime = (keyIndex + 0.5) / 10 * ele.duration
  84. },
  85. { //ctrl+1 to ctrl+5: set audio volume to 20%,40%,60%,80%,100%
  86. key: ["1", "2", "3", "4", "5"], modifiers: "C",
  87. func: (ele, key, keyIndex) => updAudioVolume(ele, (parseInt(key) * 2) / 10)
  88. },
  89. { //shift+left: rewind media by 30 seconds
  90. key: "ArrowLeft", modifiers: "S",
  91. func: (ele, key) => ele.currentTime -= 30
  92. },
  93. { //ctrl+left: rewind media by 1 minute
  94. key: "ArrowLeft", modifiers: "C",
  95. func: (ele, key) => ele.currentTime -= 60
  96. },
  97. { //ctrl+shift+left: rewind media by 5 minutes
  98. key: "ArrowLeft", modifiers: "CS",
  99. func: (ele, key) => ele.currentTime -= 300
  100. },
  101. { //shift+right: fast forward media by 30 seconds
  102. key: "ArrowRight", modifiers: "S",
  103. func: (ele, key) => ele.currentTime += 30
  104. },
  105. { //ctrl+right: fast forward media by 1 minute
  106. key: "ArrowRight", modifiers: "C",
  107. func: (ele, key) => ele.currentTime += 60
  108. },
  109. { //ctrl+shift+right: fast forward media by 5 minutes
  110. key: "ArrowRight", modifiers: "CS",
  111. func: (ele, key) => ele.currentTime += 300
  112. },
  113. { //ctrl+/: fast forward media by 1.5 minutes
  114. key: "/", modifiers: "CS",
  115. func: (ele, key) => ele.currentTime += 90
  116. },
  117. { //ctrl+[: decrease media speed
  118. key: "[", modifiers: "C",
  119. func: (ele, key) => {
  120. key = ele.playbackRate - incrementUnit;
  121. if (key < 0.1) {
  122. key = 0.1;
  123. } else if ((key < 1) && (ele.playbackRate > 1)) key = 1;
  124. updVideoSpeed(ele, key);
  125. }
  126. },
  127. { //ctrl+]: increase media speed
  128. key: "]", modifiers: "C",
  129. func: (ele, key) => {
  130. key = ele.playbackRate + incrementUnit;
  131. if (key > 16) {
  132. key = 16;
  133. } else if ((key > 1) && (ele.playbackRate < 1)) key = 1;
  134. updVideoSpeed(ele, key);
  135. }
  136. },
  137. { //ctrl+;: reset media speed to 1x
  138. key: ";", modifiers: "C",
  139. func: (ele, key) => updVideoSpeed(ele, 1)
  140. },
  141. { //ctrl+': use custom media speed
  142. key: "'", modifiers: "C",
  143. func: (ele, key) => {
  144. 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;
  145. if (isNaN(key = parseFloat(key.trim()))) {
  146. alert("Input must be a number.");
  147. return;
  148. }
  149. updVideoSpeed(ele, (key = parseFloat(key.toFixed(1))) < 0.1 ? 0.1 : (key > 16 ? 16 : key));
  150. }
  151. },
  152. { //ctrl+\: change unit of media speed increment/decrement
  153. key: "\\", modifiers: "C",
  154. func: (ele, key) => {
  155. if ((key = prompt("Enter unit of media speed increment/decrement from 0.1 to 4 (inclusive).", incrementUnit)) === null) return;
  156. if (!isNaN(key = parseFloat(key.trim()))) {
  157. incrementUnit = (key = parseFloat(key.toFixed(1))) < 0.1 ? 0.1 : (key > 4 ? 4 : key);
  158. } else alert("Input must be a number.");
  159. }
  160. },
  161. { //ctrl+6: Zoom ultra widescreen
  162. key: "6", modifiers: "C", videoOnly: true,
  163. func: (ele, key) => updVideoAspect("scale(1.3333)", "Ultra Widescreen Zoom")
  164. },
  165. { //ctrl+7: Letterbox aspect ratio
  166. key: "7", modifiers: "C", videoOnly: true,
  167. func: (ele, key) => updVideoAspect("scaleY(1.3333)", "Letterbox")
  168. },
  169. { //ctrl+8: TV aspect ratio
  170. key: "8", modifiers: "C", videoOnly: true,
  171. func: (ele, key) => updVideoAspect("scaleX(0.75)", "TV")
  172. },
  173. { //ctrl+shift+6: Ultra widescreen aspect ratio
  174. key: "Digit6", modifiers: "CS", videoOnly: true,
  175. func: (ele, key) => updVideoAspect("scaleY(0.7168)", "Ultra Widescreen")
  176. },
  177. { //ctrl+shift+7: Half-zoom letterbox
  178. key: "Digit7", modifiers: "CS", videoOnly: true,
  179. func: (ele, key) => updVideoAspect("scale(1.1666)", "Letterbox Half-Zoom")
  180. },
  181. { //ctrl+shift+8: Widescreen aspect ratio
  182. key: "Digit8", modifiers: "CS", videoOnly: true,
  183. func: (ele, key) => updVideoAspect("scaleY(0.5625)", "Widescreen")
  184. },
  185. { //ctrl+9: reset video aspect ratio
  186. key: "9", modifiers: "C", videoOnly: true,
  187. func: (ele, key) => updVideoAspect("", "Reset")
  188. }
  189. ];
  190. keys.forEach((k, s, m) => {
  191. s = k.modifiers.toUpperCase();
  192. k.modifiers = {ctrl: s.includes("C"), shift: s.includes("S"), alt: s.includes("A")};
  193. });
  194.  
  195. //=== CONFIGURATION END ===
  196.  
  197. function showOSD(s) {
  198. if (osdTimeout < 0) return;
  199. if (eleOSD) {
  200. eleOSD.textContent = s;
  201. } else {
  202. eleOSD = document.createElement("DIV");
  203. 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";
  204. eleOSD.textContent = s;
  205. document.body.appendChild(eleOSD);
  206. }
  207. clearTimeout(osdTimer);
  208. osdTimer = setTimeout(() => {
  209. eleOSD.remove();
  210. eleOSD = null;
  211. }, osdTimeout);
  212. }
  213.  
  214. function stopEvent(ev) {
  215. ev.preventDefault();
  216. ev.stopPropagation();
  217. ev.stopImmediatePropagation();
  218. }
  219.  
  220. function updVideoSpeed(ele, spd, e) {
  221. if ((location.hostname === "www.youtube.com") && (e = ele.parentNode.parentNode).setPlaybackRate && (spd >= 0.25) && (spd <= 2)) {
  222. e.setPlaybackRate(spd = parseFloat(spd.toFixed(1)));
  223. } else ele.playbackRate = spd = parseFloat(spd.toFixed(1));
  224. showOSD("Speed " + spd + "x");
  225. }
  226.  
  227. function updVideoAspect(asp, label, s) {
  228. if (!(s = document.getElementById("vidAspOvr"))) document.body.appendChild(s = document.createElement("STYLE")).id = "vidAspOvr";
  229. s.innerHTML = asp ? `video{transform:${asp}!important}` : "";
  230. showOSD("Ratio: " + label);
  231. }
  232.  
  233. function updAudioVolume(ele, vol) {
  234. if ((location.hostname === "www.youtube.com") && (e = ele.parentNode.parentNode).setVolume) {
  235. e.setVolume(vol * 100);
  236. } else ele.volume = vol;
  237. showOSD("Audio " + (vol * 100) + "%");
  238. }
  239.  
  240. incrementUnit = parseFloat((incrementUnit < 0.1 ? 0.1 : (incrementUnit > 1 ? 1 : incrementUnit)).toFixed(1));
  241. addEventListener("keydown", function(ev, ele) {
  242. if ((!(ele = document.activeElement) || !((ele.contentEditable === "true") || ["BUTTON", "INPUT", "SELECT", "TEXTAREA"].includes(ele.tagName))) && (ele = document.querySelector("video,audio"))) {
  243. keys.some((k, a, i) => {
  244. a = Array.isArray(k.key);
  245. if (
  246. ((!a && ((k.key === ev.key) || (k.key === ev.code))) || (a && (((i = k.key.indexOf(ev.key)) >= 0) || ((i = k.key.indexOf(ev.code)) >= 0)))) &&
  247. (k.modifiers.ctrl === ev.ctrlKey) && (k.modifiers.shift === ev.shiftKey) && (k.modifiers.alt === ev.altKey) &&
  248. (!k.videoOnly || (ele.tagName === "VIDEO"))
  249. ) {
  250. stopEvent(ev);
  251. k.func(ele, ev.key, a ? i : null);
  252. return true;
  253. }
  254. });
  255. }
  256. }, true);
  257.  
  258. })();