您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Block autoplay and require direct user interaction to play media, detecting covers via computed styles
当前为
// ==UserScript== // @name Disable Autoplay // @namespace https://www.androidacy.com/ // @version 1.7.1 // @description Block autoplay and require direct user interaction to play media, detecting covers via computed styles // @author Androidacy // @include * // @icon https://www.androidacy.com/wp-content/uploads/cropped-cropped-cropped-cropped-New-Project-32-69C2A87-1-192x192.jpg // @grant none // @run-at document-start // ==/UserScript== (function() { 'use strict'; const allowedToPlay = new WeakSet(); const mediaTags = ['video', 'audio']; function debugLog(...args) { console.debug('[DisableAutoplay]', ...args); } function warnLog(...args) { console.warn('[DisableAutoplay]', ...args); } function disableAutoplay(media) { if (allowedToPlay.has(media)) return; debugLog('Processing media:', media); // Remove autoplay attribute if (media.hasAttribute('autoplay')) { media.removeAttribute('autoplay'); debugLog('Removed autoplay attribute'); } // Pause if playing if (!media.paused) { media.pause(); debugLog('Paused media'); } // Override play method const originalPlay = media.play; media.play = function(...args) { if (allowedToPlay.has(media)) { debugLog('Playing media:', media); return originalPlay.apply(media, args); } else { warnLog('Autoplay blocked:', media); return Promise.reject(new Error('Autoplay is disabled by a userscript.')); } }; // Enable playback on trusted user interaction const enablePlayback = (event) => { if (!event.isTrusted) { warnLog('Untrusted event ignored:', event); return; } debugLog('User interaction detected:', event.type, 'on', event.target); allowedToPlay.add(media); media.play().catch(err => warnLog('Playback error:', err, media)); // Remove listeners after enabling media.removeEventListener('click', enablePlayback); media.removeEventListener('touchstart', enablePlayback); removeCoverListeners(media, enablePlayback); }; // Add event listeners to media media.addEventListener('click', enablePlayback, { once: true }); media.addEventListener('touchstart', enablePlayback, { once: true }); debugLog('Added event listeners to media'); // Add event listeners to cover elements detected via computed styles addCoverListeners(media, enablePlayback); } function addCoverListeners(media, handler) { const covers = findCoverElements(media); covers.forEach(cover => { cover.addEventListener('click', handler, { once: true }); cover.addEventListener('touchstart', handler, { once: true }); debugLog('Added event listeners to cover:', cover); }); } function removeCoverListeners(media, handler) { const covers = findCoverElements(media); covers.forEach(cover => { cover.removeEventListener('click', handler); cover.removeEventListener('touchstart', handler); debugLog('Removed event listeners from cover:', cover); }); } function findCoverElements(media) { const covers = []; const mediaRect = media.getBoundingClientRect(); const parent = media.parentElement; if (!parent) return covers; // Iterate through parent's children to find overlapping elements Array.from(parent.children).forEach(sibling => { if (sibling === media) return; const style = window.getComputedStyle(sibling); const position = style.position; const display = style.display; const visibility = style.visibility; const pointerEvents = style.pointerEvents; if (display === 'none' || visibility === 'hidden' || pointerEvents === 'none') return; if (position !== 'absolute' && position !== 'fixed' && position !== 'relative') return; const siblingRect = sibling.getBoundingClientRect(); // Check if sibling overlaps significantly with media const overlap = isOverlapping(mediaRect, siblingRect); if (overlap) { covers.push(sibling); } }); return covers; } function isOverlapping(rect1, rect2) { const threshold = 0.3; // Minimum overlap percentage const intersection = { left: Math.max(rect1.left, rect2.left), right: Math.min(rect1.right, rect2.right), top: Math.max(rect1.top, rect2.top), bottom: Math.min(rect1.bottom, rect2.bottom) }; const width = intersection.right - intersection.left; const height = intersection.bottom - intersection.top; if (width <= 0 || height <= 0) return false; const areaIntersection = width * height; const areaMedia = rect1.width * rect1.height; return (areaIntersection / areaMedia) >= threshold; } function processMediaElements() { mediaTags.forEach(tag => { document.querySelectorAll(tag).forEach(media => disableAutoplay(media)); }); } function observeMedia() { const observer = new MutationObserver(mutations => { mutations.forEach(mutation => { mutation.addedNodes.forEach(node => { if (node.nodeType !== Node.ELEMENT_NODE) return; mediaTags.forEach(tag => { if (node.matches(tag)) { debugLog('New media added:', node); disableAutoplay(node); } node.querySelectorAll(tag).forEach(media => { debugLog('New nested media added:', media); disableAutoplay(media); }); }); }); }); }); observer.observe(document.documentElement, { childList: true, subtree: true }); debugLog('Started observing DOM for new media elements'); } function init() { debugLog('Initializing DisableAutoplay userscript'); processMediaElements(); observeMedia(); } // Run init at document-start init(); // Also process at DOMContentLoaded to catch any late-loaded media document.addEventListener('DOMContentLoaded', () => { debugLog('DOMContentLoaded event fired'); processMediaElements(); }); })();