YouTube Quick Speed Interface

Add a quick speed interface to YouTube's middle-bottom area without interfering with existing controls.

  1. // ==UserScript==
  2. // @name YouTube Quick Speed Interface
  3. // @name:en YouTube Quick Speed Interface
  4. // @namespace https://twitter.com/CobleeH
  5. // @version 1.14
  6. // @description Add a quick speed interface to YouTube's middle-bottom area without interfering with existing controls.
  7. // @description:en Add a quick speed interface to YouTube's middle-bottom area without interfering with existing controls.
  8. // @author CobleeH
  9. // @match https://www.youtube.com/*
  10. // @grant none
  11. // @license MIT
  12. // ==/UserScript==
  13.  
  14. (function() {
  15. 'use strict';
  16.  
  17. const speeds = [0.5, 1, 1.5, 2, 3];
  18.  
  19. // 創建速度控件
  20. function createSpeedOptions() {
  21. const speedContainer = document.createElement('div');
  22. speedContainer.classList.add('ytp-speed-options');
  23. speedContainer.style.display = 'flex';
  24. speedContainer.style.alignItems = 'center';
  25. speedContainer.style.position = 'absolute';
  26. speedContainer.style.right = '15px'; // 放在中間偏右的位置
  27. speedContainer.style.bottom = '55px'; // 略高於進度條
  28. speedContainer.style.zIndex = '9999';
  29. speedContainer.style.background = 'rgba(0, 0, 0, 0.7)';
  30. speedContainer.style.borderRadius = '5px';
  31. speedContainer.style.padding = '5px 10px';
  32. speedContainer.style.color = '#fff';
  33. speedContainer.style.fontSize = '14px';
  34.  
  35. // 添加標籤
  36. const label = document.createElement('span');
  37. label.innerText = 'Speed:';
  38. label.style.marginRight = '8px';
  39. speedContainer.appendChild(label);
  40.  
  41. // 添加速度選項
  42. speeds.forEach(speed => {
  43. const option = document.createElement('div');
  44. option.innerText = speed + 'x';
  45. option.style.cursor = 'pointer';
  46. option.style.margin = '0 5px';
  47.  
  48. option.addEventListener('click', () => {
  49. const video = document.querySelector('video');
  50. if (video) {
  51. video.playbackRate = speed;
  52. highlightOption(option);
  53. }
  54. });
  55.  
  56. speedContainer.appendChild(option);
  57. });
  58.  
  59. return speedContainer;
  60. }
  61.  
  62. // 高亮選擇的速度選項
  63. function highlightOption(selectedOption) {
  64. const options = document.querySelectorAll('.ytp-speed-options div');
  65. options.forEach(option => {
  66. option.style.color = '#fff';
  67. option.style.fontWeight = 'normal';
  68. });
  69. selectedOption.style.color = '#ff0';
  70. selectedOption.style.fontWeight = 'bold';
  71. }
  72.  
  73. // 插入速度選項到播放器
  74. function insertSpeedOptions() {
  75. const chromeBottom = document.querySelector('.ytp-chrome-bottom');
  76. if (!chromeBottom || document.querySelector('.ytp-speed-options')) return;
  77.  
  78. const speedOptions = createSpeedOptions();
  79. chromeBottom.appendChild(speedOptions);
  80.  
  81. // 設置初始高亮
  82. setInitialSpeedHighlight();
  83. }
  84.  
  85. // 初始化高亮速度選項
  86. function setInitialSpeedHighlight() {
  87. const currentSpeed = document.querySelector('video')?.playbackRate || 1;
  88. const options = document.querySelectorAll('.ytp-speed-options div');
  89. options.forEach(option => {
  90. if (option.innerText === currentSpeed + 'x') {
  91. highlightOption(option);
  92. }
  93. });
  94. }
  95.  
  96. // 使用 MutationObserver 監聽播放器變動
  97. const observer = new MutationObserver(() => {
  98. insertSpeedOptions();
  99. });
  100.  
  101. observer.observe(document.body, { childList: true, subtree: true });
  102.  
  103. // 頁面加載完成後執行
  104. window.addEventListener('load', insertSpeedOptions);
  105. })();