|
1 | | -// youtube_content.js – PureShield ULTRA MODE YouTube Ad Blocker (v1.0.4) |
2 | | -// Strategy: "Physical Removal" to fix grid gaps + Skip fallback |
| 1 | +// youtube_content.js – PureShield v1.0.5 (Isolated World – DOM Cleanup) |
| 2 | +// Works alongside yt_scriptlet.js (Main World). |
| 3 | +// Handles: element removal, skip-button clicking, shielding overlay, anti-adblock dismissal. |
3 | 4 |
|
4 | 5 | (function () { |
5 | 6 | 'use strict'; |
6 | 7 |
|
7 | 8 | let adsEnabled = true; |
8 | | - let wasAdPlaying = false; |
9 | 9 |
|
10 | | - // ── Ultra Removal Selectors (Elements to COMPLETELY .remove()) ──────────── |
11 | | - const REMOVAL_SELECTORS = [ |
| 10 | + // ── Elements to physically remove from the DOM ──────────────────── |
| 11 | + const NUKE_SELECTORS = [ |
| 12 | + // Homepage / feed ads |
12 | 13 | 'ytd-ad-slot-renderer', |
13 | 14 | 'ytd-rich-item-renderer:has(ytd-ad-slot-renderer)', |
14 | 15 | 'ytd-rich-item-renderer:has(ytd-in-feed-ad-layout-renderer)', |
| 16 | + 'ytd-in-feed-ad-layout-renderer', |
| 17 | + 'ytd-banner-promo-renderer', |
| 18 | + 'ytd-statement-banner-renderer', |
15 | 19 | 'ytd-carousel-ad-renderer', |
16 | 20 | 'ytd-merch-shelf-renderer', |
17 | | - 'ytd-search-pyv-renderer', |
18 | | - 'ytd-promoted-video-renderer', |
19 | | - 'ytd-display-ad-renderer', |
20 | | - 'ytd-statement-banner-renderer', |
21 | | - 'ytd-in-feed-ad-layout-renderer', |
22 | | - 'ytd-compact-promoted-item-renderer', |
23 | 21 | 'ytd-brand-video-singleton-renderer', |
24 | 22 | 'ytd-brand-video-shelf-renderer', |
25 | 23 | '#masthead-ad', |
26 | 24 | '#player-ads', |
| 25 | + // Search ads |
| 26 | + 'ytd-search-pyv-renderer', |
| 27 | + 'ytd-promoted-video-renderer', |
| 28 | + 'ytd-promoted-sparkles-web-renderer', |
| 29 | + // Sidebar / companion ads |
27 | 30 | 'ytd-action-companion-ad-renderer', |
28 | 31 | 'ytd-companion-slot-renderer', |
29 | | - 'ytd-promoted-sparkles-web-renderer', |
30 | | - 'ytd-mealbar-promo-renderer', |
| 32 | + 'ytd-display-ad-renderer', |
| 33 | + 'ytd-compact-promoted-item-renderer', |
31 | 34 | 'ytd-video-masthead-ad-v3-renderer', |
32 | | - 'ytd-enforcement-message-view-model', |
33 | | - 'tp-yt-paper-dialog.ytd-popup-container:has(#dismiss-button)', |
34 | | - 'tp-yt-iron-overlay-backdrop', |
| 35 | + 'ytd-mealbar-promo-renderer', |
| 36 | + '#panels > ytd-engagement-panel-section-list-renderer[target-id="engagement-panel-ads"]', |
| 37 | + // Player overlay ads |
| 38 | + '.ytp-ad-overlay-container', |
35 | 39 | '.ytp-ad-module', |
36 | | - '.ytp-ad-player-overlay-layout' |
| 40 | + '.ytp-ad-player-overlay-layout', |
| 41 | + '.ytp-ad-image-overlay', |
| 42 | + '.ytp-ad-text-overlay', |
| 43 | + '.ytp-ad-action-interstitial', |
| 44 | + // Anti-adblock popups |
| 45 | + 'ytd-enforcement-message-view-model', |
| 46 | + 'tp-yt-paper-dialog:has(yt-formatted-string#text.yt-about-this-ad-renderer)', |
37 | 47 | ]; |
38 | 48 |
|
39 | | - let shieldEl = null; |
40 | | - |
41 | | - function createShield() { |
42 | | - if (shieldEl) return; |
43 | | - shieldEl = document.createElement('div'); |
44 | | - shieldEl.id = 'pureshield-ultra-overlay'; |
45 | | - shieldEl.innerHTML = ` |
46 | | - <div class="pureshield-container"> |
47 | | - <div class="pureshield-logo-wrap"> |
48 | | - <div class="pureshield-logo">PureShield</div> |
49 | | - <div class="pureshield-bolt">⚡</div> |
50 | | - </div> |
51 | | - <div class="pureshield-status">ULTRA MODE: Scriptlet Active</div> |
52 | | - </div> |
53 | | - `; |
54 | | - const shieldStyle = document.createElement('style'); |
55 | | - shieldStyle.textContent = ` |
56 | | - #pureshield-ultra-overlay { |
57 | | - position: absolute; |
58 | | - top: 0; left: 0; width: 100%; height: 100%; |
59 | | - background: radial-gradient(circle, #0a0a0a 0%, #000 100%); |
60 | | - z-index: 2147483647; display: none; |
61 | | - flex-direction: column; justify-content: center; align-items: center; |
62 | | - color: #fff; font-family: 'Inter', sans-serif; pointer-events: none; |
| 49 | + // ── CSS to inject ──────────────────────────────────────────────── |
| 50 | + function injectCSS() { |
| 51 | + const s = document.createElement('style'); |
| 52 | + s.id = 'pureshield-v5-css'; |
| 53 | + s.textContent = ` |
| 54 | + /* Hide video element during ad playback */ |
| 55 | + .ad-showing video, |
| 56 | + [class*="ad-interrupting"] video { |
| 57 | + opacity: 0 !important; |
| 58 | + pointer-events: none !important; |
| 59 | + } |
| 60 | + /* Hide player chrome during ad */ |
| 61 | + .ad-showing .ytp-chrome-bottom, |
| 62 | + .ad-showing .ytp-chrome-top { |
| 63 | + opacity: 0 !important; |
63 | 64 | } |
64 | | - .ad-showing #pureshield-ultra-overlay, [class*="ad-interrupting"] #pureshield-ultra-overlay { |
65 | | - display: flex !important; |
| 65 | + /* Visual shield overlay */ |
| 66 | + #pureshield-shield { |
| 67 | + position: absolute; inset: 0; |
| 68 | + background: #000; z-index: 2147483647; |
| 69 | + display: none; place-items: center; |
| 70 | + color: #fff; font-family: 'Inter', sans-serif; |
| 71 | + pointer-events: none; |
| 72 | + } |
| 73 | + .ad-showing #pureshield-shield, |
| 74 | + [class*="ad-interrupting"] #pureshield-shield { |
| 75 | + display: grid !important; |
| 76 | + } |
| 77 | + #pureshield-shield .ps-logo { |
| 78 | + font-size: 26px; font-weight: 900; |
| 79 | + color: #2ecc71; letter-spacing: 3px; |
| 80 | + text-transform: uppercase; |
| 81 | + } |
| 82 | + #pureshield-shield .ps-sub { |
| 83 | + font-size: 12px; opacity: .6; margin-top: 8px; |
| 84 | + text-transform: uppercase; letter-spacing: 1px; |
66 | 85 | } |
67 | | - .pureshield-logo { font-size: 28px; font-weight: 900; color: #2ecc71; text-transform: uppercase; letter-spacing: 4px; } |
68 | | - .pureshield-bolt { font-size: 24px; color: #f1c40f; } |
69 | | - .pureshield-status { font-size: 13px; text-transform: uppercase; opacity: 0.7; margin-top: 10px; } |
70 | | - .ad-showing video, [class*="ad-interrupting"] video { opacity: 0 !important; visibility: hidden !important; } |
71 | 86 | `; |
72 | | - document.head.appendChild(shieldStyle); |
73 | | - |
74 | | - const mount = () => { |
75 | | - const player = document.querySelector('#movie_player') || document.querySelector('.html5-video-player'); |
76 | | - if (player && !player.contains(shieldEl)) player.appendChild(shieldEl); |
77 | | - }; |
78 | | - mount(); |
79 | | - setInterval(mount, 2000); |
| 87 | + (document.head || document.documentElement).appendChild(s); |
| 88 | + } |
| 89 | + |
| 90 | + // ── Shield overlay ─────────────────────────────────────────────── |
| 91 | + function mountShield() { |
| 92 | + const player = document.querySelector('#movie_player, .html5-video-player'); |
| 93 | + if (!player || player.querySelector('#pureshield-shield')) return; |
| 94 | + const d = document.createElement('div'); |
| 95 | + d.id = 'pureshield-shield'; |
| 96 | + d.innerHTML = '<div><div class="ps-logo">PureShield ⚡</div><div class="ps-sub">Ad neutralized</div></div>'; |
| 97 | + player.appendChild(d); |
80 | 98 | } |
81 | 99 |
|
82 | | - function removeAds() { |
| 100 | + // ── Core cleanup loop ──────────────────────────────────────────── |
| 101 | + function cleanup() { |
83 | 102 | if (!adsEnabled) return; |
84 | | - |
85 | | - // 1. Physical Removal to fix grid layout |
86 | | - REMOVAL_SELECTORS.forEach(selector => { |
87 | | - document.querySelectorAll(selector).forEach(el => { |
88 | | - // If it's a rich-item-renderer, we should be careful to re-flow |
89 | | - el.remove(); |
90 | | - // Trigger resize event to force YouTube to recalculate grid |
91 | | - window.dispatchEvent(new Event('resize')); |
92 | | - }); |
93 | | - }); |
94 | 103 |
|
95 | | - // 2. Video Skip Fallback |
96 | | - const video = document.querySelector('video'); |
97 | | - const adActive = !!(document.querySelector('.ad-showing') || document.querySelector('[class*="ad-interrupting"]')); |
| 104 | + // 1. Remove ad elements |
| 105 | + for (const sel of NUKE_SELECTORS) { |
| 106 | + const els = document.querySelectorAll(sel); |
| 107 | + if (els.length) { |
| 108 | + els.forEach(el => el.remove()); |
| 109 | + } |
| 110 | + } |
| 111 | + |
| 112 | + // 2. Dismiss anti-adblock overlays |
| 113 | + const overlays = document.querySelectorAll('tp-yt-iron-overlay-backdrop'); |
| 114 | + overlays.forEach(o => o.remove()); |
| 115 | + // Click dismiss / continue buttons |
| 116 | + const dismiss = document.querySelector( |
| 117 | + '#dismiss-button, .yt-about-this-ad-renderer button, tp-yt-paper-dialog #dismiss-button' |
| 118 | + ); |
| 119 | + if (dismiss) dismiss.click(); |
98 | 120 |
|
| 121 | + // 3. Mount shield if needed |
| 122 | + mountShield(); |
| 123 | + |
| 124 | + // 4. Skip / jump video ads |
| 125 | + const adActive = document.querySelector('.ad-showing, [class*="ad-interrupting"]'); |
| 126 | + const video = document.querySelector('video'); |
99 | 127 | if (adActive && video) { |
100 | | - wasAdPlaying = true; |
101 | | - |
102 | | - // Instant skip button click |
103 | | - const skip = document.querySelector('.ytp-skip-ad-button, .ytp-ad-skip-button, .ytp-ad-skip-button-modern'); |
104 | | - if (skip) skip.click(); |
105 | | - |
106 | | - // Speed up + Jump |
107 | | - if (video.duration && isFinite(video.duration)) { |
108 | | - video.currentTime = video.duration - 0.1; |
109 | | - } |
110 | | - video.playbackRate = 16; |
111 | | - video.muted = true; |
112 | | - } else if (wasAdPlaying && !adActive) { |
113 | | - wasAdPlaying = false; |
114 | | - if (video) { |
115 | | - video.playbackRate = 1; |
116 | | - video.muted = false; |
117 | | - } |
| 128 | + // Click skip button instantly |
| 129 | + const skip = document.querySelector( |
| 130 | + '.ytp-skip-ad-button, .ytp-ad-skip-button, .ytp-ad-skip-button-modern, .ytp-ad-skip-button-slot button' |
| 131 | + ); |
| 132 | + if (skip && skip.offsetParent !== null) { |
| 133 | + skip.click(); |
| 134 | + return; |
| 135 | + } |
| 136 | + // Jump to end |
| 137 | + if (video.duration && isFinite(video.duration) && video.duration > 0.5) { |
| 138 | + video.currentTime = video.duration - 0.1; |
| 139 | + } |
| 140 | + video.playbackRate = 16; |
| 141 | + video.muted = true; |
118 | 142 | } |
119 | 143 | } |
120 | 144 |
|
| 145 | + // ── Init ────────────────────────────────────────────────────────── |
121 | 146 | function init() { |
122 | | - chrome.storage.local.get(['filters'], (saved) => { |
123 | | - adsEnabled = saved.filters ? saved.filters.ads !== false : true; |
124 | | - if (adsEnabled) { |
125 | | - createShield(); |
126 | | - // Polling for physical cleanup |
127 | | - setInterval(removeAds, 100); |
128 | | - // Instant removal for home grid |
129 | | - const observer = new MutationObserver(removeAds); |
130 | | - observer.observe(document.body, { childList: true, subtree: true }); |
131 | | - } |
| 147 | + chrome.storage.local.get(['filters'], (result) => { |
| 148 | + adsEnabled = result.filters ? result.filters.ads !== false : true; |
| 149 | + if (!adsEnabled) return; |
| 150 | + |
| 151 | + injectCSS(); |
| 152 | + |
| 153 | + // Run cleanup immediately and on interval |
| 154 | + cleanup(); |
| 155 | + setInterval(cleanup, 150); |
| 156 | + |
| 157 | + // MutationObserver for instant reaction |
| 158 | + const obs = new MutationObserver(cleanup); |
| 159 | + obs.observe(document.documentElement, { childList: true, subtree: true }); |
132 | 160 | }); |
133 | 161 | } |
134 | 162 |
|
| 163 | + // Listen for setting changes |
| 164 | + chrome.storage.onChanged.addListener((changes) => { |
| 165 | + if (changes.filters) { |
| 166 | + adsEnabled = changes.filters.newValue.ads !== false; |
| 167 | + } |
| 168 | + }); |
| 169 | + |
135 | 170 | if (document.readyState === 'loading') { |
136 | 171 | document.addEventListener('DOMContentLoaded', init); |
137 | 172 | } else { |
|
0 commit comments