// Persistent YouTube ad auto-skipper for the broadcast Chrome. Connects over // CDP and injects a small watcher into every tab (current and future). The // watcher clicks the "Skip ad" button the instant it appears, closes overlay // ads, and fast-forwards unskippable ads (seek to end + 16x + mute) so they are // gone in ~1s. Self-contained: no extension, no network/hosts changes. // // node bot/scripts/stream-test/ad-skip.mjs (CDP_PORT, default 9222) import { chromium } from 'playwright'; const CDP = process.env.CDP_PORT || '9222'; const WATCH = `(() => { if (window.__ytAdSkip) return; window.__ytAdSkip = true; const tick = () => { try { const p = document.getElementById('movie_player'); const adShowing = !!(p && p.classList && p.classList.contains('ad-showing')); // 1) click any skip button const skip = document.querySelector( '.ytp-ad-skip-button, .ytp-ad-skip-button-modern, .ytp-skip-ad-button, .ytp-ad-skip-button-container button'); if (skip) skip.click(); // 2) close overlay/banner ads document.querySelectorAll('.ytp-ad-overlay-close-button, .ytp-ad-overlay-close-container button').forEach((b) => b.click()); // 3) fast-forward an unskippable ad const v = document.querySelector('video'); if (v) { if (adShowing) { v.muted = true; if (isFinite(v.duration) && v.duration > 0) { try { v.currentTime = v.duration; } catch {} } v.playbackRate = 16; } else if (v.playbackRate > 2) { v.playbackRate = 1; // restore after the ad } } } catch {} }; setInterval(tick, 250); })();`; async function arm(page) { try { await page.addInitScript(WATCH); } catch {} // survives navigations try { await page.evaluate(WATCH); } catch {} // arm the already-loaded doc } async function session() { const b = await chromium.connectOverCDP(`http://localhost:${CDP}`); const ctx = b.contexts()[0]; for (const p of ctx.pages()) await arm(p); ctx.on('page', arm); // new tabs console.log('ad-skip armed on', ctx.pages().length, 'tab(s)'); await new Promise((resolve) => b.on('disconnected', resolve)); // until Chrome goes away } // Reconnect across Chrome restarts so the broadcast stays ad-free. while (true) { try { await session(); } catch (e) { /* CDP down */ } await new Promise((r) => setTimeout(r, 3000)); }