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.

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

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         YouTube Volume Control with Memory
// @namespace    https://github.com/Nick2bad4u/UserStyles
// @version      4.0
// @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.
// @author       Nick2bad4u
// @match        *://www.youtube.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=youtube.com
// @grant        GM.getValue
// @grant        GM.setValue
// @license      UnLicense
// @tag          youtube
// ==/UserScript==

(async function () {
  'use strict';

  // Default volume if none is saved
  let previousVolume = await GM.getValue('youtubeVolume', 5);

  // Create input element for volume control
  const volumeInput = document.createElement('input');
  volumeInput.type = 'number';
  volumeInput.min = 0;
  volumeInput.max = 100;
  volumeInput.value = previousVolume;

  // Set input field styles to resemble YouTube's UI
  volumeInput.style.width = '30px';
  volumeInput.style.marginRight = '10px';
  volumeInput.style.backgroundColor = 'rgba(255, 255, 255, 0.0)';
  volumeInput.style.color = 'white';
  volumeInput.style.border = '0px solid rgba(255, 255, 255, 0.0)';
  volumeInput.style.borderRadius = '4px';
  volumeInput.style.zIndex = 9999;
  volumeInput.style.height = '24px';
  volumeInput.style.fontSize = '16px';
  volumeInput.style.padding = '0 4px';
  volumeInput.style.transition = 'border-color 0.3s, background-color 0.3s';
  volumeInput.style.outline = 'none';
  volumeInput.style.position = 'relative';
  volumeInput.style.top = '13px';

  // Change border color on focus
  volumeInput.addEventListener('focus', () => {
    volumeInput.style.borderColor = 'rgba(255, 255, 255, 0.6)';
  });

  volumeInput.addEventListener('blur', () => {
    volumeInput.style.borderColor = 'rgba(255, 255, 255, 0.3)';
  });

  // Change background color on hover
  volumeInput.addEventListener('mouseenter', () => {
    volumeInput.style.backgroundColor = 'rgba(0, 0, 0, 0.8)';
  });

  volumeInput.addEventListener('mouseleave', () => {
    volumeInput.style.backgroundColor = 'rgba(255, 255, 255, 0.0)';
  });

  // Prevent YouTube hotkeys when typing in the input
  volumeInput.addEventListener('keydown', function (event) {
    event.stopPropagation();
    console.log('Keydown event in volume input, stopping propagation.');
  });

  // Function to set the volume based on input value
  async function setVolume(volumeValue) {
    const player = document.querySelector('video');
    if (player) {
      // Validate input (must be between 0 and 100)
      if (volumeValue < 0) volumeValue = 0;
      if (volumeValue > 100) volumeValue = 100;
      volumeInput.value = volumeValue;

      // Set the player volume and save to Tampermonkey storage
      player.volume = volumeValue / 100;
      await GM.setValue('youtubeVolume', volumeValue);
      console.log(`Volume set to ${volumeValue} and saved to Tampermonkey storage.`);

      // Sync YouTube's volume slider UI
      const volumeSlider = document.querySelector('.ytp-volume-slider-handle');
      if (volumeSlider) {
        volumeSlider.style.left = `${volumeValue}%`;
        console.log('YouTube volume slider updated.');
      }
    }
  }

  // Event listener for input change (manually changing the volume in the input box)
  volumeInput.addEventListener('input', () => setVolume(volumeInput.value));

  // Function to update the input field when YouTube's player volume is changed
  async function updateVolumeInput() {
    const player = document.querySelector('video');
    if (player) {
      const currentVolume = Math.round(player.volume * 100);
      volumeInput.value = currentVolume;
      await GM.setValue('youtubeVolume', currentVolume);
      console.log(`Volume input updated to ${currentVolume} from video player.`);

      // Show 0 if the video is muted
      if (player.muted) {
        volumeInput.value = 0;
      }
    }
  }

  // Function to handle mute changes
  async function handleMuteChange() {
    const player = document.querySelector('video');
    if (player) {
      if (player.muted) {
        volumeInput.value = 0; // Show 0 when muted
      } else {
        volumeInput.value = previousVolume; // Restore previous volume when unmuted
        player.volume = previousVolume / 100; // Set the player volume back to previous
      }
      console.log(`Mute state changed: muted = ${player.muted}`);
    }
  }

  // Inject the input box into YouTube's control bar
  function injectVolumeControl() {
    const volumeSliderPanel = document.querySelector('.ytp-volume-panel');
    if (volumeSliderPanel) {
      volumeSliderPanel.parentNode.insertBefore(volumeInput, volumeSliderPanel);
      setVolume(previousVolume); // Set initial volume
      const player = document.querySelector('video');
      if (player) {
        player.addEventListener('volumechange', updateVolumeInput);
        player.addEventListener('mute', handleMuteChange);
        player.addEventListener('unmute', handleMuteChange);
        console.log('Volume input injected and event listeners attached.');
      }
    } else {
      console.log('Volume panel not found, retrying...');
      setTimeout(injectVolumeControl, 500);
    }
  }

  // Inject the volume control when the page is ready
  injectVolumeControl();
})();