Last active
February 22, 2024 00:07
-
-
Save Nate-Wilkins/a3725d5d40dd10e22020985113bdbfb3 to your computer and use it in GitHub Desktop.
amazon_prime_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 - 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