Skip to content

Instantly share code, notes, and snippets.

@Nick2bad4u
Created October 30, 2024 03:28
Show Gist options
  • Save Nick2bad4u/ca5cbc515cb665ca19e7916e72b7d00e to your computer and use it in GitHub Desktop.
Save Nick2bad4u/ca5cbc515cb665ca19e7916e72b7d00e to your computer and use it in GitHub Desktop.
// ==UserScript==
// @name YouTube Volume Control with Memory
// @namespace https://github.com/Nick2bad4u/UserStyles
// @version 3.2
// @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
// @updateURL https://github.com/Nick2bad4u/UserStyles/raw/refs/heads/main/YouTubeVolumeControl.user.js
// @downloadURL https://github.com/Nick2bad4u/UserStyles/raw/refs/heads/main/YouTubeVolumeControl.user.js
// ==/UserScript==
(async function () {
'use strict';
// Default volume if none is saved
let previousVolume = await GM.getValue('youtubeVolume', 50);
// 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();
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment