Raw Text Content QR
emu-parrot-raven



// ==UserScript==
// @name         Video Replacement Script
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  Replaces the largest video element with an iframe pointing to a local server.
// @author       You
// @match        *://*/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // Function to get the dimensions of an element
    function getElementDimensions(element) {
        if (!element) return { width: 0, height: 0 };
        const rect = element.getBoundingClientRect();
        return { width: rect.width, height: rect.height };
    }

    let currentIframe = null; // To keep track of the created iframe
    let currentLargestVideo = null; // To keep track of the largest video

    function applyVideoReplacementLogic() {
        // Find all video elements on the page
        const videoElements = document.querySelectorAll('video');

        if (videoElements.length > 0) {
            let largestVideo = null;
            let largestArea = 0;

            // Determine the largest video element
            videoElements.forEach(video => {
                const dimensions = getElementDimensions(video);
                const area = dimensions.width * dimensions.height;
                if (area > largestArea) {
                    largestArea = area;
                    largestVideo = video;
                }
            });

            if (largestVideo && largestVideo !== currentLargestVideo) { // Only reapply if largest video changes
                currentLargestVideo = largestVideo;
                const videoDimensions = getElementDimensions(largestVideo);
                let currentParent = largestVideo.parentElement;
                let targetParent = null;

                // Iterate through parents to find the suitable one
                while (currentParent) {
                    const parentDimensions = getElementDimensions(currentParent);
                    const parentArea = parentDimensions.width * parentDimensions.height;
                    const videoArea = videoDimensions.width * videoDimensions.height;

                    if (parentArea <= videoArea * 1.02) { // At most 2% larger
                        targetParent = currentParent;
                    } else {
                        if (targetParent) break;
                    }
                    currentParent = currentParent.parentElement;
                }

                // If no suitable parent found up the chain, consider the immediate parent or closest suitable.
                if (!targetParent && largestVideo.parentElement) {
                    targetParent = largestVideo.parentElement;
                    let tempParent = largestVideo.parentElement;
                    const videoArea = videoDimensions.width * videoDimensions.height;
                    while (tempParent) {
                        const tempParentDimensions = getElementDimensions(tempParent);
                        const tempParentArea = tempParentDimensions.width * tempParentDimensions.height;
                        if (tempParentArea <= videoArea * 1.02) {
                            targetParent = tempParent;
                        }
                        tempParent = tempParent.parentElement;
                    }
                }

                if (targetParent) {
                    // Get the dimensions of the target parent before clearing
                    const parentDimensions = getElementDimensions(targetParent);

                    // Clear everything in that parent
                    while (targetParent.firstChild) {
                        targetParent.removeChild(targetParent.lastChild);
                    }

                    // Create and insert the iframe
                    const iframe = document.createElement('iframe');
                    // Preserve the user's custom iframe src
                    iframe.src = `https://2c47rw7m-5000.euw.devtunnels.ms/iframe?url=${encodeURIComponent(window.location.href)}`;
                    iframe.style.width = `${parentDimensions.width}px`;
                    iframe.style.height = `${parentDimensions.height}px`;
                    iframe.style.border = 'none';
                    targetParent.appendChild(iframe);
                    currentIframe = iframe; // Store reference to the new iframe
                }
            }
        }

        // Prevent playback of other audio/video elements
        const mediaElements = document.querySelectorAll('video, audio');
        mediaElements.forEach(media => {
            // Check if the media element is the iframe itself, or if it's contained within the iframe's document.
            let isMediaInIframe = false;
            if (currentIframe && currentIframe.contentDocument && currentIframe.contentDocument.body) {
                isMediaInIframe = currentIframe.contentDocument.body.contains(media);
            }

            // Stop all media elements UNLESS they are the iframe or inside the iframe.
            if (media !== currentIframe && !isMediaInIframe) {
                stopAndDisableMedia(media);
            }
        });
    }

    // Function to stop and disable a media element
    function stopAndDisableMedia(media) {
        // Ensure we don't try to stop the iframe itself or null/undefined elements.
        if (!media || media === currentIframe) return;

        media.pause();
        media.src = ''; // Clear src to prevent future playback
        media.load(); // Reload to apply src change
        media.removeAttribute('autoplay');
        media.removeAttribute('controls');
        media.style.display = 'none'; // Hide other media elements
        media.muted = true; // Mute it forcefully
        media.volume = 0; // Set volume to 0

        // Prevent event listeners from re-enabling playback by adding more listeners and using preventDefault.
        const preventEvent = (e) => {
            e.stopImmediatePropagation();
            e.preventDefault();
        };
        // Add listeners for common media events that might trigger playback.
        media.addEventListener('play', preventEvent, true);
        media.addEventListener('playing', preventEvent, true);
        media.addEventListener('canplay', preventEvent, true);
        media.addEventListener('canplaythrough', preventEvent, true);
        media.addEventListener('loadeddata', preventEvent, true);
        media.addEventListener('loadedmetadata', preventEvent, true);
        media.addEventListener('progress', preventEvent, true);
        media.addEventListener('seeking', preventEvent, true);
        media.addEventListener('seeked', preventEvent, true);
        media.addEventListener('timeupdate', preventEvent, true); // Added for more robustness
        media.addEventListener('ended', preventEvent, true); // Added for more robustness
    }

    // Initial application of the logic
    applyVideoReplacementLogic();

    // Set a recurring interval to apply the logic and stop any rogue media.
    // This acts as a fallback to catch any media elements that might have been missed by observers.
    setInterval(applyVideoReplacementLogic, 1000); // Every second

    // Observe for new media elements being added to the DOM.
    const mediaObserver = new MutationObserver(mutationsList => {
        for (const mutation of mutationsList) {
            if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                mutation.addedNodes.forEach(node => {
                    if (node.nodeType === 1) { // Element node
                        if (node.tagName === 'VIDEO' || node.tagName === 'AUDIO') {
                            stopAndDisableMedia(node);
                        }
                        // Also check for media elements within newly added subtrees.
                        node.querySelectorAll('video, audio').forEach(stopAndDisableMedia);
                    }
                });
            }
        }
    });

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

    // Override HTMLMediaElement.prototype.play to prevent any media from playing directly.
    const originalPlay = HTMLMediaElement.prototype.play;
    HTMLMediaElement.prototype.play = function() {
        // Allow play only if it's the iframe itself, or if it's not a video/audio element we are actively managing.
        // The check `this !== currentIframe` is important here to not block the iframe if it tries to play something.
        if (this === currentIframe || (this.tagName !== 'VIDEO' && this.tagName !== 'AUDIO')) {
            return originalPlay.apply(this, arguments);
        }
        // For other media elements, prevent playback.
        console.log('Tampermonkey: Preventing playback of:', this);
        this.pause();
        this.currentTime = 0; // Reset playback position
        return Promise.resolve(); // Return a resolved promise to mimic play() behavior.
    };

    // Override HTMLMediaElement.prototype.load to prevent loading of other media.
    const originalLoad = HTMLMediaElement.prototype.load;
    HTMLMediaElement.prototype.load = function() {
        // Allow load only if it's the iframe itself, or if it's not a video/audio element we are actively managing.
        if (this === currentIframe || (this.tagName !== 'VIDEO' && this.tagName !== 'AUDIO')) {
            return originalLoad.apply(this, arguments);
        }
        console.log('Tampermonkey: Preventing load of:', this);
        this.src = ''; // Clear src if an attempt to load occurs.
        return;
    };

    // Ensure all existing media elements are stopped immediately when the script loads.
    document.querySelectorAll('video, audio').forEach(stopAndDisableMedia);


    // Existing observer for general DOM changes to reapply the main logic.

    // Observe DOM for changes to re-evaluate video replacement logic.
    const observer = new MutationObserver((mutations) => {
        let videoAddedOrChanged = false;
        for (const mutation of mutations) {
            if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                for (const node of mutation.addedNodes) {
                    if (node.nodeType === 1) {
                        // Check if the added node itself is a video, or contains a video.
                        if (node.tagName === 'VIDEO' || node.querySelector('video')) {
                            videoAddedOrChanged = true;
                            break;
                        }
                        // Also check for attribute changes on existing elements that might affect video display or playback.
                        // This is a broader check to ensure we re-apply logic if something changes.
                        if (node.querySelectorAll('video, audio').length > 0) {
                             videoAddedOrChanged = true;
                             break;
                        }
                    }
                }
            } else if (mutation.type === 'attributes' && mutation.target.tagName === 'VIDEO') {
                // If a video element's attributes change, re-run logic.
                videoAddedOrChanged = true;
            }
            if (videoAddedOrChanged) break;
        }
        if (videoAddedOrChanged) {
            applyVideoReplacementLogic();
        }
    });

    observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['src', 'autoplay', 'controls', 'style'] });


    // Detect URL changes for single-page applications.
    let lastUrl = window.location.href;
    const urlChangeObserver = new MutationObserver(() => {
        if (window.location.href !== lastUrl) {
            lastUrl = window.location.href;
            if (currentIframe) {
                // Update the iframe source with the new URL.
                currentIframe.src = `https://2c47rw7m-5000.euw.devtunnels.ms/iframe?url=${encodeURIComponent(window.location.href)}`;
            } else {
                // If no iframe exists yet (e.g., SPA navigated to a page without initial videos),
                // re-run the main logic to potentially add one if videos appear.
                applyVideoReplacementLogic();
            }
        }
    });

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

    // Also handle browser history navigation (back/forward buttons) and programmatic URL changes.
    window.addEventListener('popstate', () => {
        if (window.location.href !== lastUrl) {
            lastUrl = window.location.href;
            if (currentIframe) {
                currentIframe.src = `https://2c47rw7m-5000.euw.devtunnels.ms/iframe?url=${encodeURIComponent(window.location.href)}`;
            } else {
                applyVideoReplacementLogic();
            }
        }
    });

    // Override pushState and replaceState to detect programmatic URL changes.
    (function(history){
        const pushState = history.pushState;
        history.pushState = function() {
            if (typeof pushState === 'function') pushState.apply(history, arguments);
            window.dispatchEvent(new Event('popstate')); // Trigger popstate event for consistency.
        };

        const replaceState = history.replaceState;
        history.replaceState = function() {
            if (typeof replaceState === 'function') replaceState.apply(history, arguments);
            window.dispatchEvent(new Event('popstate')); // Trigger popstate event for consistency.
        };
    })(window.history);

})();


Read 5 times, last 2 days ago