Skip to content

Instantly share code, notes, and snippets.

@KiitoX
Last active March 14, 2024 12:19
Show Gist options
  • Save KiitoX/38f8e4c1375939848a79cfa756fa32ef to your computer and use it in GitHub Desktop.
Save KiitoX/38f8e4c1375939848a79cfa756fa32ef to your computer and use it in GitHub Desktop.
More volume steps on youtube music
// ==UserScript==
// @name More volume steps on youtube music
// @version 1.0.5
// @author Emma
// @license WTFPL; do with this whatever you want
// @namespace https://github.com/KiitoX
// @updateURL https://gist.githubusercontent.com/KiitoX/38f8e4c1375939848a79cfa756fa32ef/raw/youtubemusicvolume.js
// @match https://music.youtube.com/*
// @grant none
// @run-at document-end
// ==/UserScript==
// [0 - 1] is the maximal volume range, if you want to limit it lower or give yourself more steps
// for compatibility and stuff the slider value is scaled up by 100, so range from 0 to 100
const vol_min = 0, vol_max = 25;
const vol_step = 0.02;
// this is the slider width
const vol_width = 150;
function throttle(timeout, func) {
let previous = 0;
let after;
let run = function() {
let now = Date.now();
if (after) {
window.clearTimeout(after);
}
if (now - previous >= timeout) {
func();
previous = now;
after = null;
} else {
after = window.setTimeout(run, timeout - (now - previous));
}
};
return run;
}
(function () {
'use strict';
let global = {video: null, custom: null};
let insertTimeout = null;
let doInsert = function() {
let slider = document.getElementById('volume-slider');
let expandSlider = document.getElementById('expand-volume-slider');
let video = document.querySelector('video');
if (!video) {
return;
}
console.log('Adding custom volume slider');
let custom = document.createElement('input');
custom.id = 'custom-volume';
custom.type = 'range';
custom.min = vol_min;
custom.max = vol_max;
custom.step = vol_step;
custom.style.width = vol_width + 'px';
// initialize the value onto our same volume range
let match = document.cookie.match(/customvolume=(.+?)(;|$)/);
if (match) {
custom.value = Number(match[1]);
} else {
custom.value = (vol_max - vol_min) * 0.1;
}
slider.volume = (custom.value === 0) ? 0 : 5;
video.volume = custom.value / 100;
let changeVolume = throttle(50, function () {
global.video.volume = global.custom.value / 100;
document.cookie = `customvolume=${global.custom.value};max-age=8640000;sameSite=strict`;
});
// sadly I could not manage to eliminate the audio popping whenever the volume is changed,
// could use some help with that, doubt that there is a good solution though
custom.addEventListener('input', function (evt) {
// since the original slider limits step size and minimum volume,
// we bypass it completely and set the volume manually on the video
changeVolume();
// the slider practically does not store any fidelity at all,
// so we just use it for mute button toggle display stuff
slider.volume = (custom.value === 0) ? 0 : 5;
});
custom.addEventListener('click', function (evt) {
// do not toggle the playlist drawer when clicking on the slider
evt.stopPropagation();
});
slider.parentNode.insertBefore(custom, slider);
global.custom = custom;
connect();
// hiding the original volume bar, since it is not compatible
slider.style.display = 'none';
expandSlider.style.display = 'none';
};
let connect = throttle(100, function() {
if (!global.video || !global.video.isConnected) {
global.video = document.querySelector('video');
if (!global.video) {
return;
}
console.log('connecting');
}
if (!global.video.hasAttribute('listening')) {
global.video.setAttribute('listening', '');
global.video.addEventListener('volumechange', function (evt) {
// youtube decides to reset the volume every time the audio/video
// source changes, so we just have to reset it every time
let difference = Math.abs(global.video.volume * 100 - global.custom.value);
if (difference >= 0.1) {
console.log(`diff ${difference};\nfrom ${global.video.volume * 100} to ${global.custom.value}`);
global.video.volume = global.custom.value / 100;
global.custom.value = global.video.volume * 100;
}
});
global.video.addEventListener('emptied', function (evt) {
// this in triggered on forward/backward, where the volume is also reset
global.video.volume = global.custom.value / 100;
});
/*
global.video.addEventListener('durationchange', function (evt) {
// scroll current song into view
document.querySelector("ytmusic-player-queue-item[selected]").scrollIntoView({behavior: "smooth", block: "center", inline: "nearest"})
});*/
}
});
// youtube is a highly dynamic website, stuff gets loaded in
// after the fact all the time, so everytime something changes
// we want to see if the thing we're looking for is loaded in
let observer = new MutationObserver(function (mutations) {
/*mutations.forEach(function (mut) {
console.log(mut);
});*/
let bar = document.querySelector('ytmusic-player-bar');
if (bar) {
let custom = document.getElementById('custom-volume');
if (!custom) {
doInsert();
} else {
connect();
}
// once we added our slider we can stop monitoring the website changes
// since it seems like the playback component is staying the same on page navigation
// should that ever change, we would want to change this
// EDIT: this seems to not be the case ...
//observer.disconnect();
}
});
observer.observe(document.body, {subtree: true, childList: true, characterData: true});
})();
@ToLIManl
Copy link

do the same thing only for YouTube

@KiitoX
Copy link
Author

KiitoX commented Mar 14, 2024

@ToLIManl sorry I'm no longer maintaining this. For me, this extension: https://mybrowseraddon.com/sound-adjustment.html has been a better, more reliable solution :>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment