Disable DRC Audio on YouTube (+ both tweaks)

The script disables DRC audio (aka Stable Volume) on YouTube (along with both volume normalization disabled and prevents automatic dubbing on all videos).

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Disable DRC Audio on YouTube (+ both tweaks)
// @author       LegendCraft (forked from Adri and The0x539)
// @namespace    Tampermonkey Scripts
// @match        https://www.youtube.com/*
// @grant        none
// @version      2025.11.03
// @description  The script disables DRC audio (aka Stable Volume) on YouTube (along with both volume normalization disabled and prevents automatic dubbing on all videos).
// @license      MIT
// @run-at       document-idle
// ==/UserScript==
// Disables the 'Stable volume' feature
/* jshint esversion: 11 */
 
function waitForElement(selector) {
	return new Promise((resolve, reject) => {
		let element = document.querySelector(selector);
		if (element) {
			resolve(element);
			return;
		}
 
		const observer = new MutationObserver(mutations => {
			const element = document.querySelector(selector);
			if (element) {
				observer.disconnect();
				resolve(element);
			}
		});
		observer.observe(document.body, {
			childList: true,
			subtree: true
		});
	});
}
 
async function disableDRC() {
	const menuButton = await waitForElement('.ytp-settings-button');
 
	menuButton.click();
	menuButton.click();
 
	const drcMenuItem = await waitForElement('.ytp-drc-menu-item:not([aria-disabled])');
 
	if (drcMenuItem.getAttribute('aria-checked') === 'true') {
		drcMenuItem.click();
		console.log('Disabled DRC Audio');
	} else {
		console.log('DRC Audio is already disabled');
	}
}
 
disableDRC().catch(error => console.error('Error:', error));

// Disable 'Loudness normalization' feature (first introduced back in Dec 2014)
function ytvt_hook() {
  function ytvt_patchDescriptor(targetProto, targetProp) {
    let desc = Object.getOwnPropertyDescriptor(targetProto, targetProp);
    Object.defineProperty(targetProto, targetProp, {
      enumerable: true,
      configurable: true,
      get: function() { return desc.get.call(this); },
      set: function(e) { void e; }
    });
    return desc;
  }
 
  let ytvt_desc = ytvt_patchDescriptor(HTMLMediaElement.prototype, 'volume');
  let ytvt_valSetter = HTMLElement.prototype.setAttribute;
  HTMLElement.prototype.setAttribute = function(name, val) {
    if(name === 'aria-valuenow') {
      let isVolume = this.matches('[aria-label="Volume"][role="slider"]');
      if(isVolume) {
        let newVolume = val / 100;
        let players = document.querySelectorAll('.video-stream');
        // players[1] can be the active, so just set the volume on all players
        for(let player of players) {
          if(player.volume !== newVolume) {
            ytvt_desc.set.call(player, newVolume);
          }
        }
      }
    }
    ytvt_valSetter.call(this, name, val);
  };
 
  let ytvt_rule = '.ytp-sfn-content::after, ytmusic-nerd-stats::after { content: "using No Volume Normalization" }';
  let ytvt_sheet = new CSSStyleSheet();
  ytvt_sheet.insertRule(ytvt_rule);
  document.adoptedStyleSheets = (document.adoptedStyleSheets || []).concat([ytvt_sheet]);
 
  let ytvt_script = document.getElementById('ytvoltweak');
  if(ytvt_script) { ytvt_script.remove(); }
}
 
function ytvt_inject() {
  let ytvt_textContent = ytvt_hook + '\n' + ytvt_hook.name + '();';
  GM_addElement('script', {textContent: ytvt_textContent, id: 'ytvoltweak'});
}
 
if(!document.head) {
  let ytvt_headObserver = new MutationObserver(function(_arr, obs) {
    if(document.head) { obs.disconnect(); ytvt_inject(); }
  });
  ytvt_headObserver.observe(document, {subtree: true, childList: true});
} else {
  ytvt_inject();
}

// Prevent to force the 'Auto-dubbing' feature (for all videos that have any conturies)
/*jshint esversion: 11 */
 
(function () {
    'use strict';
 
    function fallbackGetPlayer() {
        if (window.location.hostname === 'm.youtube.com') return document.querySelector('#movie_player');
        if (window.location.pathname.startsWith('/shorts')) return document.querySelector('#shorts-player');
        if (window.location.pathname.startsWith('/watch')) return document.querySelector('#movie_player');
        return document.querySelector('.inline-preview-player');
    }
 
    function main(event) {
        try {
            const getTrackId = (track) => Object.values(track ?? {}).find((p) => p?.id)?.id ?? null;
            const player = event?.target?.player_ ?? fallbackGetPlayer();
            const availableTracks = player.getAvailableAudioTracks();
            if (availableTracks?.length === 0) return; // Either no dubbing or YouTube's API failed.
            const dubAudioTrack = player.getAudioTrack();
            if (`${dubAudioTrack}` === 'Default') return; // YouTube sometimes returns a partially populated object named "Default" if already using the original language. Perhaps a bug on YouTube's side?
            const renderer = player.getPlayerResponse()?.captions?.playerCaptionsTracklistRenderer;
            const originalAudioId = renderer?.audioTracks?.[renderer?.defaultAudioTrackIndex]?.audioTrackId;
            if (!originalAudioId || getTrackId(dubAudioTrack) === originalAudioId) return; // No undo necessary so return early.
            console.log('Auto-dub detected, trying to undo...');
            const originalAudioTrack = availableTracks.find((track) => getTrackId(track) === originalAudioId);
            if (!originalAudioTrack) throw new Error('Unable to determine the original audio track.');
            player.setAudioTrack(originalAudioTrack);
            console.log(`Auto-dub undo successful. Audio track reverted from ${dubAudioTrack} to ${originalAudioTrack}.`);
        } catch (error) {
            console.error('Failed to prevent YouTube auto-dubbing.', error);
        }
    }
 
    const playerUpdateEvent = window.location.hostname === 'm.youtube.com' ? 'state-navigateend' : 'yt-player-updated';
    window.addEventListener(playerUpdateEvent, main, true);
    window.addEventListener('pageshow', main, {once: true});
})();

// Add CSS to remove the 'Stable volume' option from the right menu (legacy code included)
(function() {
let css = `.ytp-panel .ytp-menuitem.ytp-drc-menu-item { display: none !important }`;
if (typeof GM_addStyle !== "undefined") {
  GM_addStyle(css);
} else {
  let styleNode = document.createElement("style");
  styleNode.appendChild(document.createTextNode(css));
  (document.querySelector("head") || document.documentElement).appendChild(styleNode);
}
})();

(function() {
    'use strict';
    const style = document.createElement('style');
    style.type = 'text/css';
    style.innerText = '.ytp-panel .ytp-menuitem.ytp-drc-menu-item{display:none;}';
    document.head.appendChild(style);
})();