Several of my video streaming web sites cannot have their ads blocked by things like uBlock Origin. I'm not talking sites like YouTube, they can try all they want, uBO still finds a way! Pluto TV is an example. uBO cannot do anything about their ads. And they also serve the same EXACT ads time after time after time. Needless to say, the repetition makes those ads EXTREMELY annoying. Here is how I block them, an "out of sight, out of mind" approach. The video plays normally: But when I hover the mouse over the upper right corner, several buttons appear: 30 seconds up to 5 minutes in 30sec intervals. Click a button and the underlying crap-ad is "hidden" (next pic). It mutes if originally unmuted, then returns to original unmuted after the countdown. It keeps unmuted if originally unmuted, then returns to original unmuted after countdown. The countdown can be clicked to cancel at any time, returning to original mute/unmute. The borders are blurred to view a "non-offensive" portion of screen to cancel if wanted. Code: // ==UserScript== // @name - Add - Video - List - Blackout Overlay Button // @version 3.1.2 // @match *://pluto.tv/* // @grant none // ==/UserScript== (function () { 'use strict'; /*** CONFIGURATION ***/ const HOVER_WIDTH = 210; // px from right edge const HOVER_HEIGHT = 66; // px from top edge const COLOR_CHANGE_INTERVAL = 30000; // 30 seconds in ms const CANCEL_COLORS = [ '#3A3A3A', // Dark Charcoal '#4B3B5E', // Slate Violet '#004D7A', // Deep Azure '#007A7A', // Teal Blue '#007A33', // Emerald Green '#4F7A00', // Olive Green '#7A6A00', // Golden Olive '#947400', // Antique Gold '#A67F00', // Amber Gold '#B38B00' // Rich Mustard Gold ]; let originalMuteStates = []; let originalMuteStatusText = 'Unknown'; let countdownInterval = null; let hideTimeout = null; let colorInterval = null; let currentColorIndex = 0; let hoverTimer = null; // Create container for buttons (5x2 grid) const buttonContainer = document.createElement('div'); Object.assign(buttonContainer.style, { display: 'grid', gap: '5px', gridTemplateColumns: 'repeat(5, auto)', opacity: '0', pointerEvents: 'none', position: 'fixed', right: '5px', top: '5px', zIndex: '9999999' }); // Create overlay trigger buttons const btn30Sec = createTriggerButton('0:30', '#2B0000'); // Deep Maroon const btn1Min = createTriggerButton('1:00', '#5E1A00'); // Rust Brown const btn1Min30Sec = createTriggerButton('1:30', '#7A4A00'); // Amber Ochre const btn2Min = createTriggerButton('2:00', '#6B7A00'); // Moss Green const btn2Min30Sec = createTriggerButton('2:30', '#1F7A00'); // Forest Green const btn3Min = createTriggerButton('3:00', '#007A3D'); // Sea Green const btn3Min30Sec = createTriggerButton('3:30', '#007A7A'); // Teal Blue const btn4Min = createTriggerButton('4:00', '#004D7A'); // Cerulean Blue const btn4Min30Sec = createTriggerButton('4:30', '#2B007A'); // Indigo Violet const btn5Min = createTriggerButton('5:00', '#7A00AA'); // Royal Purple buttonContainer.appendChild(btn30Sec); buttonContainer.appendChild(btn1Min); buttonContainer.appendChild(btn1Min30Sec); buttonContainer.appendChild(btn2Min); buttonContainer.appendChild(btn2Min30Sec); buttonContainer.appendChild(btn3Min); buttonContainer.appendChild(btn3Min30Sec); buttonContainer.appendChild(btn4Min); buttonContainer.appendChild(btn4Min30Sec); buttonContainer.appendChild(btn5Min); // Create first overlay (foreground layer) const overlay = document.createElement('div'); overlay.id = 'blackoutOverlay'; Object.assign(overlay.style, { alignItems: 'center', backgroundColor: '#000', display: 'none', displayFlex: 'flex', height: '100%', justifyContent: 'center', position: 'fixed', left: '0', top: '0', width: '100%', zIndex: '9999997', maskImage: ` linear-gradient(to right, transparent 0%, black 40px, black calc(100% - 40px), transparent 100%), linear-gradient(to bottom, transparent 0%, black 30px, black calc(100% - 50px), transparent 100%) `, maskComposite: 'intersect', maskRepeat: 'no-repeat', maskPosition: 'center', maskSize: '100% 100%' }); // Create second overlay (background layer) const overlay2 = document.createElement('div'); overlay2.id = 'blackoutOverlay2'; Object.assign(overlay2.style, { alignItems: 'center', backgroundColor: 'rgba(0, 0, 0, 0.6)', display: 'none', displayFlex: 'flex', height: '100%', justifyContent: 'center', position: 'fixed', left: '0', top: '0', width: '100%', zIndex: '9999995' }); // Create cancel button with countdown + mute status const cancelBtn = document.createElement('button'); Object.assign(cancelBtn.style, { backgroundColor: CANCEL_COLORS[0], border: 'none', borderRadius: '5px', color: '#fff', cursor: 'pointer', fontFamily: 'monospace', fontSize: '16px', lineHeight: '1.5', padding: '5px', textAlign: 'center', width: '80px' }); overlay.appendChild(cancelBtn); /*** FUNCTIONS ***/ function createTriggerButton(text, bgColor) { const btn = document.createElement('button'); btn.id = 'blackoutButton' + text; btn.textContent = text; Object.assign(btn.style, { backgroundColor: bgColor, border: 'none', borderRadius: '5px', color: '#fff', cursor: 'pointer', height: '28px', fontFamily: 'monospace', fontSize: '14px', padding: '5px', textAlign: 'center', width: '40px' }); return btn; } function muteMediaConditionally() { originalMuteStates = []; let anyUnmuted = false; document.querySelectorAll('video, audio').forEach(media => { originalMuteStates.push({ element: media, wasMuted: media.muted }); if (!media.muted) { anyUnmuted = true; media.muted = true; } }); originalMuteStatusText = anyUnmuted ? 'Unmuted' : 'Muted'; } function restoreMuteStates() { originalMuteStates.forEach(({ element, wasMuted }) => { if (!wasMuted) { element.muted = false; } }); originalMuteStates = []; } function startColorCycle() { currentColorIndex = 0; cancelBtn.style.backgroundColor = CANCEL_COLORS[currentColorIndex]; cancelBtn.id = 'blackoutCancel' + CANCEL_COLORS[currentColorIndex]; colorInterval = setInterval(() => { currentColorIndex = (currentColorIndex + 1) % CANCEL_COLORS.length; cancelBtn.style.backgroundColor = CANCEL_COLORS[currentColorIndex]; cancelBtn.id = 'blackoutCancel' + CANCEL_COLORS[currentColorIndex]; }, COLOR_CHANGE_INTERVAL); } function stopColorCycle() { clearInterval(colorInterval); colorInterval = null; } function showOverlay(durationSeconds) { overlay.style.display = 'flex'; overlay2.style.display = 'flex'; muteMediaConditionally(); const totalSegments = Math.ceil(durationSeconds / 30); let currentSegment = 1; let segmentTimeLeft = Math.min(30, durationSeconds); // start at 30 or less if short overlay updateCancelText(segmentTimeLeft, currentSegment, totalSegments); startColorCycle(); countdownInterval = setInterval(() => { segmentTimeLeft--; if (segmentTimeLeft <= 0) { // Move to next segment if time remains if (currentSegment < totalSegments) { currentSegment++; segmentTimeLeft = Math.min(30, durationSeconds - (currentSegment - 1) * 30); } else { hideOverlay(); return; } } updateCancelText(segmentTimeLeft, currentSegment, totalSegments); }, 1000); hideTimeout = setTimeout(hideOverlay, durationSeconds * 1000); } function updateCancelText(segmentSeconds, currentSegment, totalSegments) { const formattedSeconds = String(segmentSeconds).padStart(2, '0'); cancelBtn.innerHTML = `${formattedSeconds}` + `<br>${currentSegment} of ${totalSegments}` + `<br>${originalMuteStatusText}`; } function hideOverlay() { overlay.style.display = 'none'; overlay2.style.display = 'none'; clearInterval(countdownInterval); clearTimeout(hideTimeout); stopColorCycle(); restoreMuteStates(); } /*** EVENT LISTENERS ***/ btn5Min.addEventListener('click', () => showOverlay(300)); btn4Min30Sec.addEventListener('click', () => showOverlay(270)); btn4Min.addEventListener('click', () => showOverlay(240)); btn3Min30Sec.addEventListener('click', () => showOverlay(210)); btn3Min.addEventListener('click', () => showOverlay(180)); btn2Min30Sec.addEventListener('click', () => showOverlay(150)); btn2Min.addEventListener('click', () => showOverlay(120)); btn1Min30Sec.addEventListener('click', () => showOverlay(90)); btn1Min.addEventListener('click', () => showOverlay(60)); btn30Sec.addEventListener('click', () => showOverlay(30)); cancelBtn.addEventListener('click', hideOverlay); // Larger rectangular hover detection document.addEventListener('mousemove', (e) => { const inHoverZone = e.clientX >= window.innerWidth - HOVER_WIDTH && e.clientY <= HOVER_HEIGHT; if (inHoverZone) { buttonContainer.style.opacity = '1'; // Start 5-second timer to hide hoverTimer = setTimeout(() => { buttonContainer.style.opacity = '0'; }, 5000); buttonContainer.style.pointerEvents = 'auto'; } else { buttonContainer.style.opacity = '0'; clearTimeout(hoverTimer); buttonContainer.style.pointerEvents = 'none'; } }); // Append elements to DOM document.body.appendChild(buttonContainer); document.body.appendChild(overlay); document.body.appendChild(overlay2); })();
<side note> I suppose the "scripting" section is a better place for this. Perhaps a Mod can move? </side note>
This is actually a pretty practical approach for streaming sites where the same ads keep interrupting the viewing experience. I like that the script doesn’t try to interfere with the video itself; it simply gives the user a temporary way to hide the annoying part while keeping playback running. Repeated ads can be especially distracting when you’re watching movies on MovieBox or other long-form video content. I usually prefer a clean viewing experience where the video, audio, subtitles, and controls stay uninterrupted. The countdown option is also a nice touch because it gives the user control instead of permanently changing the page. This kind of small quality-of-life improvement can make a noticeable difference when watching movies through different streaming or movie-app setups.