YouTube 按键加速播放

在YouTube上按住右箭头键时视频加速到2.5倍速,避免与快进功能冲突

当前为 2024-12-17 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name YouTube Speed Control
  3. // @name:zh-CN YouTube 按键加速播放
  4. // @namespace https://github.com/landrarwolf/youtube-speed-control
  5. // @version 0.10
  6. // @description Hold right arrow key to speed up YouTube video to 2.5x, without interfering with the forward function
  7. // @description:zh-CN 在YouTube上按住右箭头键时视频加速到2.5倍速,避免与快进功能冲突
  8. // @icon https://img.icons8.com/?size=100&id=9991&format=png&color=000000
  9. // @author landrarwolf
  10. // @match https://www.youtube.com/*
  11. // @license MIT
  12. // @supportURL https://github.com/landrarwolf/youtube-speed-control/issues
  13. // @homepageURL https://github.com/landrarwolf/youtube-speed-control
  14. // @grant none
  15. // ==/UserScript==
  16.  
  17. // 用户可配置选项
  18. const config = {
  19. speedMultiplier: 2.5, // 加速倍数
  20. keyPressDelay: 200, // 按键延迟时间(毫秒)
  21. darkTheme: {
  22. background: 'rgba(33, 33, 33, 0.9)',
  23. text: '#ffffff',
  24. shadow: '0 2px 4px rgba(0, 0, 0, 0.2)'
  25. },
  26. lightTheme: {
  27. background: 'rgba(255, 255, 255, 0.9)',
  28. text: '#000000',
  29. shadow: '0 2px 4px rgba(0, 0, 0, 0.1)'
  30. }
  31. };
  32.  
  33. // 在文件开头添加语言配置
  34. const i18n = {
  35. en: {
  36. speedIndicator: `⚡ ${config.speedMultiplier}x Speed`
  37. },
  38. zh: {
  39. speedIndicator: `⚡ ${config.speedMultiplier}x 加速中`
  40. }
  41. };
  42.  
  43. // 在文件开头添加
  44. const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
  45.  
  46. // 获取当前语言
  47. function getCurrentLanguage() {
  48. const lang = navigator.language.toLowerCase().split('-')[0];
  49. return lang in i18n ? lang : 'en';
  50. }
  51.  
  52. (function () {
  53. 'use strict';
  54.  
  55. let normalSpeed = 1.0; // 保存正常播放速度
  56. let speedTimeout = null; // 用于延迟处理速度变化
  57. let isSpeedUp = false; // 标记是否处于加速状态
  58. let pressStartTime = 0; // 记录按键开始时间
  59. let speedIndicator = null; // 速度提示元素
  60.  
  61. // 创建速度提示元素
  62. function createSpeedIndicator() {
  63. const indicator = document.createElement('div');
  64.  
  65. // 基础样式
  66. const baseStyles = `
  67. position: fixed;
  68. top: 20px;
  69. left: 50%;
  70. transform: translateX(-50%);
  71. padding: 8px 16px;
  72. border-radius: 8px;
  73. z-index: 2147483647;
  74. font-size: 14px;
  75. font-weight: 500;
  76. font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;
  77. display: none;
  78. opacity: 0;
  79. transition: all 0.2s ease;
  80. backdrop-filter: blur(8px);
  81. -webkit-backdrop-filter: blur(8px);
  82. `;
  83.  
  84. // 创建样式更新函数
  85. const updateTheme = () => {
  86. const isDarkMode = darkModeMediaQuery.matches;
  87. const theme = isDarkMode ? config.darkTheme : config.lightTheme;
  88.  
  89. indicator.style.cssText = `
  90. ${baseStyles}
  91. background: ${theme.background};
  92. color: ${theme.text};
  93. box-shadow: ${theme.shadow};
  94. `;
  95. };
  96.  
  97. // 初始化样式
  98. updateTheme();
  99.  
  100. // 监听系统主题变化
  101. darkModeMediaQuery.addEventListener('change', updateTheme);
  102.  
  103. // 使用当前语言的文本
  104. indicator.textContent = i18n[getCurrentLanguage()].speedIndicator;
  105.  
  106. // 将指示器添加到合适的容器中
  107. const container = document.fullscreenElement || document.body;
  108. container.appendChild(indicator);
  109.  
  110. // 监听全屏变化事件
  111. document.addEventListener('fullscreenchange', () => {
  112. const newContainer = document.fullscreenElement || document.body;
  113. if (indicator.parentElement !== newContainer) {
  114. indicator.remove();
  115. newContainer.appendChild(indicator);
  116. }
  117. });
  118.  
  119. return indicator;
  120. }
  121.  
  122. // 显示速度提示
  123. function showSpeedIndicator() {
  124. try {
  125. if (!speedIndicator) {
  126. speedIndicator = createSpeedIndicator();
  127. }
  128. speedIndicator.style.display = 'block';
  129. requestAnimationFrame(() => {
  130. speedIndicator.style.opacity = '1';
  131. speedIndicator.style.transform = 'translateX(-50%) translateY(0)';
  132. });
  133. } catch (error) {
  134. console.error('显示速度提示时出错:', error);
  135. }
  136. }
  137.  
  138. // 隐藏速度提示
  139. function hideSpeedIndicator() {
  140. if (speedIndicator) {
  141. speedIndicator.style.opacity = '0';
  142. speedIndicator.style.transform = 'translateX(-50%) translateY(-10px)';
  143. setTimeout(() => {
  144. speedIndicator.style.display = 'none';
  145. }, 200);
  146. }
  147. }
  148.  
  149. // 监听键盘按下事件
  150. document.addEventListener('keydown', function (event) {
  151. if (event.key === 'ArrowRight') {
  152. // 立即阻止事件传播,防止触发 YouTube 的快进功能
  153. event.preventDefault();
  154. event.stopPropagation();
  155.  
  156. if (!event.repeat) {
  157. pressStartTime = Date.now();
  158. speedTimeout = setTimeout(() => {
  159. const video = document.querySelector('video');
  160. if (video) {
  161. normalSpeed = video.playbackRate;
  162. video.playbackRate = config.speedMultiplier;
  163. isSpeedUp = true;
  164. showSpeedIndicator();
  165. }
  166. }, config.keyPressDelay);
  167. }
  168. }
  169. }, true);
  170.  
  171. // 监听键盘释放事件
  172. document.addEventListener('keyup', function (event) {
  173. if (event.key === 'ArrowRight') {
  174. const pressDuration = Date.now() - pressStartTime;
  175.  
  176. if (speedTimeout) {
  177. clearTimeout(speedTimeout);
  178. speedTimeout = null;
  179. }
  180.  
  181. // 如果按键时间小于延迟时间,模拟一次快进操作
  182. if (pressDuration < config.keyPressDelay) {
  183. const video = document.querySelector('video');
  184. if (video) {
  185. video.currentTime += 5; // 手动触发5秒快进
  186. }
  187. }
  188.  
  189. if (isSpeedUp) {
  190. const video = document.querySelector('video');
  191. if (video) {
  192. video.playbackRate = normalSpeed;
  193. isSpeedUp = false;
  194. hideSpeedIndicator();
  195. }
  196. }
  197. }
  198. }, true);
  199.  
  200. // 在 IIFE 内部添加
  201. const cleanup = () => {
  202. if (speedIndicator && speedIndicator.parentNode) {
  203. speedIndicator.parentNode.removeChild(speedIndicator);
  204. }
  205. if (speedTimeout) {
  206. clearTimeout(speedTimeout);
  207. }
  208. };
  209.  
  210. // 添加清理监听
  211. if (typeof window.onbeforeunload === 'function') {
  212. const originalUnload = window.onbeforeunload;
  213. window.onbeforeunload = function () {
  214. cleanup();
  215. return originalUnload.apply(this, arguments);
  216. };
  217. } else {
  218. window.onbeforeunload = cleanup;
  219. }
  220. })();

QingJ © 2025

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