Block autoplay and require direct user interaction to play media, with debug logs and cover element support
目前為
// ==UserScript==
// @name Disable Autoplay with Debugging and Cover Handling
// @namespace https://www.androidacy.com/
// @version 1.5.0
// @description Block autoplay and require direct user interaction to play media, with debug logs and cover element support
// @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-end
// ==/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);
if (media.hasAttribute('autoplay')) {
media.removeAttribute('autoplay');
debugLog('Removed autoplay attribute');
}
if (!media.paused) {
media.pause();
debugLog('Paused media');
}
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.'));
}
};
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));
media.removeEventListener('click', enablePlayback);
media.removeEventListener('touchstart', enablePlayback);
removeCoverListeners(media, enablePlayback);
};
media.addEventListener('click', enablePlayback, { once: true });
media.addEventListener('touchstart', enablePlayback, { once: true });
debugLog('Added event listeners to media');
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 possibleClasses = ['cover', 'overlay', 'media-cover', 'media-overlay'];
const parent = media.parentElement;
if (parent) {
possibleClasses.forEach(cls => {
parent.querySelectorAll(`.${cls}`).forEach(cover => covers.push(cover));
});
}
return covers;
}
function processExistingMedia() {
mediaTags.forEach(tag => {
document.querySelectorAll(tag).forEach(media => disableAutoplay(media));
});
}
function observeNewMedia() {
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.body, { childList: true, subtree: true });
debugLog('Started observing for new media elements');
}
function init() {
debugLog('Initializing DisableAutoplay userscript');
processExistingMedia();
observeNewMedia();
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();