// ==UserScript== // @name Youtube Anti-anti-adblock // @namespace http://github.com/planetarian/TamperMonkey-Scripts // @version 0.12 // @description Replaces the youtube video player with a youtube embed iframe to subvert the anti-adblock measures. // @author cracked.io/@Sc00bii // @match https://www.youtube.com/* // @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com // @updateURL https://github.com/planetarian/TamperMonkey-Scripts/raw/main/YoutubeAntiAntiAdblock.user.js // @downloadURL https://github.com/planetarian/TamperMonkey-Scripts/raw/main/YoutubeAntiAntiAdblock.user.js // @grant GM_addStyle // ==/UserScript== /* YT player locations: body > #player > #player-wrap > #player-api > ytd-app > #content > #page-manager > ytd-watch-flexy > #full-bleed-container > #columns > #primary > #primary-inner > #player > #player-container-outer > #player-container-inner > #container > #player-container > #ytd-player > #container > #movie_player > .html5-video-container > video //before > #movie_player > #aab-embed //after */ (function() { 'use strict'; function log(message) { console.log("AAB: " + message); } function callPlayer(func, args) { if (!embed) return; embed.contentWindow.postMessage(JSON.stringify({ 'event': 'command', 'func': func, 'args': args || [] }), '*'); } // get the video ID of the video currently being watched function youtube_parser(url){ var regExp = /^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#&?]*).*/; var match = url.match(regExp); return (match&&match[7].length==11)? match[7] : false; } // replace the youtube player with the embed function replacePlayer(videoId, timestamp) { // movie_player contains all the player controls, get rid of them and replace with the embed const player = document.getElementById('movie_player'); if (!player && !embed) { // We're still loading the initial page; do nothing log("page loading; skipping."); return false; } log("checking for video player."); const videoEls = player.getElementsByClassName('html5-main-video'); if (videoEls.length > 1) { log("there seem to be multiple video players?"); } // if we have an html5 player (no anti-adblock) if (videoEls.length > 0 && !!videoEls[0].src) { log("html5 player present. using that instead."); if (embed) { log("removing embed."); embed.remove(); embed = undefined; } } // if there's no html5 player (anti-adblock present) else { log("no html5 player present. adding embed."); // remove the notice const errorOverlay = document.getElementById('error-screen'); if (errorOverlay) { errorOverlay.remove(); log("error overlay removed."); } var src = `https://www.youtube.com/embed/${videoId}?enablejsapi=1`; if (!!timestamp && timestamp.length) { let time = timestamp[0] time = time.replace(/s$/, ''); src += `&start=${time}` } if (embed) { embed.src = src; player.innerHtml = ''; player.appendChild(embed); } else { // replace the player player.innerHTML = ``; embed = document.getElementById('aab-embed'); } callPlayer('playVideo'); log("YouTube player replaced with embed iframe."); return true; } } // replace player again after the user navigates to a new video document.addEventListener("yt-navigate-finish", function(event) { stage = 0; // clear the embed if we've added it already const embed = document.getElementById('aab-embed'); if (embed) embed.remove(); // replace the video on the new page const videoId = youtube_parser(document.location.href); if (!videoId) return; const url = new URL(document.location.href); replacePlayer(videoId, url.searchParams.getAll('t')); }); // unhide the player container stuff in case anti-adblock hid it GM_addStyle('ytd-watch-flexy[player-unavailable] #player-container-outer.ytd-watch-flexy { visibility: visible !important; }'); // hold onto the embed for later reference var embed = null; const descObserver = new MutationObserver(textContainersMutated); const commentsObserver = new MutationObserver(commentsContainerMutated); const textObserver = new MutationObserver(textContainersMutated); // set up the mutation observer to monitor for when the player is added const pageManager = document.getElementById('page-manager'); const pageObserver = new MutationObserver(pageMutated); try { log("observing page manager."); pageObserver.observe(pageManager, { childList: true }); } catch (error) { log("couldn't observe page manager."); } var stage = 0; const observingTag = 'aab-observing'; function pageMutated(mutationList, observer) { if (stage >= 2) return; for (const mutation of mutationList) { if (mutation.type === "childList") { log(`anti-anti-adblock reacting to element mutation. Stage ${stage}`); if (stage === 0) { // looking for the main player container const playerContainer = document.getElementById('player'); if (!playerContainer) { log("no player container found."); continue; } addTextObservers(); // the actual player itself hasn't been added yet // so we need to monitor the element it'll be added to const ytdPlayer = document.getElementById('ytd-player'); const ytdContainer = ytdPlayer.getElementsByClassName('ytd-player'); if (ytdContainer.length == 1) { pageObserver.disconnect(); stage = 1; log("observing containers."); pageObserver.observe(ytdContainer[0], { childList: true }); } } else if (stage === 1) { // should have the player ready to replace now const videoId = youtube_parser(document.location.href); if (!videoId) { log("not a video page."); continue; } const url = new URL(document.location.href); if (!replacePlayer(videoId, url.searchParams.getAll('t'))) continue; pageObserver.disconnect(); stage = 2; } } } } function addTextObservers() { const desc = document.querySelectorAll('#description-inline-expander yt-attributed-string')[0]; if (desc && !desc.classList.contains(observingTag)) { try { log("observing video description."); descObserver.observe(desc, { childList: true }); desc.classList.add(observingTag); } catch (error) { log("couldn't observe video description."); console.error(error); } } const comments = document.querySelectorAll('#comments')[0]; if (comments && !comments.classList.contains(observingTag)) { try { log("observing comments section."); commentsObserver.observe(comments, { childList: true }); comments.classList.add(observingTag); } catch (error) { log("couldn't observe comments section."); console.error(error); } } } function commentsContainerMutated(mutationList, observer) { log("comments section mutated."); for (const mutation of mutationList) { const comments = document.querySelectorAll('#comments > #sections > #contents')[0]; textObserver.observe(comments, { childList: true }); } } function textContainersMutated(mutationList, observer) { for (const mutation of mutationList) { for (let i = 0; i < Array.from(mutation.addedNodes).length; i++) { const node = mutation.addedNodes[i]; const links = node.querySelectorAll('#comment #body #comment-content #content-text a'); if (!links.length) continue; for (let l = 0; l < links.length; l++) { const link = links[l]; const taggedClass = 'aab-link'; if (link.classList.contains(taggedClass)) continue; link.classList.add(taggedClass); const matches = /(?:(?\d\d?):)?(?\d\d?):(?\d\d?)/.exec(link.innerText); if (!matches) continue; let timestamp = (Number(matches.groups.mm)*60)+Number(matches.groups.ss); if (matches.groups.hh > 0) { timestamp += Number(matches.groups.hh) * 60 * 60; } log(`found timestamp ${timestamp}`); link.addEventListener('click', (ev) => { log(timestamp); callPlayer('playVideo'); callPlayer('seekTo', [timestamp]); }); } } } } })();