Skip to content

Instantly share code, notes, and snippets.

@Nate-Wilkins
Last active February 22, 2024 00:07
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Nate-Wilkins/a3725d5d40dd10e22020985113bdbfb3 to your computer and use it in GitHub Desktop.
Save Nate-Wilkins/a3725d5d40dd10e22020985113bdbfb3 to your computer and use it in GitHub Desktop.
amazon_prime_muter.userscript
// ==UserScript==
// @name CodeNull - Amazone Prime Video Ad Muter
// @author nate-wilkins@code-null.com
// @version 4.0
// @namespace http://tampermonkey.net/
// @description Detects and blocks ads on Amazon Prime Video. Automatically mute ads. Turn sound on again after the ad.
// @match https://www.amazon.com/gp/video/*
// @grant none
// @run-at document-start
// @icon https://www.google.com/s2/favicons?sz=64&domain=amazon.com
// @downloadURL https://gist.githubusercontent.com/Nate-Wilkins/a3725d5d40dd10e22020985113bdbfb3/raw
// @updateURL https://gist.githubusercontent.com/Nate-Wilkins/a3725d5d40dd10e22020985113bdbfb3/raw
// ==/UserScript==
document.addEventListener('DOMContentLoaded', async () => {
const selectorVideo = 'video';
const selectorDivVideoTimeline = '.atvwebplayersdk-bottompanel-container';
const selectorDivAdvertisement = '.atvwebplayersdk-adtimeindicator-text';
const $video = () => document.querySelector(selectorVideo);
const $divVideoTimeline = () => document.querySelector(selectorDivVideoTimeline);
const $divAdvertisement = () => document.querySelector(selectorDivAdvertisement);
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) {
clearInterval(idInterval);
reject(new Error("Timed out waiting for condition."));
}
}
}, timeCheck);
});
};
// const $rInputVolumeController = getReactInstance($inputVolume().parentElement);
const getReactInstance = ($element) => {
const keys = Object.keys($element);
const keyReactInternalInstance = keys.find(key => key.startsWith("__reactInternalInstance"));
return $element[keyReactInternalInstance];
};
let observer_$divVideoTimeline = null;
const run = async () => {
// Disconnect.
if (observer_$divVideoTimeline !== null) {
observer_$divVideoTimeline.disconnect();
}
// Wait for Video.
try {
await waitFor($divVideoTimeline);
} catch (e) {
setTimeout(run, 3000);
return;
}
// Observe Video for Ads.
let previousVolume = null;
observer_$divVideoTimeline = 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_$divVideoTimeline.observe($divVideoTimeline(), {
characterData: true,
childList: true,
attributes: true,
subtree: true
});
// Do we need to re-listen for Videos?
try {
await waitFor($divVideoTimeline);
} 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