X.com Video Speed Controller (2x speed)

Automatically sets X.com videos to custom speed (default 2x)

// ==UserScript==
// @name         X.com Video Speed Controller (2x speed)
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Automatically sets X.com videos to custom speed (default 2x)
// @author       You
// @match        https://x.com/*
// @match        https://twitter.com/*
// @grant        none
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';
    
    // =============================================
    // Configure your desired video speed here (1.0 is normal speed)
    const PLAYBACK_SPEED = 2.0;
    // =============================================

    // Function to check if video is a live stream
    function isLiveVideo(video) {
        // X.com doesn't typically have live streams in the main feed
        // but we can check for common live indicators
        const videoContainer = video.closest('[data-testid="videoComponent"]') || video.closest('article');
        if (videoContainer) {
            const text = videoContainer.textContent.toLowerCase();
            return text.includes('live') || text.includes('streaming');
        }
        return false;
    }

    // Function to set video speed
    function setVideoSpeed(video) {
        if (!video) return;
        
        const isLive = isLiveVideo(video);
        const targetSpeed = isLive ? 1.0 : PLAYBACK_SPEED;
        
        // Set the playback rate
        video.playbackRate = targetSpeed;
        
        // Add a visual indicator (optional)
        if (!video.dataset.speedModified) {
            video.dataset.speedModified = 'true';
            console.log(`X Video Speed: Set to ${targetSpeed}x ${isLive ? '(Live)' : ''}`);
        }
    }

    // Function to process all videos on the page
    function processAllVideos() {
        const videos = document.querySelectorAll('video');
        videos.forEach(video => {
            if (!video.dataset.speedProcessed) {
                video.dataset.speedProcessed = 'true';
                setVideoSpeed(video);
                
                // Also set speed when video starts playing
                video.addEventListener('play', () => setVideoSpeed(video));
                video.addEventListener('loadedmetadata', () => setVideoSpeed(video));
            }
        });
    }

    // Initial processing
    setTimeout(processAllVideos, 1000);

    // Monitor for dynamically loaded videos (X loads content dynamically)
    const observer = new MutationObserver((mutations) => {
        let hasNewVideos = false;
        
        mutations.forEach((mutation) => {
            if (mutation.addedNodes.length) {
                mutation.addedNodes.forEach((node) => {
                    if (node.nodeType === Node.ELEMENT_NODE) {
                        // Check if the added node contains videos
                        const videos = node.querySelectorAll ? node.querySelectorAll('video') : [];
                        if (videos.length > 0 || node.nodeName === 'VIDEO') {
                            hasNewVideos = true;
                        }
                    }
                });
            }
        });
        
        if (hasNewVideos) {
            setTimeout(processAllVideos, 100);
        }
    });

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

    // Handle videos that start playing
    document.addEventListener('play', function(e) {
        if (e.target.nodeName === 'VIDEO') {
            setVideoSpeed(e.target);
        }
    }, true);

    // Process videos when page becomes visible (for tab switching)
    document.addEventListener('visibilitychange', function() {
        if (!document.hidden) {
            setTimeout(processAllVideos, 500);
        }
    });

    // Also process on scroll (X lazy loads content)
    let scrollTimeout;
    window.addEventListener('scroll', function() {
        clearTimeout(scrollTimeout);
        scrollTimeout = setTimeout(processAllVideos, 300);
    });

})();