Disable Video Autoplay

Prevents videos from autoplaying on websites and adds a play button overlay

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Disable Video Autoplay
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Prevents videos from autoplaying on websites and adds a play button overlay
// @author       You
// @match        *://*/*
// @grant        none
// @run-at       document-start
// ==/UserScript==

(function() {
    'use strict';
    
    // User-configurable options
    const config = {
        // List of domains where autoplay blocking is disabled (e.g. 'youtube.com')
        allowlist: [],
        // List of domains where autoplay blocking is always enabled (leave empty to target all sites)
        denylist: [],
        // Show a play button overlay on videos
        showPlayButton: true,
        // Enable debug logging in the console
        debug: false
    };

    // Decide if the current site should be processed based on allowlist/denylist
    function shouldProcessSite() {
        const currentDomain = window.location.hostname.replace('www.', '');
        
        // Skip processing if the site is in the allowlist
        if (config.allowlist.some(site => currentDomain.includes(site))) {
            logDebug('Site in allowlist, skipping');
            return false;
        }
        
        // Only process if denylist is empty or the site is in the denylist
        if (config.denylist.length > 0 && !config.denylist.some(site => currentDomain.includes(site))) {
            logDebug('Site not in denylist, skipping');
            return false;
        }
        
        return true;
    }

    // Print debug messages if debug mode is enabled
    function logDebug(message) {
        if (config.debug) {
            console.log('[Disable Autoplay]', message);
        }
    }

    // Remove autoplay from <video> elements and prevent programmatic autoplay
    function processVideoElement(video) {
        if (video.hasAttribute('autoplay')) {
            video.removeAttribute('autoplay');
            video.pause();
            logDebug('Removed autoplay attribute from video element');
        }
        
        // Unmute video (some sites use muted+autoplay to bypass restrictions)
        video.muted = false;
        
        // Prevent autoplay even if triggered by scripts
        video.addEventListener('play', function(e) {
            if (!video.hasAttribute('data-user-initiated')) {
                video.pause();
                logDebug('Prevented automatic playback');
            }
        }, true);
        
        // Add overlay if enabled and not already present
        if (config.showPlayButton && !video.hasAttribute('data-overlay-added')) {
            addPlayButtonOverlay(video);
        }
    }

    // Modify <iframe> embeds (e.g. YouTube, Vimeo) to disable autoplay
    function processIframeElement(iframe) {
        // Special handling for YouTube/Vimeo embeds
        if (iframe.src && iframe.src.match(/youtube|vimeo/i)) {
            const currentSrc = iframe.src;
            // If autoplay=1, set to 0
            if (currentSrc.includes('autoplay=1')) {
                iframe.src = currentSrc.replace('autoplay=1', 'autoplay=0');
                logDebug('Disabled autoplay for embedded video iframe');
            } else if (!currentSrc.includes('autoplay=0')) {
                // If no autoplay param, add autoplay=0
                iframe.src = currentSrc + (currentSrc.includes('?') ? '&' : '?') + 'autoplay=0';
                logDebug('Added autoplay=0 parameter to iframe');
            }
        }
        
        // For other iframes, try to replace autoplay=1 with autoplay=0 in the src
        else if (iframe.src && iframe.src.includes('autoplay')) {
            try {
                iframe.src = iframe.src.replace(/autoplay=1/g, 'autoplay=0');
                logDebug('Modified autoplay parameter in iframe src');
            } catch (e) {
                logDebug('Could not modify iframe src: ' + e.message);
            }
        }
    }

    // Add a play button overlay to the video element
    function addPlayButtonOverlay(video) {
        // Don't add overlay if already present
        if (video.hasAttribute('data-overlay-added')) {
            return;
        }
        
        // Mark as processed so overlay isn't added again
        video.setAttribute('data-overlay-added', 'true');
        
        // Create overlay container
        const container = document.createElement('div');
        container.style.cssText = `
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            display: flex;
            align-items: center;
            justify-content: center;
            background-color: rgba(0, 0, 0, 0.2);
            z-index: 10000;
            cursor: pointer;
            opacity: 0;
            transition: opacity 0.3s;
        `;
        container.classList.add('autoplay-disabled-overlay');
        
        // Create the circular play button
        const playButton = document.createElement('div');
        playButton.style.cssText = `
            width: 60px;
            height: 60px;
            background-color: rgba(0, 0, 0, 0.6);
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
        `;
        
        // Create the triangle play icon
        const playIcon = document.createElement('div');
        playIcon.style.cssText = `
            width: 0;
            height: 0;
            border-top: 15px solid transparent;
            border-bottom: 15px solid transparent;
            border-left: 25px solid white;
            margin-left: 5px;
        `;
        
        // Assemble overlay
        playButton.appendChild(playIcon);
        container.appendChild(playButton);
        
        // Make sure the parent is positioned for absolute overlay
        const videoParent = video.parentElement;
        videoParent.style.position = videoParent.style.position || 'relative';
        videoParent.appendChild(container);
        
        // Show overlay on hover
        videoParent.addEventListener('mouseenter', () => {
            container.style.opacity = '1';
        });
        
        videoParent.addEventListener('mouseleave', () => {
            container.style.opacity = '0';
        });
        
        // Play video when overlay is clicked
        container.addEventListener('click', () => {
            video.setAttribute('data-user-initiated', 'true');
            video.play()
                .then(() => {
                    // Hide overlay when playback starts
                    container.style.display = 'none';
                    logDebug('Video playback started by user');
                })
                .catch(err => {
                    logDebug('Error starting video playback: ' + err.message);
                    // Keep overlay visible if playback fails
                });
        });
        
        logDebug('Added play button overlay to video');
    }

    // Scan and process all <video> and <iframe> elements currently in the DOM
    function processExistingMedia() {
        document.querySelectorAll('video').forEach(processVideoElement);
        document.querySelectorAll('iframe').forEach(processIframeElement);
        logDebug('Processed existing media elements');
    }

    // Watch for new videos/iframes added dynamically and process them
    function setupMutationObserver() {
        const observer = new MutationObserver((mutations) => {
            let needsProcessing = false;
            
            // If any new nodes are added, flag for processing
            mutations.forEach((mutation) => {
                if (mutation.addedNodes.length) {
                    needsProcessing = true;
                }
            });
            
            // Batch process after a short delay
            if (needsProcessing) {
                setTimeout(() => {
                    processExistingMedia();
                }, 100); // Let DOM settle
            }
        });
        
        // Observe the whole document for added nodes
        observer.observe(document.body, {
            childList: true,
            subtree: true
        });
        
        logDebug('Mutation observer set up for dynamic content');
    }

    // Main entry point: set up everything
    function initialize() {
        if (!shouldProcessSite()) {
            return;
        }
        
        logDebug('Initializing autoplay blocker');
        
        // Add style for overlays
        const style = document.createElement('style');
        style.textContent = `
            video[data-overlay-added] {
                visibility: visible !important;
                opacity: 1 !important;
            }
        `;
        document.head.appendChild(style);
        
        // Process any existing media right away
        processExistingMedia();
        
        // Watch for new media
        setupMutationObserver();
        
        // Re-scan after short delays to catch late-loaded elements
        setTimeout(processExistingMedia, 1000);
        setTimeout(processExistingMedia, 3000);
    }

    // Run as soon as possible
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initialize);
    } else {
        initialize();
    }
})();