YouTube 播放速度记忆

自动切换到你预先设定的播放速度。

目前为 2024-08-15 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name Youtube Remember Speed
  3. // @name:zh-TW YouTube 播放速度記憶
  4. // @name:zh-CN YouTube 播放速度记忆
  5. // @name:ja YouTube 再生速度メモリー
  6. // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAAXNSR0IArs4c6QAAAqhJREFUaEPtmc9rE0EUxz+DjSh6UAQRxP4F9uhBRKjipef+FwqtoZdYEk3U4jGn0FJ6KrQnj6X0EKVKKIi9tAotPZSCYilFoq0/sK1Z92V329XGENiZSRZ2LtllZ9+8z/e9ncy8UcS8qZj7TwLQ7ggmEUgiEFGB/6aQAxeBq8Al4GxonDPAydD9+dB1qkFfefy9iZ9fgRrwC/jh96v6vz+Bj8B7BduNbBwDcOA6UABuAyciCqTr9d/ACxf0oYI3YaOHAA71KfWpq8QDF6BTP27H9/GRArk+ctSBZ0BGl2SG7YwoyB4COF66lDtY+X/1EPVvKXhVTxUHKsANw6rpNl9RcFM50A1sxEj9QAiJQrcA9LvT5XPd8liy1y8Ad4GSpQF1D3NPAO4DRd2WLdlL6wUYH4dKBSYnLfmPZoDZWejrg/l5GByE5WXTIIYAxO1aDaamYGgIthsuY3TAGQQI3KtWoVCAUgkODnQ4HbZhASAYbnUV0mmYm9MJYREgcHtmxvs+1td1gLQBQNze24OxMchmYXc3CkibAOQDl6k2k4GtrZgBLC56KbSwEMXx4F2LEdjchHweJia8KVZPswCwvw+jo5DLwc6OHrePrBgGKJdhYABWVnQ7bjiF1ta8OV+WFmab5ghMT8PSEhSL3lRpvmkGSKVAct5eqwPEfkMT+y3lZeBDbDf1kq6xLqv4AL3AyxhFQUoqvQpeh2ujI+46cdjeBBJppL9Li34UBCYP5Do4ErKIeiLV82PF3UAPB64Bj4E7biW4K5JO+l6WvajUbqW8/jZsttkBxwWgB7gCnPZfCg4z5P6UH6lzTfyUgxGp7ctBRdBkBxNsjiWXv4Seyd93+DDkG/AJeKfgc6NxOvUcoOXYJQAtS2WoYxIBQ8K2bDaJQMtSGer4B8aT1sve/dr7AAAAAElFTkSuQmCC
  7. // @author ElectroKnight22
  8. // @namespace electroknight22_youtube_remember_playback_rate_namespace
  9. // @version 1.0.1
  10. // @match *://www.youtube.com/*
  11. // @exclude *://www.youtube.com/live_chat*
  12. // @grant GM.getValue
  13. // @grant GM.setValue
  14. // @grant GM.deleteValue
  15. // @grant GM.listValues
  16. // @grant GM_registerMenuCommand
  17. // @grant GM_unregisterMenuCommand
  18. // @license MIT
  19. // @description Automcatically switches to your pre-selected speed.
  20. // @description:zh-TW 自動切換到你預先設定的播放速度。
  21. // @description:zh-CN 自动切换到你预先设定的播放速度。
  22. // @description:ja 自動的に設定した再生速度に替わります。
  23. // ==/UserScript==
  24.  
  25. /*jshint esversion: 11 */
  26.  
  27. (function() {
  28. "use strict";
  29.  
  30. const DEBUG = false;
  31.  
  32. const DEFAULT_SETTINGS = {
  33. targetSpeed: 1
  34. };
  35.  
  36. const speeds = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2];
  37.  
  38. let userSettings = { ...DEFAULT_SETTINGS };
  39. let menuCommandIds = [];
  40.  
  41. let doc = document, win = window;
  42.  
  43. // --------------------
  44. // --- FUNCTIONS ------
  45. // --------------------
  46.  
  47. function debugLog(message, shouldShow = true) {
  48. if (DEBUG && shouldShow) {
  49. console.log("YTRS DEBUG | " + message);
  50. }
  51. }
  52.  
  53. // --------------------
  54. // Attempt to set the video resolution to target quality or the next best quality
  55. function setSpeed(targetSpeed) {
  56.  
  57. let ytPlayer = doc.getElementById("movie_player") || doc.getElementsByClassName("html5-video-player")[0];
  58.  
  59. if (!isValidVideo(ytPlayer)) return;
  60.  
  61. ytPlayer.setPlaybackRate(targetSpeed);
  62. debugLog("Trying to set speed to: " + targetSpeed + "x");
  63.  
  64. }
  65.  
  66. function isValidVideo(ytPlayer, force) {
  67.  
  68. if (!ytPlayer?.getAvailableQualityLabels()[0]) {
  69. debugLog("Video data missing");
  70. return false;
  71. }
  72.  
  73. if (win.location.href.startsWith("https://www.youtube.com/shorts/")) {
  74. debugLog("Skipping Youtube Shorts");
  75. return false;
  76. }
  77.  
  78. return true;
  79. }
  80.  
  81. // --------------------
  82. // Functions for the speed selection menu
  83.  
  84. function createSpeedMenu() {
  85. GM_registerMenuCommand("Set Speed (show/hide)", () => {
  86. menuCommandIds.length ? removeSpeedMenuItems() : showSpeedMenuItems();
  87. }, {
  88. autoClose: false
  89. });
  90. }
  91.  
  92. function showSpeedMenuItems() {
  93. removeSpeedMenuItems();
  94. speeds.forEach((speed) => {
  95. let speedText = speed + "x";
  96. if (speed === userSettings.targetSpeed) {
  97. speedText += " (selected)";
  98. }
  99. let menuCommandId = GM_registerMenuCommand(speedText, () => {
  100. setSelectedSpeed(speed);
  101. }, {
  102. autoClose: false,
  103. });
  104. menuCommandIds.push(menuCommandId);
  105. });
  106. }
  107.  
  108. function removeSpeedMenuItems() {
  109. while (menuCommandIds.length) {
  110. GM_unregisterMenuCommand(menuCommandIds.pop());
  111. }
  112. }
  113.  
  114. function setSelectedSpeed(speed) {
  115. if (userSettings.targetSpeed == speed) return;
  116. userSettings.targetSpeed = speed;
  117. GM.setValue('targetSpeed', speed);
  118. removeSpeedMenuItems();
  119. showSpeedMenuItems();
  120. setSpeed(speed);
  121. }
  122.  
  123. // --------------------
  124. // Sync settings with locally stored values
  125. async function applySettings() {
  126. try {
  127. // Get all keys from GM
  128. const storedValues = await GM.listValues();
  129.  
  130. // Write any missing key-value pairs from DEFAULT_SETTINGS to GM
  131. await Promise.all(Object.entries(DEFAULT_SETTINGS).map(async ([key, value]) => {
  132. if (!storedValues.includes(key)) {
  133. await GM.setValue(key, value);
  134. }
  135. }));
  136.  
  137. // Delete any extra keys in GM that are not in DEFAULT_SETTINGS
  138. await Promise.all(storedValues.map(async key => {
  139. if (!(key in DEFAULT_SETTINGS)) {
  140. await GM.deleteValue(key);
  141. }
  142. }));
  143.  
  144. // Retrieve and update user settings from GM
  145. await Promise.all(
  146. storedValues.map(key => GM.getValue(key).then(value => [key, value]))
  147. ).then(keyValuePairs => keyValuePairs.forEach(([newKey, newValue]) => {
  148. userSettings[newKey] = newValue;
  149. }));
  150.  
  151. debugLog(Object.entries(userSettings).map(([key, value]) => key + ": " + value).join(", "));
  152. } catch (error) {
  153. debugLog("Error when applying settings: " + error.message);
  154. }
  155. }
  156.  
  157. // --------------------
  158. // Main function
  159. function main() {
  160. if (win.self == win.top) { createSpeedMenu(); }
  161. setSpeed(userSettings.targetSpeed);
  162. win.addEventListener("loadstart", () => { setSpeed(userSettings.targetSpeed); }, true);
  163. }
  164.  
  165. // --------------------
  166. // Entry Point
  167. applySettings().then(main);
  168. })();