OneKey Speed Control

A script to control video playback speed and navigation keys

  1. // ==UserScript==
  2. // @name OneKey Speed Control
  3. // @namespace http://tampermonkey.net/
  4. // @version 2024-03-03
  5. // @description A script to control video playback speed and navigation keys
  6. // @author ExistoT01
  7. // @match https://*/*
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com
  9. // @grant none
  10. // @license MIT
  11. // ==/UserScript==
  12.  
  13. (function() {
  14. 'use strict';
  15.  
  16. const logPrefix = "[OKSC]: " //日志前缀
  17.  
  18. const TIME_UNIT = 10; //快进,倒带单位(s)
  19. const RATE_UNIT = 0.25; //调整倍率单位
  20. const RATE_MAX = 10; //最大播放速率
  21. const RATE_MIN = 0.25; //最小播放速率
  22. const LOCKED_SPEED = 3; //倍速播放速度
  23.  
  24. const TYPE_CHANGE_SPEED = 1; //类型1,改变视频播放速度
  25. const TYPE_LOCK_SPEED = 2; //类型2,锁定倍速播放
  26. const TYPE_CHANGE_PROGRESS_FORWORD = 3; //类型3,快进
  27. const TYPE_CHANGE_PROGRESS_REWIND = 4; //类型4,倒退
  28. const TYPE_PLAY = 5;
  29. const TYPE_PAUSE = 6;
  30.  
  31. const KEY_INC_SPEED = 'x';
  32. const KEY_DEC_SPEED = 'z';
  33. const KEY_LOCK_SPEED = 'u';
  34. const KEY_REWIND = 'j';
  35. const KEY_FORWORD = 'l';
  36. const KEY_PLAY_OR_PAUSE = 'k';
  37.  
  38. const MSG_VIDEO_NOT_FOUND = 'video element not found!';
  39.  
  40. const HOSTNAME_YOUTUBE = 'www.youtube.com';
  41. const HOSTNAME_DISNEYPLUS = 'www.disneyplus.com';
  42.  
  43. const VIDEO_SELECTOR = 'video';
  44. const VIDEO_SELECTOR_DISNEYPLUS = '#hivePlayer';
  45.  
  46. let originalSpeed;
  47. let isSpeedLocked = false;
  48. let timeoutId;
  49.  
  50. // Your code here...
  51. document.addEventListener('keydown', function(event) {
  52. // console.log(logPrefix, 'keydown', event.key);
  53.  
  54. if (isSpeedLocked) {
  55. return;
  56. }
  57.  
  58. let video = getVideo();
  59. if (!video) return;
  60.  
  61. // Increase speed
  62. if (event.key === KEY_INC_SPEED) {
  63. video.playbackRate = Math.min(video.playbackRate + RATE_UNIT, RATE_MAX);
  64. showNotification(TYPE_CHANGE_SPEED, video.playbackRate);
  65. return;
  66. }
  67. // Decrease speed
  68. else if (event.key === KEY_DEC_SPEED) {
  69. video.playbackRate = Math.max(video.playbackRate - RATE_UNIT, RATE_MIN);
  70. showNotification(TYPE_CHANGE_SPEED, video.playbackRate);
  71. return;
  72. }
  73. // Lock speed
  74. else if (event.key === KEY_LOCK_SPEED) {
  75. isSpeedLocked = true;
  76. originalSpeed = video.playbackRate;
  77. video.playbackRate = LOCKED_SPEED;
  78. clearTimeout(timeoutId);
  79. showNotification(TYPE_LOCK_SPEED, LOCKED_SPEED);
  80. return;
  81. }
  82.  
  83. // Youtube already has this feature
  84. if (window.location.hostname !== HOSTNAME_YOUTUBE) {
  85. switch (event.key) {
  86. case KEY_REWIND: // Jump back 10 seconds
  87. video.currentTime = Math.max(video.currentTime - TIME_UNIT, 0);
  88. showNotification(TYPE_CHANGE_PROGRESS_REWIND);
  89. break;
  90. case KEY_PLAY_OR_PAUSE: // Toggle play/pause
  91. if (video.paused) {
  92. video.play();
  93. showNotification(TYPE_PLAY, 0);
  94. } else {
  95. video.pause();
  96. showNotification(TYPE_PAUSE, 0);
  97. }
  98. break;
  99. case KEY_FORWORD: // Jump forward 10 seconds
  100. video.currentTime = Math.min(video.currentTime + TIME_UNIT, video.duration);
  101. showNotification(TYPE_CHANGE_PROGRESS_FORWORD);
  102. break;
  103. }
  104. }
  105. });
  106.  
  107. document.addEventListener('keyup', function(event) {
  108. // console.log(logPrefix, 'keyup', event.key);
  109.  
  110. isSpeedLocked = false;
  111.  
  112. let video = getVideo();
  113. if (!video) return;
  114.  
  115. if (event.key === KEY_LOCK_SPEED) {
  116. video.playbackRate = originalSpeed;
  117. notification.style.display = 'none';
  118. showNotification(TYPE_CHANGE_SPEED, originalSpeed);
  119. }
  120. })
  121.  
  122.  
  123. // CSS for the notification
  124. var style = document.createElement('style');
  125. style.type = 'text/css';
  126.  
  127. // Use a text node to safely insert CSS rules
  128. style.appendChild(document.createTextNode(`.speed-notification {
  129. position: fixed;
  130. bottom: 50px;
  131. right: 20px;
  132. background-color: black;
  133. color: white;
  134. padding: 8px;
  135. border-radius: 4px;
  136. z-index: 9999999;
  137. display: none;
  138. }`));
  139.  
  140. document.head.appendChild(style);
  141.  
  142. // Create the notification element
  143. var notification = document.createElement('div');
  144. notification.className = 'speed-notification';
  145. document.body.appendChild(notification);
  146.  
  147. // Function to show notification
  148. function showNotification(type, speed) {
  149.  
  150. if (type === TYPE_LOCK_SPEED) {
  151. notification.textContent = 'Speed: ' + LOCKED_SPEED + 'x';
  152. notification.style.display = 'block';
  153. return;
  154. }
  155.  
  156. if (type === TYPE_CHANGE_SPEED) {
  157. notification.textContent = 'Speed: ' + speed + 'x';
  158. } else if (type === TYPE_CHANGE_PROGRESS_FORWORD) {
  159. notification.textContent = '-->>';
  160. } else if (type === TYPE_CHANGE_PROGRESS_REWIND) {
  161. notification.textContent = '<<--';
  162. } else if (type === TYPE_PLAY) {
  163. notification.textContent = ' || ';
  164. } else if (type === TYPE_PAUSE) {
  165. notification.textContent = ' |> ';
  166. }
  167.  
  168. notification.style.display = 'block';
  169.  
  170. // Hide the notification after 2 seconds
  171. clearTimeout(timeoutId);
  172. timeoutId = setTimeout(function() {
  173. notification.style.display = 'none';
  174. }, 2000);
  175.  
  176. }
  177.  
  178. // function to get video element
  179. function getVideo() {
  180. let video;
  181.  
  182. // Disney+ odd version
  183. // if (window.location.hostname === HOSTNAME_DISNEYPLUS) {
  184. // let player = document.querySelector('disney-web-player');
  185. // if (!player) return;
  186. // video = player.shadowRoot.querySelector('video');
  187. // }
  188. // else {
  189. // video = document.querySelector('video');
  190. // }
  191.  
  192. if (window.location.hostname === HOSTNAME_DISNEYPLUS) {
  193. video = document.querySelector(VIDEO_SELECTOR_DISNEYPLUS);
  194. }
  195. else {
  196. video = document.querySelector(VIDEO_SELECTOR);
  197. }
  198.  
  199.  
  200. if (!video) {
  201. console.log(logPrefix, MSG_VIDEO_NOT_FOUND);
  202. return;
  203. }
  204.  
  205. return video;
  206. }
  207.  
  208.  
  209.  
  210.  
  211.  
  212.  
  213. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址