YouTube Volume Control with Memory

Set YouTube volume manually on a scale of 1-100, remember last set volume, and inject the UI to the left of the volume slider on the video player. Syncs the slider, disables invalid inputs, and adds debugging.

安裝腳本?
作者推薦腳本

您可能也會喜歡 YouTubeTV Volume Control with Memory

安裝腳本
  1. // ==UserScript==
  2. // @name YouTube Volume Control with Memory
  3. // @namespace typpi.online
  4. // @version 4.1
  5. // @description Set YouTube volume manually on a scale of 1-100, remember last set volume, and inject the UI to the left of the volume slider on the video player. Syncs the slider, disables invalid inputs, and adds debugging.
  6. // @author Nick2bad4u
  7. // @match *://www.youtube.com/*
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com
  9. // @grant GM.getValue
  10. // @grant GM.setValue
  11. // @license UnLicense
  12. // @tag youtube
  13. // ==/UserScript==
  14.  
  15. (async function () {
  16. 'use strict';
  17.  
  18. // Default volume if none is saved
  19. let previousVolume = await GM.getValue('youtubeVolume', 5);
  20.  
  21. // Create input element for volume control
  22. const volumeInput = document.createElement('input');
  23. volumeInput.type = 'number';
  24. volumeInput.min = 0;
  25. volumeInput.max = 100;
  26. volumeInput.value = previousVolume;
  27.  
  28. // Set input field styles to resemble YouTube's UI
  29. volumeInput.style.width = '30px';
  30. volumeInput.style.marginRight = '10px';
  31. volumeInput.style.backgroundColor = 'rgba(255, 255, 255, 0.0)';
  32. volumeInput.style.color = 'white';
  33. volumeInput.style.border = '0px solid rgba(255, 255, 255, 0.0)';
  34. volumeInput.style.borderRadius = '4px';
  35. volumeInput.style.zIndex = 9999;
  36. volumeInput.style.height = '24px';
  37. volumeInput.style.fontSize = '16px';
  38. volumeInput.style.padding = '0 4px';
  39. volumeInput.style.transition = 'border-color 0.3s, background-color 0.3s';
  40. volumeInput.style.outline = 'none';
  41. volumeInput.style.position = 'relative';
  42. volumeInput.style.top = '13px';
  43.  
  44. // Change border color on focus
  45. volumeInput.addEventListener('focus', () => {
  46. volumeInput.style.borderColor = 'rgba(255, 255, 255, 0.6)';
  47. });
  48.  
  49. volumeInput.addEventListener('blur', () => {
  50. volumeInput.style.borderColor = 'rgba(255, 255, 255, 0.3)';
  51. });
  52.  
  53. // Change background color on hover
  54. volumeInput.addEventListener('mouseenter', () => {
  55. volumeInput.style.backgroundColor = 'rgba(0, 0, 0, 0.8)';
  56. });
  57.  
  58. volumeInput.addEventListener('mouseleave', () => {
  59. volumeInput.style.backgroundColor = 'rgba(255, 255, 255, 0.0)';
  60. });
  61.  
  62. // Prevent YouTube hotkeys when typing in the input
  63. volumeInput.addEventListener('keydown', function (event) {
  64. event.stopPropagation();
  65. console.log('Keydown event in volume input, stopping propagation.');
  66. });
  67.  
  68. // Function to set the volume based on input value
  69. async function setVolume(volumeValue) {
  70. const player = document.querySelector('video');
  71. if (player) {
  72. // Validate input (must be between 0 and 100)
  73. if (volumeValue < 0) volumeValue = 0;
  74. if (volumeValue > 100) volumeValue = 100;
  75. volumeInput.value = volumeValue;
  76.  
  77. // Set the player volume and save to Tampermonkey storage
  78. player.volume = volumeValue / 100;
  79. await GM.setValue('youtubeVolume', volumeValue);
  80. console.log(
  81. `Volume set to ${volumeValue} and saved to Tampermonkey storage.`,
  82. );
  83.  
  84. // Sync YouTube's volume slider UI
  85. const volumeSlider = document.querySelector('.ytp-volume-slider-handle');
  86. if (volumeSlider) {
  87. volumeSlider.style.left = `${volumeValue}%`;
  88. console.log('YouTube volume slider updated.');
  89. }
  90. }
  91. }
  92.  
  93. // Event listener for input change (manually changing the volume in the input box)
  94. volumeInput.addEventListener('input', () => setVolume(volumeInput.value));
  95.  
  96. // Function to update the input field when YouTube's player volume is changed
  97. async function updateVolumeInput() {
  98. const player = document.querySelector('video');
  99. if (player) {
  100. const currentVolume = Math.round(player.volume * 100);
  101. volumeInput.value = currentVolume;
  102. await GM.setValue('youtubeVolume', currentVolume);
  103. console.log(
  104. `Volume input updated to ${currentVolume} from video player.`,
  105. );
  106.  
  107. // Show 0 if the video is muted
  108. if (player.muted) {
  109. volumeInput.value = 0;
  110. }
  111. }
  112. }
  113.  
  114. // Function to handle mute changes
  115. async function handleMuteChange() {
  116. const player = document.querySelector('video');
  117. if (player) {
  118. if (player.muted) {
  119. volumeInput.value = 0; // Show 0 when muted
  120. } else {
  121. volumeInput.value = previousVolume; // Restore previous volume when unmuted
  122. player.volume = previousVolume / 100; // Set the player volume back to previous
  123. }
  124. console.log(`Mute state changed: muted = ${player.muted}`);
  125. }
  126. }
  127.  
  128. // Inject the input box into YouTube's control bar
  129. function injectVolumeControl() {
  130. const volumeSliderPanel = document.querySelector('.ytp-volume-panel');
  131. if (volumeSliderPanel) {
  132. volumeSliderPanel.parentNode.insertBefore(volumeInput, volumeSliderPanel);
  133. setVolume(previousVolume); // Set initial volume
  134. const player = document.querySelector('video');
  135. if (player) {
  136. player.addEventListener('volumechange', updateVolumeInput);
  137. player.addEventListener('mute', handleMuteChange);
  138. player.addEventListener('unmute', handleMuteChange);
  139. console.log('Volume input injected and event listeners attached.');
  140. }
  141. } else {
  142. console.log('Volume panel not found, retrying...');
  143. setTimeout(injectVolumeControl, 500);
  144. }
  145. }
  146.  
  147. // Inject the volume control when the page is ready
  148. injectVolumeControl();
  149. })();