Greasy Fork is available in English.

YouTube Go

Bloquea la carga de varios apartados poco usados para optimizar la carga y el peso de la web.

// ==UserScript==
// @name         YouTube Go
// @namespace    UserScripts
// @version      2.0.1
// @license      MIT License
// @author       AkioBrian
// @icon         https://i.imgur.com/Y93ppBt.png
// @description  Bloquea la carga de varios apartados poco usados para optimizar la carga y el peso de la web.
// @match        https://www.youtube.com/*
// @exclude      https://www.youtube.com/live_chat*
// @exclude      https://www.youtube.com/live_chat_replay*
// @run-at       document-start
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // ===============================
    // === CSS ULTRA-RÁPIDO (ANTES DEL RENDERIZADO) ===
    // ===============================
    // Inyectar CSS inmediatamente para ocultar elementos antes de que se rendericen
    const style = document.createElement('style');
    style.textContent = `
        /* Ocultar sidebar inmediatamente */
        #secondary,
        ytd-watch-next-secondary-results-renderer,
        ytd-compact-video-renderer,
        #related,
        .ytd-watch-next-secondary-results-renderer {
            display: none !important;
        }

        /* Ocultar botones específicos - más selectores */
        button[aria-label*="No me gusta"],
        button[aria-label*="Descargar"],
        button[aria-label*="Gracias"],
        button[aria-label*="Recortar"],
        button[aria-label*="Denunciar"],
        ytd-button-renderer[aria-label*="No me gusta"],
        ytd-button-renderer[aria-label*="Descargar"],
        ytd-button-renderer[aria-label*="Gracias"],
        ytd-button-renderer[aria-label*="Recortar"],
        ytd-menu-service-item-renderer[aria-label*="Descargar"],
        ytd-menu-service-item-renderer[aria-label*="Denunciar"],
        /* Selectores por contenido de texto */
        button:has(yt-formatted-string[title*="No me gusta"]),
        button:has(yt-formatted-string[title*="Descargar"]),
        button:has(yt-formatted-string[title*="Gracias"]),
        button:has(yt-formatted-string[title*="Recortar"]) {
            display: none !important;
        }
    `;

    // Inyectar CSS tan pronto como sea posible
    if (document.head) {
        document.head.appendChild(style);
    } else {
        // Si head no existe aún, esperar a que se cree
        const headObserver = new MutationObserver(() => {
            if (document.head) {
                document.head.appendChild(style);
                headObserver.disconnect();
            }
        });
        headObserver.observe(document.documentElement, { childList: true });
    }

    // ===============================
    // === INTERCEPTAR RESPUESTAS DE RED ===
    // ===============================
    // Interceptar fetch para modificar respuestas antes del renderizado
    const originalFetch = window.fetch;
    window.fetch = function(...args) {
        return originalFetch.apply(this, args).then(response => {
            const url = typeof args[0] === 'string' ? args[0] : args[0].url;

            // Interceptar respuestas de YouTube que contengan datos de la sidebar
            if (url.includes('next') || url.includes('watch') || url.includes('browse')) {
                return response.clone().text().then(data => {
                    try {
                        // Si es JSON, modificarlo
                        if (response.headers.get('content-type')?.includes('application/json')) {
                            let jsonData = JSON.parse(data);

                            // Remover datos de sidebar/recomendaciones
                            if (jsonData.contents?.twoColumnWatchNextResults?.secondaryResults) {
                                delete jsonData.contents.twoColumnWatchNextResults.secondaryResults;
                            }

                            return new Response(JSON.stringify(jsonData), {
                                status: response.status,
                                statusText: response.statusText,
                                headers: response.headers
                            });
                        }
                    } catch (e) {
                        // Si no es JSON válido, devolver respuesta original
                    }
                    return response;
                });
            }
            return response;
        });
    };

    // ===============================
    // === ESPERAR A QUE EL DOM ESTÉ LISTO ===
    // ===============================
    function initWhenReady() {
        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', initMain);
        } else {
            initMain();
        }
    }

    function initMain() {

        // ===============================
        // === LIMPIEZA ADICIONAL (FALLBACK) ===
        // ===============================
        // Por si algo se escapa del CSS, limpiar periodicamente
        function limpiezaCompleta() {
            // Eliminar sidebar
            const sidebar = document.querySelector('#secondary, ytd-watch-next-secondary-results-renderer');
            if (sidebar) {
                sidebar.remove();
                console.log('Sidebar eliminado (fallback).');
            }

            // Eliminar botones específicos
            const keywords = ["No me gusta", "Descargar", "Gracias", "Recortar", "Denunciar"];
            const botones = document.querySelectorAll('button, ytd-button-renderer, ytd-menu-service-item-renderer');

            botones.forEach(el => {
                const aria = el.getAttribute('aria-label') || "";
                const text = el.textContent || "";

                for (const palabra of keywords) {
                    if ((aria.includes(palabra) || text.includes(palabra)) && el.parentNode) {
                        el.remove();
                        console.log(`Botón eliminado (fallback): ${palabra}`);
                        break;
                    }
                }
            });
        }

        // Ejecutar limpieza inicial
        limpiezaCompleta();

        // Limpieza periódica menos agresiva (solo si es necesario)
        const cleanupObserver = new MutationObserver(() => {
            // Throttle para no ejecutar constantemente
            clearTimeout(cleanupObserver.timeout);
            cleanupObserver.timeout = setTimeout(limpiezaCompleta, 100);
        });
        cleanupObserver.observe(document.body, { childList: true, subtree: true });

    }

    // Iniciar el script
    initWhenReady();
})();