X.com Media Gallery Slideshow with Timer and Video Support + Pause

Extract images and videos from X.com (Twitter) media gallery and play slideshow with controls, countdown timer and pause on spacebar

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

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

Necesitarás 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.

Necesitará instalar una extensión como Tampermonkey para 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)

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

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

// ==UserScript==
// @name         X.com Media Gallery Slideshow with Timer and Video Support + Pause
// @namespace    http://tampermonkey.net/
// @version      1.7
// @description  Extract images and videos from X.com (Twitter) media gallery and play slideshow with controls, countdown timer and pause on spacebar
// @author       OpenAI
// @match        https://twitter.com/*
// @match        https://x.com/*
// @grant        none
// @license MIT
// ==/UserScript==

(function () {
    'use strict';

    let slideshowOverlay = null;
    let slideshowMedia = []; // Array of objects {type: 'image'|'video', src: URL}
    let currentIndex = 0;
    let slideshowTimeout = null;
    let countdownInterval = null;
    let countdownSeconds = 10;
    let isPaused = false;
    let currentVideo = null;

    // Extract media URLs (images and videos) from X.com media gallery
    function extractMedia() {
        const seen = new Set();
        const media = [];

        // Images
        const imgElements = document.querySelectorAll('img[src*="pbs.twimg.com/media/"]');
        imgElements.forEach(img => {
            const baseUrl = img.src.split('?')[0];
            if (!seen.has(baseUrl)) {
                seen.add(baseUrl);
                // Images full size
                media.push({ type: 'image', src: baseUrl + '?format=jpg&name=large' });
            }
        });

        // Videos
        const videoElements = document.querySelectorAll('video');
        videoElements.forEach(video => {
            let src = video.currentSrc || video.src;
            if (src && !seen.has(src)) {
                seen.add(src);
                media.push({ type: 'video', src: src });
            }
        });

        return media;
    }

    function clearTimers() {
        clearTimeout(slideshowTimeout);
        clearInterval(countdownInterval);
    }

    // Create and show slideshow overlay
    function showSlideshow(mediaArray) {
        if (mediaArray.length === 0) {
            alert('No media found!');
            return;
        }

        if (slideshowOverlay) {
            slideshowOverlay.remove();
            clearTimers();
            document.removeEventListener('keydown', keyHandler);
        }

        slideshowMedia = mediaArray;
        currentIndex = 0;
        countdownSeconds = 10;
        isPaused = false;
        currentVideo = null;

        slideshowOverlay = document.createElement('div');
        slideshowOverlay.style.position = 'fixed';
        slideshowOverlay.style.top = '0';
        slideshowOverlay.style.left = '0';
        slideshowOverlay.style.width = '100vw';
        slideshowOverlay.style.height = '100vh';
        slideshowOverlay.style.backgroundColor = 'rgba(0,0,0,0.9)';
        slideshowOverlay.style.display = 'flex';
        slideshowOverlay.style.alignItems = 'center';
        slideshowOverlay.style.justifyContent = 'center';
        slideshowOverlay.style.flexDirection = 'column';
        slideshowOverlay.style.zIndex = '100000';

        const mediaContainer = document.createElement('div');
        mediaContainer.style.maxWidth = '90%';
        mediaContainer.style.maxHeight = '90%';
        mediaContainer.style.borderRadius = '10px';
        mediaContainer.style.boxShadow = '0 0 20px rgba(255, 255, 255, 0.7)';
        mediaContainer.style.display = 'flex';
        mediaContainer.style.alignItems = 'center';
        mediaContainer.style.justifyContent = 'center';
        slideshowOverlay.appendChild(mediaContainer);

        const timerDisplay = document.createElement('div');
        timerDisplay.style.color = 'white';
        timerDisplay.style.fontSize = '18px';
        timerDisplay.style.marginTop = '10px';
        timerDisplay.style.fontFamily = 'Arial, sans-serif';
        timerDisplay.textContent = `Next media in: ${countdownSeconds} seconds`;
        slideshowOverlay.appendChild(timerDisplay);

        const closeBtn = document.createElement('button');
        closeBtn.innerText = '✕';
        closeBtn.style.position = 'fixed';
        closeBtn.style.top = '20px';
        closeBtn.style.right = '20px';
        closeBtn.style.fontSize = '30px';
        closeBtn.style.background = 'transparent';
        closeBtn.style.color = 'white';
        closeBtn.style.border = 'none';
        closeBtn.style.cursor = 'pointer';
        closeBtn.style.zIndex = '100001';
        closeBtn.title = 'Close Slideshow';
        closeBtn.addEventListener('click', () => {
            clearTimers();
            slideshowOverlay.remove();
            slideshowOverlay = null;
            document.removeEventListener('keydown', keyHandler);
        });
        slideshowOverlay.appendChild(closeBtn);

        function loadMedia(index) {
            mediaContainer.innerHTML = '';
            currentVideo = null;
            const mediaObj = slideshowMedia[index];
            if (!mediaObj) return;

            if (mediaObj.type === 'image') {
                const img = document.createElement('img');
                img.src = mediaObj.src;
                img.style.maxWidth = '100%';
                img.style.maxHeight = '100%';
                img.style.borderRadius = '10px';
                mediaContainer.appendChild(img);

                countdownSeconds = 10;
                timerDisplay.textContent = `Next media in: ${countdownSeconds} seconds`;

                resetInterval(10000);
            } else if (mediaObj.type === 'video') {
                const video = document.createElement('video');
                video.src = mediaObj.src;
                video.style.maxWidth = '100%';
                video.style.maxHeight = '100%';
                video.style.borderRadius = '10px';
                video.autoplay = true;
                video.controls = true;
                video.loop = false;
                mediaContainer.appendChild(video);

                currentVideo = video;

                video.addEventListener('loadedmetadata', () => {
                    countdownSeconds = Math.ceil(video.duration);
                    timerDisplay.textContent = `Next media in: ${countdownSeconds} seconds`;
                    resetInterval(countdownSeconds * 1000);
                    if (isPaused) video.pause();
                });

                video.addEventListener('ended', () => {
                    nextMedia();
                });
            }
        }

        function showMedia(index) {
            currentIndex = (index + slideshowMedia.length) % slideshowMedia.length;
            loadMedia(currentIndex);
        }

        function nextMedia() {
            showMedia(currentIndex + 1);
        }

        function prevMedia() {
            showMedia(currentIndex - 1);
        }

        function resetInterval(duration) {
            clearTimers();

            countdownSeconds = Math.floor(duration / 1000);
            timerDisplay.textContent = `Next media in: ${countdownSeconds} seconds`;

            if (!isPaused) {
                slideshowTimeout = setTimeout(() => {
                    nextMedia();
                }, duration);

                countdownInterval = setInterval(() => {
                    if (!isPaused) {
                        countdownSeconds--;
                        if (countdownSeconds < 0) countdownSeconds = 0;
                        timerDisplay.textContent = `Next media in: ${countdownSeconds} seconds`;
                    }
                }, 1000);
            }
        }

        function togglePause() {
            isPaused = !isPaused;
            if (isPaused) {
                clearTimers();
                if (currentVideo && !currentVideo.paused) currentVideo.pause();
            } else {
                if (currentVideo && currentVideo.paused) currentVideo.play();
                resetInterval(countdownSeconds * 1000);
            }
        }

        function keyHandler(e) {
            if (!slideshowOverlay) return;

            if (e.key === 'ArrowRight') {
                nextMedia();
            } else if (e.key === 'ArrowLeft') {
                prevMedia();
            } else if (e.key === 'Escape') {
                closeBtn.click();
            } else if (e.code === 'Space') {
                e.preventDefault(); // prevent page scroll
                togglePause();
            }
        }

        document.addEventListener('keydown', keyHandler);

        showMedia(0);

        document.body.appendChild(slideshowOverlay);
    }

    function addButton() {
        if (document.getElementById('extract-media-btn')) return;

        const btn = document.createElement('button');
        btn.innerText = 'Start X.com Slideshow';
        btn.id = 'extract-media-btn';
        btn.style.position = 'fixed';
        btn.style.top = '10px';
        btn.style.right = '10px';
        btn.style.zIndex = '9999';
        btn.style.padding = '10px';
        btn.style.background = '#1da1f2';
        btn.style.color = 'white';
        btn.style.border = 'none';
        btn.style.borderRadius = '5px';
        btn.style.cursor = 'pointer';
        btn.title = 'Start slideshow';

        btn.addEventListener('click', () => {
            const media = extractMedia();
            showSlideshow(media);
        });

        document.body.appendChild(btn);
    }

    const observer = new MutationObserver(() => {
        addButton();
    });
    observer.observe(document.body, { childList: true, subtree: true });

    addButton();
})();