YouTube 30 FPS Forzado

Fuerza YouTube a reproducir videos a 30 FPS máximo, reduciendo el uso de CPU y mejorando el rendimiento en equipos antiguos

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name            YouTube 30 FPS Forzado
// @name:en         YouTube 30 FPS Forced
// @namespace       AkioBrian
// @version         2.0.0
// @description     Fuerza YouTube a reproducir videos a 30 FPS máximo, reduciendo el uso de CPU y mejorando el rendimiento en equipos antiguos
// @description:en  Forces YouTube to play videos at a maximum of 30 FPS, reducing CPU usage and improving performance on older devices.
// @icon            https://i.imgur.com/oM51Lex.png
// @author          SteveJobzniak, AkioBrian
// @license         https://www.apache.org/licenses/LICENSE-2.0
// @match           *://www.youtube.com/*
// @exclude         *://www.youtube.com/tv*
// @exclude         *://www.youtube.com/embed/*
// @run-at          document-start
// @grant           none
// @noframes
// ==/UserScript==

(function() {
    'use strict';

    // Configuración
    const CONFIG = {
        DEBUG: false,
        MAX_FPS: 30,
        MAX_REINTENTOS: 2,
        TIMEOUT_VERIFICACION: 8000
    };

    // Crear verificador de formatos personalizado
    const crearVerificadorFormatos = (verificadorOriginal, callback) => {
        return function(tipoVideo) {
            if (CONFIG.DEBUG) {
                console.log(`[YouTube 30FPS] Consulta: "${tipoVideo}"`);
            }

            callback?.('query', tipoVideo);
            if (!tipoVideo) return false;

            // Bloquear formatos de alta frecuencia
            const coincidencias = tipoVideo.match(/framerate=(\d+)/);
            if (coincidencias) {
                const fps = parseInt(coincidencias[1], 10);
                if (fps > CONFIG.MAX_FPS) {
                    if (CONFIG.DEBUG) {
                        console.log(`[YouTube 30FPS] Bloqueado: ${fps}fps`);
                    }
                    callback?.('block', tipoVideo);
                    return false;
                }
            }

            return verificadorOriginal(tipoVideo);
        };
    };

    // Verificar si estamos en una página de video
    const esPaginaVideo = () => location.pathname === '/watch';

    // Obtener contador de recargas de la URL
    const obtenerContadorRecargas = () => {
        if (!location.hash) return null;
        const coincidencias = location.hash.match(/fpsreloads=(\d+)/);
        return coincidencias ? parseInt(coincidencias[1], 10) : null;
    };

    // Establecer contador y recargar página
    const establecerContadorYRecargar = (nuevoContador) => {
        let hashAntiguo = location.hash.replace(/^#/, '').replace(/&?fpsreloads=\d+/, '');
        let hashNuevo = hashAntiguo ? `${hashAntiguo}&fpsreloads=${nuevoContador}` : `fpsreloads=${nuevoContador}`;

        location.hash = hashNuevo;
        location.reload();
    };

    // Eliminar contador de recargas de la URL
    const eliminarContadorRecargas = () => {
        if (window.history?.replaceState) {
            const nuevaUrl = location.href.replace(/(#?)fpsreloads=\d+&?/g, '$1').replace(/[#&]+$/, '');
            if (location.href !== nuevaUrl) {
                window.history.replaceState({}, document.title, nuevaUrl);
            }
        }
    };

    // Manejar reintentos de inyección
    const manejarReintentos = (exitoso, recargasActuales) => {
        if (exitoso || !esPaginaVideo()) {
            eliminarContadorRecargas();
            if (CONFIG.DEBUG) {
                console.log('[YouTube 30FPS] Inyección exitosa');
            }
            return;
        }

        const siguienteRecarga = (recargasActuales ?? 0) + 1;

        if (siguienteRecarga <= CONFIG.MAX_REINTENTOS) {
            if (CONFIG.DEBUG) {
                console.log(`[YouTube 30FPS] Reintento ${siguienteRecarga}/${CONFIG.MAX_REINTENTOS}`);
            }
            establecerContadorYRecargar(siguienteRecarga);
        } else {
            eliminarContadorRecargas();
            mostrarMensajeError();
        }
    };

    // Mostrar mensaje de error con auto-ocultado
    const mostrarMensajeError = () => {
        const crearDivError = () => {
            if (!document.body || document.getElementById('youtube-30fps-error')) {
                return false;
            }

            const divError = document.createElement('div');
            divError.id = 'youtube-30fps-error';

            Object.assign(divError.style, {
                position: 'fixed',
                bottom: '20px',
                right: '20px',
                maxWidth: '350px',
                padding: '15px',
                borderRadius: '8px',
                fontSize: '14px',
                color: '#fff',
                backgroundColor: 'rgba(244, 67, 54, 0.95)',
                boxShadow: '0 4px 12px rgba(0,0,0,0.3)',
                zIndex: '99999',
                fontFamily: 'Arial, sans-serif',
                lineHeight: '1.4'
            });

            divError.innerHTML = `
                <div style="font-weight: bold; margin-bottom: 8px;">⚠️ YouTube 30 FPS</div>
                <div style="margin-bottom: 10px;">No se pudo limitar a 30 FPS en esta pestaña.</div>
                <div style="font-size: 12px;">
                    <a href="#" onclick="location.reload(); return false"
                       style="color: #ffcdd2; text-decoration: underline;">Recargar página</a> •
                    <a href="#" onclick="this.parentElement.parentElement.parentElement.remove(); return false"
                       style="color: #ffcdd2; text-decoration: underline;">Cerrar</a>
                </div>
            `;

            document.body.appendChild(divError);

            // Auto-ocultar después de 10 segundos
            setTimeout(() => {
                if (divError.parentNode) {
                    divError.style.transition = 'opacity 0.5s ease-out';
                    divError.style.opacity = '0';
                    setTimeout(() => divError.remove(), 500);
                }
            }, 10000);

            return true;
        };

        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', crearDivError);
        } else {
            crearDivError();
        }
    };

    // Verificar soporte para MediaSource
    if (!window.MediaSource) {
        console.warn('MediaSource no soportado en este navegador');
        return;
    }

    const recargasActuales = obtenerContadorRecargas();

    // Verificar si la inyección fue demasiado tardía
    const inyeccionTardia = window.ytplayer && Object.getOwnPropertyNames(window.ytplayer).length > 0;

    if (esPaginaVideo() && inyeccionTardia) {
        manejarReintentos(false, recargasActuales);
        return;
    }

    // Limpiar contador de recargas si la inyección parece exitosa
    eliminarContadorRecargas();

    // Configurar verificación de éxito
    let contadorBloqueos = 0;
    const actualizarContadorBloqueos = (accion) => {
        if (accion === 'block') contadorBloqueos++;
    };

    const verificarExito = () => {
        const exitoso = contadorBloqueos >= 1;
        manejarReintentos(exitoso, recargasActuales);
    };

    // Configurar timer de verificación para páginas de video
    if (esPaginaVideo()) {
        setTimeout(verificarExito, CONFIG.TIMEOUT_VERIFICACION);
    } else {
        manejarReintentos(true, recargasActuales);
    }

    // Inyectar el bloqueador de formatos de video
    try {
        const verificadorOriginal = window.MediaSource.isTypeSupported.bind(window.MediaSource);
        window.MediaSource.isTypeSupported = crearVerificadorFormatos(verificadorOriginal, actualizarContadorBloqueos);

        if (CONFIG.DEBUG) {
            console.log('[YouTube 30FPS] Script inyectado correctamente');
        }
    } catch (error) {
        console.error('[YouTube 30FPS] Error al inyectar:', error);
    }
})();