Last active
March 14, 2024 12:19
-
-
Save KiitoX/38f8e4c1375939848a79cfa756fa32ef to your computer and use it in GitHub Desktop.
More volume steps on youtube music
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==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 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
do the same thing only for YouTube