Twitch - Mute and hide ads, while keeping the audio of the stream on

Automatically mutes and hides the Twitch player when an advertisement started and revert it back to normal once finished. The stream can still be heard during ads. You can also display ads via the "Ads Options" button.

< Opinie na Twitch - Mute and hide ads, while keeping the audio of the stream on

Ocena: OK - skrypt działa, ale ma błędy

§
Napisano: 17-10-2025
Edytowano: 17-10-2025

the buttons are missing from the container. you've identified the wrong selector once again!!!!!.
here is the correct selector.!! I'm showing you a screenshot from an old version that I edited myself.





```javascript



(function() {

var _tmuteVars = { "timerCheck": 500, // EDITABLE - Checking rate of ad in progress (in milliseconds; recommended value: 250 - 1000; default: 500)
"adInProgress": false, // Track if an ad is in progress or not (directly linked to player mute state)
"adsDisplayed": 0, // Number of ads displayed
"disableDisplay": true, // EDITABLE - Disable the player display during an ad (true = yes, false = no (default))
"anticipatePreroll": false, // EDITABLE - Temporarily mute and/or hide the player when loading a new stream to anticipate a pre-roll ad (true = yes, false = no (default))
"anticipateTimer": 2000, // EDITABLE - Time where the player is muted and/or hidden when loading a new stream to anticipate a pre-roll ad (in milliseconds; default: 2000)
"anticipateInProgress": false, // Used to check if we're currently anticipating a pre-roll ad
"anticipatePrematureEnd": false, // Used to check if we prematurely ended a pre-roll ad anticipation
"alreadyMuted": false, // Used to check if the player is muted at the start of an ad
"adElapsedTime": undefined, // Used to check if Twitch forgot to remove the ad notice
"adUnlockAt": 270, // EDITABLE - Unlock the player if this amount of seconds elapsed during an ad (in seconds; default: 270)
"adMinTime": 2, // EDITABLE - Minimum amount of seconds the player will be muted/hidden since an ad started (in seconds; default: 2)
"playerIdAds": 0, // Player ID where ads may be displayed (default 0, varying on squads page)
"displayingOptions": false, // Either ads options extended menu is currently displayed or not
"highwindPlayer": undefined, // If you've the Highwind Player or not
"currentPage": undefined, // Current page to know if we need to reset ad detection on init, or add the ads options back
"currentChannel": undefined, // Current channel to avoid pre-roll ad anticipation to trigger if we visit channel pages
"optionsInitialized": false, // Used to know if the ads options have been initialized on the current page
"optionsInitializing": false, // Used to track the ads options initialization
"volumePremute": undefined, // Main player volume, used to set the volume of the stream top right during an ad
"restorePiP": false // Used to avoid displaying an ad if a stream is in Picture in Picture mode (require "disableDisplay" to true)
};
const VOLUME_KEY = 'twitch__Player____7BTVFZ___Volume';

function getStoredVolume(callback) {
if (!chrome || !chrome.storage) {
console.warn('chrome.storage не доступно. Используем значение по умолчанию 0.5.');
callback(0.5);
return;
}
chrome.storage.sync.get([VOLUME_KEY], (result) => {
const volume = result[VOLUME_KEY] !== undefined ? parseFloat(result[VOLUME_KEY]) : 0.5;
callback(volume);
});
}


// Selectors for the current player (hw: highwind player, only one existing currently)
var _tmuteSelectors = { "hw": { "player": "video-player__container", // Player class
"playerVideo": ".video-player__container video", // Player video selector
"playerDuringAd": "pbyp-player-instance", // Top-right player class, existing sometimes during an ad
"playerHidingDuringAd": "picture-by-picture-player--collapsed", // Class hiding the top-right player (during an ad)
"muteButton": "button[data-a-target='player-mute-unmute-button']", // (un)mute button selector
"volumeSlider": "input[data-a-target='player-volume-slider']", // Volume slider selector
"adNotice": undefined, // Ad notice class
"adNoticeFinder": "[data-a-target='ax-overlay']", // Ad notice selector to find the class
"viewersCount": "metadata-layout__support" // Viewers count wrapper class
}
};
// Current selector (automatically set below)
var currentSelector = undefined;

// Check if there's an ad (main loop)
function checkAd()
{
// Check if you're watching a stream, useless to continue if not
if (_tmuteVars.highwindPlayer === undefined) {
var isHwPlayer = document.getElementsByClassName(_tmuteSelectors.hw.player).length;
var isViewing = Boolean(isHwPlayer);
if (isViewing === false) return;

// We set the type of player currently used
_tmuteVars.highwindPlayer = Boolean(isHwPlayer);
currentSelector = (_tmuteVars.highwindPlayer === true) ? _tmuteSelectors.hw : null;
console.log("You're currently using the " + ((_tmuteVars.highwindPlayer === true) ? "Highwind" : "new unknown") + " player.");
if (currentSelector === null) {
clearInterval(_tmuteVars.autoCheck);
console.log("Script stopped. Failed to find the player, Twitch changed something. Feel free to contact the author of the script.");
}
} else {
var isViewing = Boolean(document.getElementsByClassName(currentSelector.player).length);
if (isViewing === false) return;
}

// Initialize the ads options if necessary.
if (_tmuteVars.optionsInitialized === false || window.location.pathname != _tmuteVars.currentPage) {
initAdsOptions();
if (currentSelector.adNotice === undefined) return;
}

var advert = document.getElementsByClassName(currentSelector.adNotice)[_tmuteVars.playerIdAds];

if (_tmuteVars.adElapsedTime !== undefined)
{
_tmuteVars.adElapsedTime += _tmuteVars.timerCheck / 1000;
if (_tmuteVars.adElapsedTime >= _tmuteVars.adUnlockAt && advert.childNodes[1] !== undefined)
{
for (var i = 0; i < advert.childElementCount; i++)
{
if (!advert.childNodes[i].classList.contains(currentSelector.adNotice)) advert.removeChild(advert.childNodes[i]);
}
console.log("Unlocking Twitch player as Twitch forgot to remove the ad notice after the ad(s).");
}
}

if ((advert.childElementCount > 2 && _tmuteVars.adInProgress === false) || (_tmuteVars.adInProgress === true && advert.childElementCount <= 2))
{
// Update at the start of an ad if the player is already muted or not
if (advert.childElementCount > 2) {
if (_tmuteVars.anticipateInProgress !== false) {
clearTimeout(_tmuteVars.anticipateInProgress);
_tmuteVars.anticipateInProgress = false;
_tmuteVars.anticipatePrematureEnd = true;
console.log("Pre-roll ad anticipation ended prematurely, ad detected.");
} else {
isAlreadyMuted();
}
}

// Keep the player muted/hidden for the minimum ad time set (Twitch started to remove the ad notice before the end of some ads)
if (advert.childElementCount <= 2 && _tmuteVars.adElapsedTime !== undefined && _tmuteVars.adElapsedTime < _tmuteVars.adMinTime) return;

mutePlayer();
}
}

// Main function to (un)mute and (un)hide the player called by checkAd()
function mutePlayer()
{
if (document.querySelector(currentSelector.muteButton) !== null)
{
if (_tmuteVars.anticipatePrematureEnd === true) { // If we ended a pre-roll ad anticipation early, we prevent an invert of the player mute state
_tmuteVars.anticipatePrematureEnd = false;
_tmuteVars.adInProgress = !(_tmuteVars.adInProgress);
} else {
actionMuteClick();
}

if (_tmuteVars.adInProgress === true)
{
_tmuteVars.adsDisplayed++;
_tmuteVars.adElapsedTime = 1;
const mainVideo = document.querySelectorAll(currentSelector.playerVideo)[_tmuteVars.playerIdAds];
if (mainVideo) {
mainVideo.muted = true;
mainVideo.volume = 0;
console.log("Основной плеер мьютнут при старте рекламы.");
}
console.log("Ad #" + _tmuteVars.adsDisplayed + " detected. Player " + (_tmuteVars.alreadyMuted === true ? "already " : "") + "muted.");
actionHidePlayer();
unmuteAdPlayer();
} else {
console.log("Ad #" + _tmuteVars.adsDisplayed + " finished (lasted " + _tmuteVars.adElapsedTime + "s)." + (_tmuteVars.alreadyMuted === true ? "" : " Player unmuted."));
_tmuteVars.adElapsedTime = undefined;
actionHidePlayer(false);

// Mute the stream shown top right during the ad to prevent double audio
var playerDuringAd = document.getElementsByClassName(currentSelector.playerDuringAd)[0];
if (playerDuringAd !== undefined) {
playerDuringAd.childNodes[0].muted = true;
}
}
} else {
console.log("No volume button found (class changed ?).");
}
}

// Unmute (and unhide) the stream showing top right during an ad if the player was initially unmuted
function unmuteAdPlayer(firstCall = true) {
var playerDuringAd = document.getElementsByClassName(currentSelector.playerDuringAd)[0];
if (playerDuringAd !== undefined) {
const pipVideo = playerDuringAd.childNodes[0]; // Это

§
Napisano: 17-10-2025

case "init":
{
initUpdate();

if (document.getElementsByClassName(currentSelector.viewersCount)[0] === undefined) break;

// Append ads options and events related
var optionsTemplate = document.createElement("div");
optionsTemplate.id = "_tmads_options-wrapper";
const buttonStyle = document.createElement('style');
buttonStyle.textContent = `
._tmads_button {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0 2px 0 2px;
margin-left: 2px;
height: 30px;
width: unset;
border-radius: var(--border-radius-medium);
}
._tmads_button:hover {
}`;
document.querySelector('head').appendChild(buttonStyle);

optionsTemplate.innerHTML = `

Unlock player
` + (_tmuteVars.disableDisplay === true ? "Show" : "Hide") + ` player during ads

Ads Options`;

// Normal player page
if (document.getElementsByClassName(currentSelector.viewersCount)[0] !== undefined)
{
_tmuteVars.playerIdAds = 0;
try {
document.getElementsByClassName(currentSelector.viewersCount)[0].parentNode.childNodes[1].childNodes[1].childNodes[0].childNodes[0].childNodes[1].appendChild(optionsTemplate); // Standard bottom
} catch(e) {
try {
document.getElementsByClassName(currentSelector.viewersCount)[0].childNodes[2].childNodes[0].appendChild(optionsTemplate); // Standard top (short variance, abandoned potentially?)
} catch(e) {
optionsTemplate.style = "padding-top: 5px;";
document.getElementsByClassName(currentSelector.viewersCount)[0].parentNode.childNodes[1].appendChild(optionsTemplate); // Last chance attachment, should always work
}
}
}

document.getElementById("_tmads_showoptions").addEventListener("click", adsOptions, false);
document.getElementById("_tmads_display").addEventListener("click", function() { adsOptions("display"); }, false);
document.getElementById("_tmads_unlock").addEventListener("click", function() { adsOptions("unlock"); }, false);
_tmuteVars.optionsInitialized = true;
console.log("Ads options initialized.");
} // before break AGAIIIN FUCKING AGAIIIIN FUCKING BREAK //
break;

HarestAutor
§
Napisano: 17-10-2025
Edytowano: 17-10-2025

Ads Options menu is still there all good since the latest changes Twitch did. If you're in an A/B testing i don't have, i can't do much. A proper rundown would be a copy/paste or screenshot of the console log, and an identifiable path to hook the buttons to append.

Also it seems, according to your screenshot, that you're using an outdated version of the script, so please state the version too while you're at it.

Odpowiedz

Zaloguj się, by odpowiedzieć.