Maximizar Video (Mejorado)

Maximiza todos los reproductores de video. Soporta imagen en imagen.

当前为 2025-06-15 提交的版本,查看 最新版本

// ==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);
  }

})();