Last active
October 15, 2023 03:12
-
-
Save Nate-Wilkins/fddab6116a31de5237e3eb215e37d3d1 to your computer and use it in GitHub Desktop.
youtube_ad_muter.userscript
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 CodeNull - YouTube Videos Ad Muter | |
// @author nate-wilkins@code-null.com | |
// @version 4.0 | |
// @namespace http://tampermonkey.net/ | |
// @description Detects and blocks ads on YouTube Videos. Automatically mute ads. Turn sound on again after the ad. | |
// @match https://www.youtube.com/* | |
// @grant none | |
// @run-at document-start | |
// @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com | |
// @downloadURL https://gist.githubusercontent.com/Nate-Wilkins/fddab6116a31de5237e3eb215e37d3d1/raw | |
// @updateURL https://gist.githubusercontent.com/Nate-Wilkins/fddab6116a31de5237e3eb215e37d3d1/raw | |
// ==/UserScript== | |
document.addEventListener('DOMContentLoaded', async () => { | |
const selectorVideo = 'video'; | |
const selectorDivContainerVideo = '.html5-video-player'; | |
const selectorDivAdvertisement = '.ad-showing'; | |
const selectorButtonSkip = '.ytp-ad-skip-button'; | |
const $video = () => document.querySelector(selectorVideo); | |
const $divContainerVideo = () => document.querySelector(selectorDivContainerVideo); | |
const $divAdvertisement = () => document.querySelector(selectorDivAdvertisement); | |
const $buttonSkip = () => document.querySelector(selectorButtonSkip); | |
const waitFor = (callbackCondition, timeCheck = 100, timeOut = 1000) => { | |
let timePassed = 0; | |
let idInterval = null; | |
return new Promise((resolve, reject) => { | |
idInterval = setInterval(() => { | |
timePassed += timeCheck; | |
if (callbackCondition()) { | |
clearInterval(idInterval); | |
resolve(); | |
} else { | |
if (timePassed >= timeOut) { | |
reject(new Error("Timed out waiting for condition.")); | |
} | |
} | |
}, timeCheck); | |
}); | |
}; | |
let observer_$divContainerVideo_skipper = null; | |
let observer_$divContainerVideo_muter = null; | |
const run = async () => { | |
// Disconnect. | |
if (observer_$divContainerVideo_skipper !== null) { | |
observer_$divContainerVideo_skipper.disconnect(); | |
} | |
if (observer_$divContainerVideo_muter !== null) { | |
observer_$divContainerVideo_muter.disconnect(); | |
} | |
// Wait for Video. | |
try { | |
await waitFor($divContainerVideo); | |
} catch (e) { | |
setTimeout(run, 3000); | |
return; | |
} | |
// Observe Video for Ads muting. | |
let previousVolume = null; | |
observer_$divContainerVideo_muter = new MutationObserver(async () => { | |
if ($divAdvertisement() && $video() && previousVolume === null) { | |
try { | |
previousVolume = $video().volume; | |
// Mute. | |
$video().volume = 0; | |
await waitFor(() => !$divAdvertisement(), 500, 10 * 60 * 1000); | |
} catch (e) { | |
console.log("Timedout waiting for Ad to finish."); | |
} finally { | |
// UnMute. | |
$video().volume = previousVolume; | |
// Cleanup. | |
previousVolume = null; | |
} | |
} | |
}); | |
observer_$divContainerVideo_muter.observe($divContainerVideo(), { | |
characterData: true, | |
childList: true, | |
attributes: true, | |
subtree: true | |
}); | |
// Observe Video for Ads skipping. | |
observer_$divContainerVideo_skipper = new MutationObserver(async () => { | |
if ($buttonSkip()) { | |
// Skip Ad. | |
$buttonSkip().click(); | |
} | |
}); | |
observer_$divContainerVideo_skipper.observe($divContainerVideo(), { | |
characterData: true, | |
childList: true, | |
attributes: true, | |
subtree: true | |
}); | |
// Do we need to re-listen for Videos? | |
try { | |
await waitFor($divContainerVideo); | |
} catch (e) { | |
setTimeout(run, 3000); | |
return; | |
} | |
}; | |
await run(); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment