您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Maximiza todos los reproductores de video. Soporta imagen en imagen.
当前为
// ==UserScript== // @name Maximizar Video (Mejorado) // @name:en Maximize Video (Improved) // @namespace https://github.com/ryomahan // @description Maximiza todos los reproductores de video. Soporta imagen en imagen. // @description:en Maximize all video players. Support Picture-in-picture. // @author 冻猫, ryomahan, AkioBrian // @icon https://i.imgur.com/3K7CBab.png // @include * // @version 13.0.1 // @run-at document-end // @license MIT // ==/UserScript== (() => { 'use strict'; // Variables globales const estado = { esPantallaCompleta: false, esIframe: false, conteoVerificacionAuto: 0, reproductor: null, padres: [], hijos: [], elementoSobreRaton: null, // Botones botonControl: null, botonPip: null, botonIzquierdo: null, botonDerecho: null, // Estados anteriores idHtmlAnterior: '', idBodyAnterior: '', controlesAnteriores: false, cambioEtapaYoutube: false, // Timers timerArregloScroll: null, timerOcultarBotones: null }; // Configuración de idioma const idioma = navigator.language.toLowerCase().includes('es') ? 'es' : 'en'; const textos = { es: { maximizar: "Pantalla Completa", pip: "PicToPic", consejo: "Video en iframe. Haz clic en el video e intenta de nuevo", listo: "Listo" }, en: { maximizar: "Full Screen", pip: "PicToPic", consejo: "Iframe video. Please click on the video and try again", listo: "Ready" } }; const txt = textos[idioma]; // Reglas específicas para sitios web const reglasHtml5 = { "www.acfun.cn": [".player-container .player"], "www.bilibili.com": ["#bilibiliPlayer"], "www.douyu.com": ["#js-player-video-case"], "www.huya.com": ["#videoContainer"], "www.twitch.tv": [".player"], "www.youtube.com": ["#ytd-player"], "www.miguvideo.com": ["#mod-player"], "www.yy.com": ["#player"], "*weibo.com": ['[aria-label="Video Player"]', ".html5-video-live .html5-video"], "v.huya.com": ["#video_embed_flash>div"], "www.netflix.com": [".VideoContainer"], "www.primevideo.com": [".dv-player-fullscreen"], "www.hulu.com": [".player-container"], "www.crunchyroll.com": ["#vilos-player"], "www.vimeo.com": [".player"], "www.dailymotion.com": [".player"], "www.facebook.com": ['[data-video-feature="video-player"]'] }; // Reglas para reproductores genéricos const reglasReproductorGeneral = [ ".dplayer", ".video-js", ".jwplayer", "[data-player]", ".plyr", ".flowplayer", ".videojs", ".jw-media" ]; // Verificar si estamos en un iframe if (window.top !== window.self) { estado.esIframe = true; } // Utilidades const utilidades = { log(mensaje) { const ahora = new Date(); const marca = `[${ahora.toLocaleString()}]`; console.log(`${marca}[Maximizar Video] > ${mensaje}`); }, obtenerRect(elemento) { const rect = elemento.getBoundingClientRect(); const scroll = this.obtenerScroll(); return { paginaX: rect.left + scroll.izquierda, paginaY: rect.top + scroll.arriba, pantallaX: rect.left, pantallaY: rect.top, }; }, esMedioPantallaCompleta(elemento) { const cliente = this.obtenerCliente(); const rect = this.obtenerRect(elemento); const anchoSimilar = Math.abs(cliente.ancho - elemento.offsetWidth) < 25; const altoSimilar = Math.abs(cliente.alto - elemento.offsetHeight) < 25; const posicionCerca = rect.pantallaX < 25 && rect.pantallaY < 15; if ((anchoSimilar && rect.pantallaX < 25) || (altoSimilar && rect.pantallaY < 15)) { const centroX = Math.abs(elemento.offsetWidth / 2 + rect.pantallaX - cliente.ancho / 2) < 25; const centroY = Math.abs(elemento.offsetHeight / 2 + rect.pantallaY - cliente.alto / 2) < 25; return centroX && centroY; } return false; }, esPantallaCompletaTotal(elemento) { const cliente = this.obtenerCliente(); const rect = this.obtenerRect(elemento); return Math.abs(cliente.ancho - elemento.offsetWidth) < 25 && rect.pantallaX < 25 && Math.abs(cliente.alto - elemento.offsetHeight) < 25 && rect.pantallaY < 15; }, obtenerScroll() { return { izquierda: document.documentElement.scrollLeft || document.body.scrollLeft, arriba: document.documentElement.scrollTop || document.body.scrollTop, }; }, obtenerCliente() { const esCompatible = document.compatMode === "CSS1Compat"; return { ancho: esCompatible ? document.documentElement.clientWidth : document.body.clientWidth, alto: esCompatible ? document.documentElement.clientHeight : document.body.clientHeight, }; }, agregarEstilo(css) { const estilo = document.createElement("style"); estilo.type = "text/css"; estilo.textContent = css; document.head.appendChild(estilo); return estilo; }, coincideRegla(cadena, regla) { const patron = new RegExp("^" + regla.split("*").join(".*") + "$"); return patron.test(cadena); }, crearBoton(id, texto) { const boton = document.createElement("div"); boton.id = id; boton.className = "boton-video-maximizado"; boton.textContent = texto; document.body.appendChild(boton); return boton; }, async mostrarConsejo(mensaje) { if (document.getElementById("consejoVideo")) return; const consejo = document.createElement("div"); consejo.id = "consejoVideo"; consejo.textContent = mensaje; consejo.style.cssText = ` position: fixed; bottom: 100px; right: -400px; background: #27a9d8; color: white; padding: 12px 20px; border-radius: 6px; font: 14px "Segoe UI", Arial, sans-serif; z-index: 2147483647; transition: right 0.5s ease; box-shadow: 0 4px 12px rgba(0,0,0,0.3); max-width: 300px; `; document.body.appendChild(consejo); // Animación de entrada setTimeout(() => consejo.style.right = "25px", 100); // Animación de salida setTimeout(() => consejo.style.right = "-400px", 3500); // Eliminar elemento setTimeout(() => { if (consejo.parentNode) { document.body.removeChild(consejo); } }, 4000); }, estaEnViewport(elemento) { const rect = elemento.getBoundingClientRect(); return rect.top >= 0 && rect.left >= 0 && rect.bottom <= window.innerHeight && rect.right <= window.innerWidth; }, esVideoValido(video) { return video.offsetWidth > 320 && video.offsetHeight > 180 && video.duration > 0; } }; // Configuración de botones const configurarBotones = { inicializar() { if (!document.getElementById("botonControlReproductor")) { inicializar(); } if (estado.esIframe && utilidades.esMedioPantallaCompleta(estado.reproductor)) { window.parent.postMessage("videoIframe", "*"); return; } this.mostrar(); }, mostrar() { // Cancelar ocultamiento si el mouse vuelve manejadores.cancelarOcultamiento(); // Remover listeners anteriores para evitar duplicados estado.reproductor.removeEventListener("mouseleave", manejadores.salirReproductor, false); estado.reproductor.removeEventListener("mouseenter", manejadores.cancelarOcultamiento, false); // Agregar nuevos listeners estado.reproductor.addEventListener("mouseleave", manejadores.salirReproductor, false); estado.reproductor.addEventListener("mouseenter", manejadores.cancelarOcultamiento, false); if (!estado.esPantallaCompleta) { document.removeEventListener("scroll", manejadores.arreglarScroll, false); document.addEventListener("scroll", manejadores.arreglarScroll, false); } estado.botonControl.style.display = "block"; estado.botonControl.style.visibility = "visible"; if (document.pictureInPictureEnabled && estado.reproductor.nodeName !== "OBJECT" && estado.reproductor.nodeName !== "EMBED") { estado.botonPip.style.display = "block"; estado.botonPip.style.visibility = "visible"; } this.posicionar(); // Iniciar timer de ocultamiento automático después de mostrar manejadores.iniciarTemporizadorOcultamiento(); }, posicionar() { const rectReproductor = utilidades.obtenerRect(estado.reproductor); // Calcular posición central horizontal const centroX = rectReproductor.pantallaX + (estado.reproductor.offsetWidth / 2); // Posición vertical: arriba para maximizar, abajo para PiP const arribaY = rectReproductor.pantallaY + 50; // 50px desde arriba const abajoY = rectReproductor.pantallaY + estado.reproductor.offsetHeight - 80; // 80px desde abajo // Botón maximizar - centro arriba estado.botonControl.style.opacity = "0.8"; estado.botonControl.style.top = `${arribaY}px`; estado.botonControl.style.right = "10px"; // Botón imagen en imagen - centro arriba if (estado.botonPip.style.display === "block") { estado.botonPip.style.opacity = "0.8"; estado.botonPip.style.top = `${arribaY}px`; estado.botonPip.style.right = "190px"; } } }; // Manejadores de eventos const manejadores = { obtenerReproductor(evento) { if (estado.esPantallaCompleta) return; estado.elementoSobreRaton = evento.target; const hostname = document.location.hostname; let reproductores = []; // Buscar reproductores específicos del sitio for (const sitio in reglasHtml5) { if (utilidades.coincideRegla(hostname, sitio)) { for (const regla of reglasHtml5[sitio]) { const elementos = document.querySelectorAll(regla); reproductores.push(...elementos); } break; } } // Buscar reproductores genéricos si no se encontraron específicos if (reproductores.length === 0) { for (const regla of reglasReproductorGeneral) { const elementos = document.querySelectorAll(regla); reproductores.push(...elementos); } } // Búsqueda automática de videos if (reproductores.length === 0 && evento.target.nodeName !== "VIDEO") { const videos = document.querySelectorAll("video"); for (const video of videos) { if (this.estaVideoSobreRaton(evento, video) && utilidades.esVideoValido(video)) { reproductores = [this.verificacionAutomatica(video)]; estado.conteoVerificacionAuto = 1; break; } } } // Verificar si el evento ocurrió dentro de algún reproductor if (reproductores.length > 0) { const ruta = evento.composedPath?.() || evento.path || []; for (const reproductor of reproductores) { if (ruta.includes(reproductor)) { estado.reproductor = reproductor; configurarBotones.inicializar(); return; } } } // Manejo directo de elementos de video this.manejarElementoDirecto(evento); }, estaVideoSobreRaton(evento, video) { const rect = video.getBoundingClientRect(); return evento.clientX >= rect.x - 5 && evento.clientX <= rect.x + rect.width + 5 && evento.clientY >= rect.y - 5 && evento.clientY <= rect.y + rect.height + 5; }, manejarElementoDirecto(evento) { const elemento = evento.target; switch (elemento.nodeName) { case "VIDEO": case "OBJECT": case "EMBED": if (utilidades.esVideoValido(elemento)) { estado.reproductor = elemento; configurarBotones.inicializar(); } break; default: this.salirReproductor(); } }, verificacionAutomatica(video) { let reproductorTemporal = video; let elemento = video; estado.hijos = [video]; while ((elemento = elemento.parentNode)) { const diferenciaAncho = Math.abs(video.offsetWidth - elemento.offsetWidth); const diferenciaAlto = Math.abs(video.offsetHeight - elemento.offsetHeight); if (diferenciaAncho < 20 && diferenciaAlto < 20) { reproductorTemporal = elemento; estado.hijos.push(elemento); } else { break; } } return reproductorTemporal; }, salirReproductor() { // Iniciar temporizador de ocultamiento inmediatamente al salir del reproductor manejadores.iniciarTemporizadorOcultamiento(); }, // Nueva función para cancelar el ocultamiento cuando el mouse vuelve cancelarOcultamiento() { clearTimeout(estado.timerOcultarBotones); estado.timerOcultarBotones = null; }, // Nueva función para iniciar el temporizador de ocultamiento iniciarTemporizadorOcultamiento() { // Limpiar timer anterior si existe clearTimeout(estado.timerOcultarBotones); // Agregar delay de 3 segundos antes de ocultar los botones estado.timerOcultarBotones = setTimeout(() => { if (estado.botonControl && estado.botonControl.style.visibility === "visible") { estado.botonControl.style.visibility = "hidden"; estado.botonControl.style.opacity = "0"; if (estado.botonPip) { estado.botonPip.style.visibility = "hidden"; estado.botonPip.style.opacity = "0"; } document.removeEventListener("scroll", manejadores.arreglarScroll, false); } }, 3000); // 3 segundos de delay }, arreglarScroll() { clearTimeout(estado.timerArregloScroll); estado.timerArregloScroll = setTimeout(() => { configurarBotones.posicionar(); }, 50); }, teclaRapida(evento) { // ESC para salir de pantalla/ventana completa if (evento.keyCode === 27) { maximizar.controlarReproductor(); } // F2 para imagen en imagen if (evento.keyCode === 113) { this.imagenEnImagen(); } }, async recibirMensaje(evento) { const acciones = { async imagenEnImagenIframe() { utilidades.log("Mensaje: imagenEnImagenIframe"); if (!document.pictureInPictureElement) { try { await document.querySelector("video").requestPictureInPicture(); } catch (error) { utilidades.mostrarConsejo(txt.consejo); } } else { await document.exitPictureInPicture(); } }, videoIframe() { utilidades.log("Mensaje: videoIframe"); if (!estado.esPantallaCompleta) { estado.reproductor = estado.elementoSobreRaton; configurarBotones.inicializar(); } }, padreCompleto() { utilidades.log("Mensaje: padreCompleto"); estado.reproductor = estado.elementoSobreRaton; if (estado.esIframe) { window.parent.postMessage("padreCompleto", "*"); } maximizar.verificarPadre(); maximizar.pantallaCompleta(); estado.esPantallaCompleta = true; }, padrePequeno() { utilidades.log("Mensaje: padrePequeno"); if (estado.esIframe) { window.parent.postMessage("padrePequeno", "*"); } maximizar.pantallaNormal(); }, interiorCompleto() { utilidades.log("Mensaje: interiorCompleto"); if (estado.reproductor?.nodeName === "IFRAME") { estado.reproductor.contentWindow.postMessage("interiorCompleto", "*"); } maximizar.verificarPadre(); maximizar.pantallaCompleta(); }, interiorPequeno() { utilidades.log("Mensaje: interiorPequeno"); if (estado.reproductor?.nodeName === "IFRAME") { estado.reproductor.contentWindow.postMessage("interiorPequeno", "*"); } maximizar.pantallaNormal(); } }; const accion = acciones[evento.data]; if (accion) { await accion(); } }, imagenEnImagen() { if (!document.pictureInPictureElement) { if (estado.reproductor) { if (estado.reproductor.nodeName === "IFRAME") { estado.reproductor.contentWindow.postMessage("imagenEnImagenIframe", "*"); } else { const video = estado.reproductor.querySelector("video") || estado.reproductor.parentNode.querySelector("video"); if (video) { video.requestPictureInPicture().catch(error => { utilidades.mostrarConsejo(txt.consejo); }); } } } else { const video = document.querySelector("video"); if (video) { video.requestPictureInPicture().catch(error => { utilidades.mostrarConsejo(txt.consejo); }); } } } else { document.exitPictureInPicture(); } } }; // Funciones de maximización const maximizar = { controlarReproductor() { if (!estado.reproductor) return; this.verificarPadre(); if (!estado.esPantallaCompleta) { if (estado.esIframe) { window.parent.postMessage("padreCompleto", "*"); } if (estado.reproductor.nodeName === "IFRAME") { estado.reproductor.contentWindow.postMessage("interiorCompleto", "*"); } this.pantallaCompleta(); this.manejarVerificacionAuto(); } else { if (estado.esIframe) { window.parent.postMessage("padrePequeno", "*"); } if (estado.reproductor.nodeName === "IFRAME") { estado.reproductor.contentWindow.postMessage("interiorPequeno", "*"); } this.pantallaNormal(); } }, manejarVerificacionAuto() { if (estado.conteoVerificacionAuto > 0 && !utilidades.esMedioPantallaCompleta(estado.hijos[0])) { if (estado.conteoVerificacionAuto > 10) { estado.hijos.forEach(elemento => { elemento.classList.add("videoMaximizado"); }); return; } const reproductorTemp = manejadores.verificacionAutomatica(estado.hijos[0]); estado.conteoVerificacionAuto++; this.controlarReproductor(); estado.reproductor = reproductorTemp; this.controlarReproductor(); } else { estado.conteoVerificacionAuto = 0; } }, verificarPadre() { if (estado.esPantallaCompleta) return; estado.padres = []; let elemento = estado.reproductor; while ((elemento = elemento.parentNode)) { if (elemento.nodeName === "BODY") break; if (elemento.getAttribute) { estado.padres.push(elemento); } } }, pantallaCompleta() { if (estado.esPantallaCompleta) return; document.removeEventListener("mouseover", manejadores.obtenerReproductor, false); // Guardar estados anteriores estado.idHtmlAnterior = document.documentElement.id; estado.idBodyAnterior = document.body.id; // Mostrar botones de control estado.botonIzquierdo.style.display = "block"; estado.botonDerecho.style.display = "block"; estado.botonPip.style.display = "none"; estado.botonControl.style.display = "none"; this.aplicarClases(); this.manejarSitiosEspeciales(); estado.esPantallaCompleta = true; }, aplicarClases() { document.documentElement.id = "htmlMaximizado"; document.body.id = "bodyMaximizado"; estado.padres.forEach(padre => { padre.classList.add("padreMaximizado"); if (getComputedStyle(padre).position === "fixed") { padre.classList.add("absolutoMaximizado"); } }); estado.reproductor.classList.add("reproductorMaximizado"); if (estado.reproductor.nodeName === "VIDEO") { estado.controlesAnteriores = estado.reproductor.controls; estado.reproductor.controls = true; } // Disparar evento de redimensionado window.dispatchEvent(new Event("resize")); }, manejarSitiosEspeciales() { const hostname = document.location.hostname; // YouTube if (hostname.includes("youtube.com")) { const reproductor = document.querySelector("#movie_player"); const botonTamaño = document.querySelector("#movie_player .ytp-size-button"); const contenedorVideo = document.querySelector(".html5-video-container"); const barraInferior = document.querySelector(".ytp-chrome-bottom"); if (reproductor && botonTamaño && contenedorVideo && barraInferior && !document.querySelector("#player-theater-container #movie_player") && contenedorVideo.clientWidth - barraInferior.clientWidth > 30) { botonTamaño.click(); estado.cambioEtapaYoutube = true; } } // Bilibili if (hostname.includes("bilibili.com")) { const contenedorDerecho = document.querySelector(".right-container"); const encabezado = document.querySelector("#biliMainHeader"); if (contenedorDerecho) contenedorDerecho.style.display = "none"; if (encabezado) encabezado.style.display = "none"; } // Netflix if (hostname.includes("netflix.com")) { const controles = document.querySelector(".PlayerControlsNeo__all-controls"); if (controles) controles.style.zIndex = "2147483647"; } // Twitch if (hostname.includes("twitch.tv")) { const chat = document.querySelector(".chat-shell"); if (chat) chat.style.display = "none"; } }, pantallaNormal() { // Restaurar IDs document.documentElement.id = estado.idHtmlAnterior; document.body.id = estado.idBodyAnterior; // Remover clases estado.padres.forEach(padre => { padre.classList.remove("padreMaximizado", "absolutoMaximizado"); }); estado.reproductor.classList.remove("reproductorMaximizado"); // Manejar sitios especiales al salir this.restaurarSitiosEspeciales(); // Restaurar controles de video if (estado.reproductor.nodeName === "VIDEO") { estado.reproductor.controls = estado.controlesAnteriores; } // Ocultar botones de control estado.botonIzquierdo.style.display = "none"; estado.botonDerecho.style.display = "none"; estado.botonControl.style.display = "none"; // Reactivar detección de mouse document.addEventListener("mouseover", manejadores.obtenerReproductor, false); // Disparar evento de redimensionado window.dispatchEvent(new Event("resize")); estado.esPantallaCompleta = false; }, restaurarSitiosEspeciales() { const hostname = document.location.hostname; // YouTube if (hostname.includes("youtube.com") && estado.cambioEtapaYoutube) { const reproductor = document.querySelector("#player-theater-container #movie_player"); const botonTamaño = document.querySelector("#movie_player .ytp-size-button"); if (reproductor && botonTamaño) { botonTamaño.click(); } estado.cambioEtapaYoutube = false; } // Bilibili if (hostname.includes("bilibili.com")) { const contenedorDerecho = document.querySelector(".right-container"); const encabezado = document.querySelector("#biliMainHeader"); if (contenedorDerecho) contenedorDerecho.style.removeProperty("display"); if (encabezado) encabezado.style.removeProperty("display"); } // Twitch if (hostname.includes("twitch.tv")) { const chat = document.querySelector(".chat-shell"); if (chat) chat.style.removeProperty("display"); } } }; // Función de inicialización function inicializar() { // Crear botones estado.botonControl = utilidades.crearBoton("botonControlReproductor", txt.maximizar); estado.botonControl.onclick = () => maximizar.controlarReproductor(); estado.botonPip = utilidades.crearBoton("botonImagenEnImagen", txt.pip); estado.botonPip.onclick = () => manejadores.imagenEnImagen(); estado.botonIzquierdo = utilidades.crearBoton("botonIzquierdoPantalla", ""); estado.botonDerecho = utilidades.crearBoton("botonDerechoPantalla", ""); // Aplicar estilos CSS const estilos = ` /* Estilos para maximización */ #htmlMaximizado, #bodyMaximizado { overflow: hidden !important; zoom: 100% !important; margin: 0 !important; padding: 0 !important; } #htmlMaximizado #bodyMaximizado .padreMaximizado { overflow: visible !important; z-index: auto !important; transform: none !important; -webkit-transform-style: flat !important; transition: none !important; contain: none !important; } #htmlMaximizado #bodyMaximizado .absolutoMaximizado { position: absolute !important; } #htmlMaximizado #bodyMaximizado .reproductorMaximizado { position: fixed !important; top: 0 !important; left: 0 !important; width: 100vw !important; height: 100vh !important; max-width: none !important; max-height: none !important; min-width: 0 !important; min-height: 0 !important; margin: 0 !important; padding: 0 !important; z-index: 2147483646 !important; border: none !important; background-color: #000 !important; transform: none !important; border-radius: 0 !important; } #htmlMaximizado #bodyMaximizado .padreMaximizado video { object-fit: contain !important; } #htmlMaximizado #bodyMaximizado .padreMaximizado .videoMaximizado { width: 100vw !important; height: 100vh !important; } /* Estilos para botones */ .boton-video-maximizado { position: fixed; z-index: 2147483647; cursor: pointer; user-select: none; font: 12px "Segoe UI", Arial, sans-serif; transition: all 0.3s ease; border: none; border-radius: 4px; box-shadow: 0 2px 8px rgba(0,0,0,0.3); visibility: hidden; opacity: 0; display: none; transform: translateX(-50%); /* Centrar horizontalmente */ } #botonControlReproductor { background: linear-gradient(135deg, #27a9d8, #2196f3); color: white; padding: 8px 14px; font-weight: 500; text-shadow: none; min-width: 110px; height: 22px; line-height: 16px; text-align: center; white-space: nowrap; } #botonControlReproductor:hover { background: linear-gradient(135deg, #2196f3, #1976d2); transform: translateX(-50%) translateY(-1px); /* Mantener centrado en hover */ box-shadow: 0 4px 12px rgba(0,0,0,0.4); } #botonImagenEnImagen { background: linear-gradient(135deg, #ff6b35, #f44336); color: white; padding: 8px 14px; font-weight: 500; text-shadow: none; min-width: 30px; height: 22px; line-height: 16px; text-align: center; white-space: nowrap; } #botonImagenEnImagen:hover { background: linear-gradient(135deg, #f44336, #d32f2f); transform: translateX(-50%) translateY(-1px); /* Mantener centrado en hover */ box-shadow: 0 4px 12px rgba(0,0,0,0.4); } #botonIzquierdoPantalla, #botonDerechoPantalla { width: 2px; height: 100vh; top: 0; background: #000; display: none; } #botonIzquierdoPantalla { left: 0; } #botonDerechoPantalla { right: 0; } /* Estilos específicos para sitios */ #htmlMaximizado #bodyMaximizado .padreMaximizado .bilibili-player-video { margin: 0 !important; } /* Mejoras para diferentes reproductores */ #htmlMaximizado #bodyMaximizado .reproductorMaximizado iframe { width: 100% !important; height: 100% !important; } #htmlMaximizado #bodyMaximizado .reproductorMaximizado .plyr { width: 100% !important; height: 100% !important; } #htmlMaximizado #bodyMaximizado .reproductorMaximizado .video-js { width: 100% !important; height: 100% !important; } /* Animaciones suaves */ .reproductorMaximizado { animation: expandirReproductor 0.3s ease-out; } @keyframes expandirReproductor { from { transform: scale(0.95); opacity: 0.8; } to { transform: scale(1); opacity: 1; } } /* Responsive design */ @media (max-width: 768px) { #botonControlReproductor, #botonImagenEnImagen { font-size: 11px; padding: 6px 10px; min-width: 60px; height: 24px; } } /* Modo oscuro */ @media (prefers-color-scheme: dark) { #botonControlReproductor { background: linear-gradient(135deg, #1a237e, #3f51b5); } #botonControlReproductor:hover { background: linear-gradient(135deg, #3f51b5, #5c6bc0); } } `; utilidades.agregarEstilo(estilos); // Configurar event listeners document.addEventListener("mouseover", manejadores.obtenerReproductor, false); document.addEventListener("keydown", manejadores.teclaRapida, false); window.addEventListener("message", manejadores.recibirMensaje, false); // Event listeners adicionales para mejor detección document.addEventListener("mouseenter", manejadores.obtenerReproductor, true); document.addEventListener("focusin", manejadores.obtenerReproductor, false); // Observador de mutaciones para detectar nuevos videos const observador = new MutationObserver((mutaciones) => { let hayNuevosVideos = false; mutaciones.forEach((mutacion) => { mutacion.addedNodes.forEach((nodo) => { if (nodo.nodeType === Node.ELEMENT_NODE) { if (nodo.tagName === 'VIDEO' || nodo.querySelector('video')) { hayNuevosVideos = true; } } }); }); if (hayNuevosVideos) { // Pequeño delay para permitir que el DOM se estabilice setTimeout(() => { // Reactivar detección si no estamos en pantalla completa if (!estado.esPantallaCompleta) { document.removeEventListener("mouseover", manejadores.obtenerReproductor, false); document.addEventListener("mouseover", manejadores.obtenerReproductor, false); } }, 500); } }); // Iniciar observación observador.observe(document.body, { childList: true, subtree: true }); // Detección automática inicial setTimeout(() => { detectarVideosAutomaticamente(); }, 1000); utilidades.log(txt.listo); } // Función para detectar videos automáticamente al cargar la página function detectarVideosAutomaticamente() { const hostname = document.location.hostname; // Sitios conocidos que cargan videos dinámicamente const sitiosDinamicos = [ 'youtube.com', 'netflix.com', 'primevideo.com', 'hulu.com', 'twitch.tv', 'bilibili.com' ]; if (sitiosDinamicos.some(sitio => hostname.includes(sitio))) { // Intentar detectar reproductores después de un delay setTimeout(() => { const videos = document.querySelectorAll('video'); videos.forEach(video => { if (utilidades.esVideoValido(video) && utilidades.estaEnViewport(video)) { // Crear evento sintético para activar la detección const eventoSintetico = new MouseEvent('mouseover', { bubbles: true, cancelable: true, clientX: video.getBoundingClientRect().left + 10, clientY: video.getBoundingClientRect().top + 10 }); video.dispatchEvent(eventoSintetico); } }); }, 2000); } } // Función para manejar cambios de página en SPAs function manejarNavegacionSPA() { let urlAnterior = location.href; const verificarCambioURL = () => { if (location.href !== urlAnterior) { urlAnterior = location.href; // Resetear estado si cambiamos de página if (estado.esPantallaCompleta) { maximizar.pantallaNormal(); } // Detectar nuevos videos después de un delay setTimeout(detectarVideosAutomaticamente, 1500); } }; // Observar cambios de URL en SPAs window.addEventListener('popstate', verificarCambioURL); // También observar cambios en el título (común en SPAs) const observadorTitulo = new MutationObserver(verificarCambioURL); observadorTitulo.observe(document.querySelector('title') || document.head, { childList: true, subtree: true }); } // Función para manejar errores function manejarErrores() { window.addEventListener('error', (evento) => { if (evento.message.includes('Maximizar Video')) { utilidades.log(`Error capturado: ${evento.message}`); // Intentar recuperación básica if (estado.esPantallaCompleta) { try { maximizar.pantallaNormal(); } catch (e) { utilidades.log('Error en recuperación: ' + e.message); } } } }); } // Función para mejorar la accesibilidad function mejorarAccesibilidad() { // Agregar atributos ARIA a los botones if (estado.botonControl) { estado.botonControl.setAttribute('role', 'button'); estado.botonControl.setAttribute('aria-label', txt.maximizar); estado.botonControl.setAttribute('tabindex', '0'); // Soporte para teclado estado.botonControl.addEventListener('keydown', (evento) => { if (evento.key === 'Enter' || evento.key === ' ') { evento.preventDefault(); maximizar.controlarReproductor(); } }); } if (estado.botonPip) { estado.botonPip.setAttribute('role', 'button'); estado.botonPip.setAttribute('aria-label', txt.pip); estado.botonPip.setAttribute('tabindex', '0'); estado.botonPip.addEventListener('keydown', (evento) => { if (evento.key === 'Enter' || evento.key === ' ') { evento.preventDefault(); manejadores.imagenEnImagen(); } }); } } // Inicialización principal try { inicializar(); manejarNavegacionSPA(); manejarErrores(); // Mejorar accesibilidad después de que los botones estén creados setTimeout(mejorarAccesibilidad, 100); } catch (error) { utilidades.log('Error en inicialización: ' + error.message); } })();