Skip to content

Instantly share code, notes, and snippets.

@abec2304
Last active July 12, 2024 13:27
Show Gist options
  • Save abec2304/2782f4fc47f9d010dfaab00f25e69c8a to your computer and use it in GitHub Desktop.
Save abec2304/2782f4fc47f9d010dfaab00f25e69c8a to your computer and use it in GitHub Desktop.
// ==UserScript==
// @name No YouTube Volume Normalization
// @namespace https://gist.github.com/abec2304
// @match https://www.youtube.com/*
// @grant none
// @version 2.1
// @author abec2304
// @description Enjoy YouTube videos at their true volume
// @inject-into content
// @run-at document-start
// @allFrames true
// ==/UserScript==
(function(recurse) {
// try in case script ran later than document-start
if(!recurse())
return;
// fallback to waiting for video element to be added
const videoObserver = new MutationObserver(function(records) {
records.forEach(function(mutation) {
Array.prototype.forEach.call(mutation.addedNodes, function(node) {
if("VIDEO" === node.tagName) {
videoObserver.disconnect();
recurse();
}
});
});
})
// begin waiting
videoObserver.observe(document.documentElement, {
subtree: true,
childList: true
});
})(function() {
// get volume bar element
const volumeElement = document.querySelector(".ytp-volume-panel");
// get video element
const videoElement = document.querySelector(".html5-main-video");
// if elements weren't found, don't continue
if(!volumeElement || !videoElement)
return 1;
// get volume set function and bind to video element
const setVolume = Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, "volume").set.bind(videoElement);
// define convenience function
function ytSetVolume(widget) {
const newVolume = widget.getAttribute("aria-valuenow") / 100;
setVolume(newVolume);
}
// determine index of video element for non-sandboxed world
const globalIndex = [].slice.call(document.getElementsByTagName("video")).indexOf(videoElement);
// define function for shadowing the volume field
const volumeShadow = function(targetIndex) {
const targetVideo = document.getElementsByTagName("video")[targetIndex];
Object.defineProperty(targetVideo, "volume", {
value: 42
});
}
// inject shadowing script (this breaks the volume bar; fixed below)
const contextScript = document.createElement("script");
contextScript.id = "ytvolfix2";
contextScript.textContent = "(" + volumeShadow+ ")(" + globalIndex + ")";
contextScript.onload = function() {
// remove script after execution
this.parentElement.removeChild(this);
};
(document.head || document.documentElement).appendChild(contextScript);
// define observer for volume bar change
const volumeObserver = new MutationObserver(function(records) {
ytSetVolume(records[0].target);
});
// register observer to make volume bar function
volumeObserver.observe(volumeElement, {
attributes: true,
attributeFilter: ["aria-valuenow"]
});
// set volume to current value of bar
ytSetVolume(volumeElement);
});
@antoniu200
Copy link

antoniu200 commented Nov 17, 2023

Ahoy, guys!
DRC is back! It is present on YouTube for Android as "Stable volume" and it is optional.
I am curious whether or not this is present on PC browsers and whether it is optional there as well.
Screenshot_20231117-162753_YouTube~2

EDIT 2: It seems like DRC is not present at all on music, as far as I can tell (for now):
Screenshot_20231117-171429_YouTube~2
Screenshot_20231117-171445_YouTube~2

EDIT 3: I don't see any option on PC to enable it. YouTube links also does not identify more than 2 audio streams (AAC and Opus).

@epicfacethe3rd
Copy link

this script isn't working for me, any advice on how to fix this?
thanks

@Comitant
Copy link

Comitant commented Mar 1, 2024

Is there a script out there that removes DRC? This script is no longer effective for removing normalization, at least for me since everything is DRC normalized.

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