// ==UserScript==
// @name Maximizar Video (Mejorado)
// @name:en Maximize Video (Improved)
// @namespace AkioBrian
// @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.6
// @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: "⛶ Max",
pip: "⧉ PiP",
consejo: "Video en iframe. Haz clic en el video e intenta de nuevo",
listo: "Listo"
},
en: {
maximizar: "⛶ Max",
pip: "⧉ PiP",
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 relativa al reproductor
const anchoReproductor = estado.reproductor.offsetWidth;
const altoReproductor = estado.reproductor.offsetHeight;
// Posición vertical: 20px desde arriba del reproductor
const arribaY = rectReproductor.pantallaY + 20;
// Botón maximizar - esquina superior derecha del reproductor
estado.botonControl.style.opacity = "0.8";
estado.botonControl.style.top = `${arribaY}px`;
estado.botonControl.style.left = `${rectReproductor.pantallaX + anchoReproductor - 150}px`; // 130px desde el borde derecho del video
estado.botonControl.style.right = "auto"; // Resetear right
// Botón imagen en imagen - a la izquierda del botón maximizar
if (estado.botonPip.style.display === "block") {
estado.botonPip.style.opacity = "0.8";
estado.botonPip.style.top = `${arribaY}px`;
estado.botonPip.style.left = `${rectReproductor.pantallaX + anchoReproductor - 215}px`; // 240px desde el borde derecho del video
estado.botonPip.style.right = "auto"; // Resetear right
}
}
};
// 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 2 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);
}
}, 2000); // 2 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: 1px solid #777777 !important;
border-radius: 9px;
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
visibility: hidden;
opacity: 0;
display: none;
}
#botonControlReproductor {
background: rgba(0, 0, 0, 0.9);
color: white !important;
padding: 6px 12px;
font-weight: 500;
text-shadow: none;
min-width: 60px;
height: 32px;
line-height: center;
text-align: center;
white-space: nowrap;
box-sizing: border-box;
}
#botonControlReproductor:hover {
background: rgba(70, 70, 70, 1);
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
}
#botonImagenEnImagen {
background: rgba(0, 0, 0, 0.9);
color: white !important;
padding: 6px 12px;
font-weight: 500;
text-shadow: none;
min-width: 60px;
height: 32px;
line-height: center;
text-align: center;
white-space: nowrap;
box-sizing: border-box;
}
#botonImagenEnImagen:hover {
background: rgba(70, 70, 70, 1);
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
}
#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: rgba(0, 0, 0, 0.9);
}
#botonControlReproductor:hover {
background: rgba(70, 70, 70, 1);
}
}
`;
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);
}
})();