YouTube 快速倍速与音量控制界面

在YouTube的中下部区域添加一个快速速度和音量界面,而不干扰现有控件。

  1. // ==UserScript==
  2. // @name YouTube Quick Speed & Volume Interface
  3. // @name:zh-TW YouTube 快速倍速與音量控制介面
  4. // @name:zh-CN YouTube 快速倍速与音量控制界面
  5. // @namespace https://twitter.com/CobleeH
  6. // @version 1.17
  7. // @description Add a quick speed and volume interface to YouTube's middle-bottom area without interfering with existing controls.
  8. // @description:zh-TW 在YouTube的中下部區域添加一個快速速度和音量界面,而不干擾現有控件。
  9. // @description:zh-CN 在YouTube的中下部区域添加一个快速速度和音量界面,而不干扰现有控件。
  10. // @author CobleeH
  11. // @match https://www.youtube.com/*
  12. // @grant none
  13. // @license MIT
  14. // ==/UserScript==
  15.  
  16. (function() {
  17. 'use strict';
  18.  
  19. const speeds = [0.5, 1, 1.5, 2, 3];
  20. const volumes = [0, 0.15, 0.35, 0.65, 1];
  21.  
  22. // 創建整體容器
  23. function createControlContainer() {
  24. const container = document.createElement('div');
  25. container.classList.add('ytp-control-container');
  26. container.style.display = 'flex';
  27. container.style.flexDirection = 'column';
  28. container.style.alignItems = 'center';
  29. container.style.position = 'absolute';
  30. container.style.right = '15px';
  31. container.style.bottom = '62px';
  32. container.style.zIndex = '9999';
  33. container.style.background = 'rgba(0, 0, 0, 0.7)';
  34. container.style.borderRadius = '5px';
  35. container.style.padding = '3px 6px'; // 縮小內邊距
  36. container.style.color = '#fff';
  37. container.style.fontSize = '12px'; // 字體縮小
  38. container.style.lineHeight = '1.2';
  39.  
  40. const volumeOptions = createVolumeOptions();
  41. const speedOptions = createSpeedOptions();
  42.  
  43. container.appendChild(volumeOptions);
  44. container.appendChild(speedOptions);
  45. return container;
  46. }
  47.  
  48. // 創建音量控件
  49. function createVolumeOptions() {
  50. const volumeContainer = document.createElement('div');
  51. volumeContainer.classList.add('ytp-volume-options');
  52. volumeContainer.style.display = 'flex';
  53. volumeContainer.style.alignItems = 'center';
  54. volumeContainer.style.marginBottom = '4px'; // 縮小上下間距
  55.  
  56. const label = document.createElement('span');
  57. label.innerText = 'Vol';
  58. label.style.marginRight = '6px';
  59. volumeContainer.appendChild(label);
  60.  
  61. volumes.forEach(volume => {
  62. const option = document.createElement('div');
  63. option.innerText = (volume * 100) + '%';
  64. option.style.cursor = 'pointer';
  65. option.style.margin = '0 3px'; // 縮小選項間距
  66. option.style.padding = '2px 4px'; // 縮小內邊距
  67.  
  68. option.addEventListener('click', () => {
  69. const video = document.querySelector('video');
  70. if (video) {
  71. video.volume = volume;
  72. highlightOption(option, '.ytp-volume-options div');
  73. }
  74. });
  75.  
  76. volumeContainer.appendChild(option);
  77. });
  78.  
  79. return volumeContainer;
  80. }
  81.  
  82. // 創建速度控件
  83. function createSpeedOptions() {
  84. const speedContainer = document.createElement('div');
  85. speedContainer.classList.add('ytp-speed-options');
  86. speedContainer.style.display = 'flex';
  87. speedContainer.style.alignItems = 'center';
  88.  
  89. const label = document.createElement('span');
  90. label.innerText = 'Spd';
  91. label.style.marginRight = '6px';
  92. speedContainer.appendChild(label);
  93.  
  94. speeds.forEach(speed => {
  95. const option = document.createElement('div');
  96. option.innerText = speed + 'x';
  97. option.style.cursor = 'pointer';
  98. option.style.margin = '0 3px'; // 縮小選項間距
  99. option.style.padding = '2px 4px'; // 縮小內邊距
  100.  
  101. option.addEventListener('click', () => {
  102. const video = document.querySelector('video');
  103. if (video) {
  104. video.playbackRate = speed;
  105. highlightOption(option, '.ytp-speed-options div');
  106. }
  107. });
  108.  
  109. speedContainer.appendChild(option);
  110. });
  111.  
  112. return speedContainer;
  113. }
  114.  
  115. // 高亮所選選項
  116. function highlightOption(selectedOption, selector) {
  117. const options = document.querySelectorAll(selector);
  118. options.forEach(option => {
  119. option.style.color = '#fff';
  120. option.style.fontWeight = 'normal';
  121. });
  122. selectedOption.style.color = '#ff0';
  123. selectedOption.style.fontWeight = 'bold';
  124. }
  125.  
  126. // 插入控件到播放器
  127. function insertControls() {
  128. const chromeBottom = document.querySelector('.ytp-chrome-bottom');
  129. if (!chromeBottom || document.querySelector('.ytp-control-container')) return;
  130.  
  131. const controlContainer = createControlContainer();
  132. chromeBottom.appendChild(controlContainer);
  133.  
  134. setInitialHighlight();
  135. }
  136.  
  137. // 初始化高亮選項
  138. function setInitialHighlight() {
  139. const video = document.querySelector('video');
  140. if (!video) return;
  141.  
  142. const currentSpeed = video.playbackRate || 1;
  143. const speedOptions = document.querySelectorAll('.ytp-speed-options div');
  144. speedOptions.forEach(option => {
  145. if (option.innerText === currentSpeed + 'x') {
  146. highlightOption(option, '.ytp-speed-options div');
  147. }
  148. });
  149.  
  150. const currentVolume = video.volume || 1;
  151. const volumeOptions = document.querySelectorAll('.ytp-volume-options div');
  152. volumeOptions.forEach(option => {
  153. if (option.innerText === (currentVolume * 100) + '%') {
  154. highlightOption(option, '.ytp-volume-options div');
  155. }
  156. });
  157. }
  158.  
  159. const observer = new MutationObserver(() => {
  160. insertControls();
  161. });
  162.  
  163. observer.observe(document.body, { childList: true, subtree: true });
  164. window.addEventListener('load', insertControls);
  165. })();

QingJ © 2025

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