Funnyjunk - Soundless Autoplay Begone

Convert autoplay videos with sound to click-to-play, keep silent autoplay videos looping, show thumbnail, conditional controls

// ==UserScript==
// @name         Funnyjunk - Soundless Autoplay Begone
// @namespace    http://tampermonkey.net/
// @version      1.10.1
// @description  Convert autoplay videos with sound to click-to-play, keep silent autoplay videos looping, show thumbnail, conditional controls
// @match        *://funnyjunk.com/*
// @grant        none
// @license MIT
// ==/UserScript==

(function () {
    'use strict';

    function processVideoLogic(video) {
        const hasAudio =
            (typeof video.mozHasAudio !== 'undefined' && video.mozHasAudio) ||
            (typeof video.webkitAudioDecodedByteCount !== 'undefined' && video.webkitAudioDecodedByteCount > 0) ||
            (video.audioTracks && video.audioTracks.length > 0);

        if (!hasAudio) {
            // Silent autoplay video: keep autoplay & loop enabled, muted, no controls (like GIF)
            video.controls = false;
            video.loop = true;
            video.muted = true;
            // No changes to autoplay or playback state
        } else {
            // Video has sound: convert to click-to-play
            video.removeAttribute('autoplay');
            video.removeAttribute('loop');
            video.loop = false;

            video.pause();
            video.muted = true;   // start muted

            video.controls = true;

            let userInteracted = false;

            // Override play() to block autoplay until user interaction
            const originalPlay = video.play.bind(video);
            video.play = function () {
                if (!userInteracted) {
                    return Promise.resolve();
                }
                return originalPlay();
            };

            // On first user play, unmute and play again
            video.addEventListener('play', function onPlay() {
                if (!userInteracted) {
                    userInteracted = true;
                    video.muted = false;
                    video.play();
                }
            }, { once: true });

            // Show thumbnail frame
            if (video.currentTime === 0) {
                video.currentTime = 0.01;
            }

            video.pause();
        }

        video.dataset.converted = 'true';
    }

    function setupVideo(video) {
        if (video.dataset.converted) return;

        if (video.autoplay && video.muted) {
            if (video.readyState >= 1) {
                // Metadata already loaded, process immediately
                processVideoLogic(video);
            } else {
                // Wait for metadata
                video.addEventListener('loadedmetadata', function onMeta() {
                    video.removeEventListener('loadedmetadata', onMeta);
                    processVideoLogic(video);
                });
            }
        }
    }

    function processNode(node) {
        if (node.nodeName === 'VIDEO') {
            setupVideo(node);
        } else if (node.querySelectorAll) {
            node.querySelectorAll('video').forEach(setupVideo);
        }
    }

    function run() {
        processNode(document);
    }

    run();

    window.addEventListener('pageshow', (event) => {
        if (event.persisted) {
            run();
        }
    });

    const observer = new MutationObserver(mutations => {
        mutations.forEach(mutation => {
            mutation.addedNodes.forEach(processNode);
        });
    });

    observer.observe(document.body, { childList: true, subtree: true });
})();