AniwatchTV - Player Isolation and Anti-Redirect

Pauses all scripts except the video player on AniwatchTV and prevents redirects

// ==UserScript==
// @name         AniwatchTV - Player Isolation and Anti-Redirect
// @namespace    https://aniwatchtv.to/
// @version      121.V121.Aniwatch.V3000
// @description  Pauses all scripts except the video player on AniwatchTV and prevents redirects
// @author       [NotYou](Gabriel Underwood)
// @match        https://aniwatchtv.to/watch/*
// @match        http://aniwatchtv.to/watch/*
// @grant        unsafeWindow
// @run-at       document-start
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';
    
    console.log('AniwatchTV Player Isolation Script initialized');
    
    // Store original functions that we'll override
    const originalSetTimeout = window.setTimeout;
    const originalSetInterval = window.setInterval;
    const originalRequestAnimationFrame = window.requestAnimationFrame;
    const originalAddEventListener = EventTarget.prototype.addEventListener;
    const originalPushState = History.prototype.pushState;
    const originalReplaceState = History.prototype.replaceState;
    const originalAssign = window.location.assign;
    const originalReplace = window.location.replace;
    const originalOpen = window.open;
    
    // Known player elements based on provided HTML
    const PLAYER_SELECTORS = [
        '.player-frame',
        '#iframe-embed',
        '#embed-loading',
        '.loading-relative',
        '.loading-box'
    ];
    
    // Track if we've found and protected the player
    let playerProtected = false;
    let redirectsPrevented = 0;
    
    // Function to check if element is part of the video player
    function isPlayerElement(element) {
        if (!element) return false;
        
        // Check if element matches our known player selectors
        for (const selector of PLAYER_SELECTORS) {
            if (element.matches && element.matches(selector)) {
                return true;
            }
            
            // Check for specific ID
            if (selector.startsWith('#') && element.id === selector.substring(1)) {
                return true;
            }
            
            // Check for specific class
            if (selector.startsWith('.') && element.classList && 
                element.classList.contains(selector.substring(1))) {
                return true;
            }
        }
        
        // Check if element is the iframe with src containing player
        if (element.tagName === 'IFRAME' && element.id === 'iframe-embed') {
            return true;
        }
        
        // Check if it's a loading indicator for the player
        if (element.classList && 
            (element.classList.contains('loading') || 
             element.classList.contains('loading-relative') ||
             element.classList.contains('loading-box'))) {
            return true;
        }
        
        // Check if it's a direct child of player-frame
        if (element.parentElement && 
            element.parentElement.classList &&
            element.parentElement.classList.contains('player-frame')) {
            return true;
        }
        
        return false;
    }
    
    // Check if element is part of the player or an ancestor of the player
    function isPlayerOrAncestor(element) {
        if (isPlayerElement(element)) return true;
        
        // Check ancestors
        let parent = element.parentElement;
        while (parent) {
            if (isPlayerElement(parent)) return true;
            parent = parent.parentElement;
        }
        
        return false;
    }
    
    // Function to show notification
    function showNotification(message, duration = 3000) {
        if (!document.body) {
            setTimeout(() => showNotification(message, duration), 100);
            return;
        }
        
        let notification = document.getElementById('aniwatch-script-notification');
        if (!notification) {
            notification = document.createElement('div');
            notification.id = 'aniwatch-script-notification';
            notification.style.position = 'fixed';
            notification.style.top = '20px';
            notification.style.right = '20px';
            notification.style.backgroundColor = 'rgba(0, 0, 0, 0.8)';
            notification.style.color = 'white';
            notification.style.padding = '12px 16px';
            notification.style.borderRadius = '6px';
            notification.style.zIndex = '9999999';
            notification.style.fontFamily = 'Arial, sans-serif';
            notification.style.fontSize = '14px';
            notification.style.maxWidth = '300px';
            notification.style.boxShadow = '0 4px 8px rgba(0,0,0,0.2)';
            document.body.appendChild(notification);
        }
        
        notification.textContent = message;
        notification.style.display = 'block';
        
        // Hide after duration
        setTimeout(() => {
            notification.style.display = 'none';
        }, duration);
    }
    
    // Block redirects to home page
    function blockRedirectToHome(url) {
        if (!url) return false;
        
        try {
            const urlObj = new URL(url, window.location.origin);
            // Check if it's trying to redirect to homepage
            if (urlObj.pathname === '/' || 
                urlObj.pathname === '/home' || 
                urlObj.pathname.toLowerCase().includes('home')) {
                redirectsPrevented++;
                return true; // Should block
            }
        } catch (e) {
            // If URL parsing fails, be cautious and don't block
            return false;
        }
        
        return false; // Don't block
    }
    
    // Override setTimeout to control script execution
    window.setTimeout = function(callback, timeout, ...args) {
        // If it's a string callback, check for problematic redirects
        if (typeof callback === 'string') {
            if (callback.includes('location') || 
                callback.includes('redirect') || 
                callback.includes('window.location')) {
                return Math.floor(Math.random() * 10000); // Fake timeout ID
            }
        }
        
        // For function callbacks, create a wrapper
        if (typeof callback === 'function') {
            const wrappedCallback = function() {
                // Detect if this is executed in player context
                const isPlayerContext = isPlayerOrAncestor(this);
                
                // If it's player-related or we're in an iframe that's the player
                if (isPlayerContext || window.self !== window.top) {
                    return callback.apply(this, args);
                }
                
                // Block potentially problematic callbacks
                return null;
            };
            
            return originalSetTimeout.call(this, wrappedCallback, timeout);
        }
        
        return originalSetTimeout.apply(this, arguments);
    };
    
    // Override setInterval similar to setTimeout
    window.setInterval = function(callback, timeout, ...args) {
        // If it's a string callback, check for problematic redirects
        if (typeof callback === 'string') {
            if (callback.includes('location') || 
                callback.includes('redirect') || 
                callback.includes('window.location')) {
                return Math.floor(Math.random() * 10000); // Fake interval ID
            }
        }
        
        // For function callbacks, create a wrapper
        if (typeof callback === 'function') {
            const wrappedCallback = function() {
                // Detect if this is executed in player context
                const isPlayerContext = isPlayerOrAncestor(this);
                
                // If it's player-related or we're in an iframe that's the player
                if (isPlayerContext || window.self !== window.top) {
                    return callback.apply(this, args);
                }
                
                // Block potentially problematic callbacks
                return null;
            };
            
            return originalSetInterval.call(this, wrappedCallback, timeout);
        }
        
        return originalSetInterval.apply(this, arguments);
    };
    
    // Override history methods to prevent navigation
    History.prototype.pushState = function(state, title, url) {
        if (blockRedirectToHome(url)) {
            console.log('Blocked pushState redirect to:', url);
            return;
        }
        return originalPushState.apply(this, arguments);
    };
    
    History.prototype.replaceState = function(state, title, url) {
        if (blockRedirectToHome(url)) {
            console.log('Blocked replaceState redirect to:', url);
            return;
        }
        return originalReplaceState.apply(this, arguments);
    };
    
    // Override location methods
    const originalLocationAssign = Location.prototype.assign;
    Location.prototype.assign = function(url) {
        if (blockRedirectToHome(url)) {
            console.log('Blocked location.assign redirect to:', url);
            return;
        }
        return originalLocationAssign.call(this, url);
    };
    
    const originalLocationReplace = Location.prototype.replace;
    Location.prototype.replace = function(url) {
        if (blockRedirectToHome(url)) {
            console.log('Blocked location.replace redirect to:', url);
            return;
        }
        return originalLocationReplace.call(this, url);
    };
    
    // Override window.open
    window.open = function(url) {
        if (url && typeof url === 'string' && url.includes('home')) {
            console.log('Blocked window.open redirect to:', url);
            redirectsPrevented++;
            return null;
        }
        return originalOpen.apply(this, arguments);
    };
    
    // Block setting location directly
    Object.defineProperty(window, 'location', {
        get: function() {
            return window.location;
        },
        set: function(url) {
            if (typeof url === 'string' && url.includes('home')) {
                console.log('Blocked direct location change to:', url);
                redirectsPrevented++;
                return window.location;
            }
            window.location.href = url;
            return window.location;
        }
    });
    
    // Find and protect the player once DOM is ready
    function findAndProtectPlayer() {
        console.log('Looking for video player to protect...');
        
        // Specifically look for the player elements from the provided HTML
        const playerFrame = document.querySelector('.player-frame');
        const iframe = document.querySelector('#iframe-embed');
        
        if (playerFrame && iframe) {
            console.log('Found player elements to protect');
            playerProtected = true;
            
            // Make sure iframe src is properly set if empty
            if (!iframe.src || iframe.src === '') {
                // Try to find the correct source from page data
                // This is a common pattern where the iframe src is set by JS
                const scriptTags = document.querySelectorAll('script:not([src])');
                let possibleSrc = '';
                
                scriptTags.forEach(script => {
                    const content = script.textContent;
                    if (content.includes('iframe-embed') && content.includes('src')) {
                        const match = content.match(/["']iframe-embed["']\.src\s*=\s*["']([^"']+)["']/);
                        if (match && match[1]) {
                            possibleSrc = match[1];
                        }
                    }
                });
                
                if (possibleSrc) {
                    console.log('Setting iframe src to:', possibleSrc);
                    iframe.src = possibleSrc;
                }
            }
            
            // Enhance player visibility
            playerFrame.style.zIndex = '9999';
            iframe.style.width = '100%';
            iframe.style.height = '100%';
            iframe.style.minHeight = '500px';
            iframe.setAttribute('allowfullscreen', 'true');
            
            showNotification('Player protected. Blocking all other scripts.', 5000);
        } else {
            console.log('Player elements not found yet');
            
            // If not found, try again in a moment
            setTimeout(findAndProtectPlayer, 500);
        }
    }
    
    // Add style to hide annoying elements and ensure player visibility
    function addStyles() {
        const style = document.createElement('style');
        style.textContent = `
            /* Hide potential ad elements */
            div[class*="ads"], div[id*="ads"],
            div[class*="ad-"], div[id*="ad-"],
            div[class*="-ad"], div[id*="-ad"],
            div[class*="banner"], div[id*="banner"],
            div[class*="popup"], div[id*="popup"] {
                display: none !important;
            }
            
            /* Make sure player is visible */
            .player-frame {
                display: block !important;
                visibility: visible !important;
                opacity: 1 !important;
                position: relative !important;
                z-index: 9999 !important;
                min-height: 500px !important;
            }
            
            #iframe-embed {
                display: block !important;
                visibility: visible !important;
                opacity: 1 !important;
                width: 100% !important;
                height: 100% !important;
                min-height: 500px !important;
            }
        `;
        
        // Add style to head
        const head = document.head || document.getElementsByTagName('head')[0];
        if (head) {
            head.appendChild(style);
        } else {
            // If head isn't available yet, wait and try again
            setTimeout(addStyles, 100);
        }
    }
    
    // Monitor status and display periodic updates
    function startStatusMonitor() {
        setInterval(() => {
            if (redirectsPrevented > 0) {
                showNotification(`Prevented ${redirectsPrevented} redirect attempts`);
                redirectsPrevented = 0;
            }
        }, 10000);
    }
    
    // Initialize everything
    function initialize() {
        // Add styles immediately
        addStyles();
        
        // Start finding and protecting player once DOM starts loading
        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', findAndProtectPlayer);
        } else {
            findAndProtectPlayer();
        }
        
        // Start status monitor
        startStatusMonitor();
        
        console.log('AniwatchTV Player Isolation Script fully initialized');
    }
    
    // Execute initialization
    initialize();
})();