Facebook Reels Enhancer

Enable controls on Facebook Reels videos, auto-unmute, and prevent pausing when switching tabs.

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

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

Tendrás que instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Tendrás que instalar una extensión como Tampermonkey antes de poder instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==UserScript==
// @name         Facebook Reels Enhancer
// @namespace    UserScript
// @match        https://www.facebook.com/*
// @version      2.3
// @license      MIT
// @author       Pyrvox
// @description  Enable controls on Facebook Reels videos, auto-unmute, and prevent pausing when switching tabs.
// @icon         https://www.google.com/s2/favicons?sz=64&domain=facebook.com
// @run-at       document-start
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // Wait for document to be ready before adding styles
    function addStyles() {
        if (!document.head) {
            setTimeout(addStyles, 10);
            return;
        }

        // Add CSS for better controls handling
        const style = document.createElement('style');
        style.textContent = `
            /* Disable overlay that blocks controls */
            .x1ey2m1c.x78zum5.xdt5ytf.xozqiw3.x17qophe.x13a6bvl.x10l6tqk.x1hkcv85,
            .x1ey2m1c.x78zum5.xdt5ytf.xozqiw3.x17qophe.x13a6bvl.x10l6tqk {
                pointer-events: none !important;
                opacity: 0.01 !important;
            }

            /* Make video controls always visible and clickable */
            video::-webkit-media-controls {
                opacity: 1 !important;
                visibility: visible !important;
                z-index: 99999 !important;
            }

            /* Make video container clickable */
            .x1ey2m1c.x78zum5.xdt5ytf.xozqiw3.x17qophe.x13a6bvl.x10l6tqk video {
                pointer-events: auto !important;
                position: relative;
                z-index: 9999 !important;
            }

            /* Disable other potential overlay elements */
            [role="presentation"],
            [data-pagelet],
            [data-visualcompletion] {
                pointer-events: none !important;
            }

            /* Make sure clickable elements remain clickable */
            [role="button"],
            [tabindex],
            button,
            a {
                pointer-events: auto !important;
            }
        `;
        document.head.appendChild(style);
    }

    // Enhanced unmute function
    function attemptUnmute(video) {
        if (!(video instanceof HTMLVideoElement)) return;

        if (video.muted || video.volume === 0) {
            video.muted = false;
            video.volume = 1.0;

            if (video.audioTracks && video.audioTracks.length > 0) {
                for (let track of video.audioTracks) {
                    track.enabled = true;
                }
            }
            video.dispatchEvent(new Event('volumechange', { bubbles: true }));
        }
    }

    // Enable controls for Reels videos
    function enableReelControls(video) {
        if (!video || video.hasAttribute('controls') || !location.href.includes('reel')) return;

        // Add controls to the video
        video.setAttribute('controls', '');
        video.setAttribute('playsinline', '');

        // Style the video and handle overlays
        setTimeout(() => {
            try {
                // Style the video
                Object.assign(video.style, {
                    'position': 'relative',
                    'zIndex': '99999',
                    'pointerEvents': 'auto',
                    'width': '100%',
                    'height': 'auto',
                    'maxHeight': '100vh'
                });

                // Find and disable overlays
                const overlays = document.querySelectorAll(`
                    .x1ey2m1c.x78zum5.xdt5ytf.xozqiw3.x17qophe.x13a6bvl.x10l6tqk,
                    [role="presentation"],
                    [data-pagelet],
                    [data-visualcompletion]
                `);

                overlays.forEach(overlay => {
                    if (!overlay.contains(video)) {
                        overlay.style.pointerEvents = 'none';
                        overlay.style.opacity = '0.01';
                    }
                });

                // Ensure video is in view
                video.scrollIntoViewIfNeeded();
            } catch (e) {
                console.error('Error in enableReelControls:', e);
            }
        }, 100);
    }

    // Handle play events
    function handlePlayEvent(event) {
        try {
            const target = event.target;
            if (!(target instanceof HTMLVideoElement)) return;

            enableReelControls(target);
            attemptUnmute(target);

            // Additional check for muted state after a short delay
            setTimeout(() => attemptUnmute(target), 500);
        } catch (e) {
            console.error('Error in handlePlayEvent:', e);
        }
    }

    // Handle click events for mute/unmute buttons
    function handleClickEvent(event) {
        try {
            const target = event.target;
            const isMuteButton = target.closest(`
                [aria-label*="mute"],
                [aria-label*="sound"],
                [role="button"][aria-pressed],
                [aria-label*="sonido"],
                [aria-label*="silenciar"]
            `);

            if (isMuteButton) {
                setTimeout(() => {
                    document.querySelectorAll('video').forEach(attemptUnmute);
                }, 100);
            }
        } catch (e) {
            console.error('Error in handleClickEvent:', e);
        }
    }

    // Prevent pausing when switching tabs
    function handleVisibilityChange() {
        try {
            // Force visible state
            Object.defineProperty(document, 'visibilityState', {
                get: () => 'visible',
                configurable: true
            });

            Object.defineProperty(document, 'hidden', {
                get: () => false,
                configurable: true
            });

            // Resume any paused videos
            document.querySelectorAll('video').forEach(video => {
                if (video.paused && video.readyState >= 2) {
                    video.play().catch(e => console.log('Auto-play failed:', e));
                }
            });
        } catch (e) {
            console.error('Error in handleVisibilityChange:', e);
        }
    }

    // Override pause method
    function initPauseHandler() {
        const originalPause = HTMLMediaElement.prototype.pause;
        HTMLMediaElement.prototype.pause = function() {
            try {
                if (this.closest('[href*="reel"], [data-pagelet*="reel"], [class*="reel"]')) {
                    return; // Prevent reels from pausing
                }
                return originalPause.apply(this, arguments);
            } catch (e) {
                console.error('Error in pause handler:', e);
                return originalPause.apply(this, arguments);
            }
        };
        return originalPause;
    }

    // Initialize
    function init() {
        try {
            console.log('Facebook Reels Enhancer initialized');

            // Add styles first
            addStyles();

            // Set up pause handler
            const originalPause = initPauseHandler();

            // Set up event listeners
            document.addEventListener('play', handlePlayEvent, true);
            document.addEventListener('click', handleClickEvent, true);
            document.addEventListener('visibilitychange', handleVisibilityChange, true);

            // Set up MutationObserver for dynamically loaded content
            const observer = new MutationObserver(mutations => {
                for (const mutation of mutations) {
                    if (mutation.addedNodes) {
                        mutation.addedNodes.forEach(node => {
                            // Check if node is a video
                            if (node.nodeName === 'VIDEO') {
                                enableReelControls(node);
                                attemptUnmute(node);
                            }
                            // Check for video elements within added nodes
                            else if (node.querySelectorAll) {
                                const videos = node.querySelectorAll('video');
                                videos.forEach(video => {
                                    enableReelControls(video);
                                    attemptUnmute(video);
                                });
                            }
                        });
                    }
                }
            });

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

            // Initial check for videos
            document.querySelectorAll('video').forEach(video => {
                enableReelControls(video);
                attemptUnmute(video);
            });

            // Clean up on page unload
            window.addEventListener('beforeunload', () => {
                document.removeEventListener('play', handlePlayEvent, true);
                document.removeEventListener('click', handleClickEvent, true);
                document.removeEventListener('visibilitychange', handleVisibilityChange, true);
                observer.disconnect();
                HTMLMediaElement.prototype.pause = originalPause;
            });
        } catch (e) {
            console.error('Error initializing Facebook Reels Enhancer:', e);
        }
    }

    // Start the script
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        // If document is already loaded, wait a bit to ensure everything is ready
        setTimeout(init, 500);
    }
})();