ExtTube

Replaces YouTube player with Invidious player and adds a "Watch on Invidious" button.

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         ExtTube
// @namespace    http://tampermonkey.net/
// @version      1.2
// @description  Replaces YouTube player with Invidious player and adds a "Watch on Invidious" button.
// @author       sypcerr
// @match        https://*.youtube.com/watch?*
// @icon         https://cdn-icons-png.flaticon.com/256/1384/1384060.png
// @run-at       document-start
// @license      MIT
// ==/UserScript==


const configs = ytcfg.d();
configs['WEB_PLAYER_CONTEXT_CONFIGS'] = {};
ytcfg.set(configs);

(function() {
    'use strict';

    const CONFIG = {
        PLAYER_SELECTOR: '#player-container[role="complementary"]',
        RETRY_ATTEMPTS: 3,
        RETRY_DELAY: 1000,
        DEFAULT_QUALITY: 'hd1080',
        IFRAME_STYLES: {
            width: '100%',
            height: '100%',
            borderRadius: '15px',
            border: 'none'
        }
    };

    function getVideoId() {
        try {
            const url = new URL(window.location.href);
            return url.searchParams.get('v');
        } catch (error) {
            return null;
        }
    }

    function createEnhancedIframe(videoId) {
        const iframe = document.createElement('iframe');
        const params = new URLSearchParams({
            autoplay: 1,
            rel: 0,
            modestbranding: 1,
            enablejsapi: 1,
            origin: window.location.origin,
            widget_referrer: window.location.href,
            hl: document.documentElement.lang || 'en',
            controls: 1,
            fs: 1,
            quality: CONFIG.DEFAULT_QUALITY
        });

        iframe.src = `https://www.youtube.com/embed/${videoId}?${params.toString()}`;
        iframe.title = 'YouTube video player';
        iframe.allow = 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share';
        iframe.referrerpolicy = 'strict-origin-when-cross-origin';
        iframe.allowFullscreen = true;

        Object.assign(iframe.style, CONFIG.IFRAME_STYLES);

        return iframe;
    }

    async function replacePlayer(attempts = CONFIG.RETRY_ATTEMPTS) {
        const videoId = getVideoId();
        if (!videoId) return;

        const findPlayerContainer = () => document.querySelector(CONFIG.PLAYER_SELECTOR);
        let playerContainer = findPlayerContainer();

        if (!playerContainer && attempts > 0) {
            console.log(`⏳ Retrying player replacement... (${attempts} attempts left)`);
            await new Promise(resolve => setTimeout(resolve, CONFIG.RETRY_DELAY));
            return replacePlayer(attempts - 1);
        }

        if (!playerContainer) {
            console.error('❌ Player container not found after all attempts');
            return;
        }

        try {
            while (playerContainer.firstChild) {
                playerContainer.firstChild.remove();
            }

            const iframe = createEnhancedIframe(videoId);
            playerContainer.appendChild(iframe);
            
            console.log('✅ Player replaced successfully');
        } catch (error) {
            console.error('❌ Error replacing player:', error);
        }
    }

    function stopMediaElements() {
        const mediaElements = document.querySelectorAll('video, audio');
        mediaElements.forEach((media) => {
            media.pause();
            media.currentTime = 0;
            media.remove();
        });
    
        if (window.AudioContext || window.webkitAudioContext) {
            const AudioContext = window.AudioContext || window.webkitAudioContext;
            const originalAudioContext = AudioContext.prototype;
    
            AudioContext.prototype.createBufferSource = function() {
                const source = originalAudioContext.createBufferSource.call(this);
                source.stop = function() {
                    console.log('🔇 Stopped Web Audio API source');
                    originalAudioContext.stop.call(this);
                };
                return source;
            };
    
            const contexts = [];
            const originalCreateContext = AudioContext.prototype.constructor;
            AudioContext.prototype.constructor = function() {
                const context = new originalCreateContext();
                contexts.push(context);
                return context;
            };
    
            contexts.forEach((context) => {
                context.close().then(() => {
                });
            });
        }
    
        if (window.MediaSource) {
            const originalMediaSource = window.MediaSource;
            window.MediaSource = function() {
                const mediaSource = new originalMediaSource();
                mediaSource.endOfStream = function() {
                    originalMediaSource.prototype.endOfStream.call(this);
                };
                return mediaSource;
            };
        }
    
        console.log('🔇 Stopped all audio and video elements, Web Audio API, and MediaSource');
    }

    function init() {
        stopMediaElements();
        window.addEventListener('yt-navigate-finish', () => replacePlayer());
    }

    init();
})();