120 lines
4.6 KiB
Python
120 lines
4.6 KiB
Python
"""
|
|
Sistema de gestión de MathJax local con descarga automática
|
|
"""
|
|
import os
|
|
import zipfile
|
|
import logging
|
|
from pathlib import Path
|
|
from urllib.request import urlretrieve
|
|
from urllib.error import URLError
|
|
|
|
|
|
class MathJaxManager:
|
|
"""Gestor de MathJax local con descarga automática"""
|
|
|
|
MATHJAX_VERSION = "3.2.2"
|
|
MATHJAX_URL = f"https://github.com/mathjax/MathJax/archive/{MATHJAX_VERSION}.zip"
|
|
LOCAL_DIR = Path(".mathjax")
|
|
|
|
def __init__(self):
|
|
self.logger = logging.getLogger(__name__)
|
|
self.mathjax_dir = self.LOCAL_DIR / f"MathJax-{self.MATHJAX_VERSION}"
|
|
self.es5_dir = self.mathjax_dir / "es5"
|
|
|
|
def get_mathjax_url(self) -> str:
|
|
"""
|
|
Obtiene la URL de MathJax (local si está disponible, CDN si no)
|
|
"""
|
|
if self.is_local_available():
|
|
# Construir URL local relativa para file://
|
|
local_url = f"file:///{self.es5_dir.absolute().as_posix()}/tex-svg.js"
|
|
self.logger.debug(f"Usando MathJax local: {local_url}")
|
|
return local_url
|
|
else:
|
|
# Descargar automáticamente si no existe
|
|
if self.download_mathjax():
|
|
local_url = f"file:///{self.es5_dir.absolute().as_posix()}/tex-svg.js"
|
|
self.logger.info(f"MathJax descargado y configurado localmente: {local_url}")
|
|
return local_url
|
|
else:
|
|
# Fallback a CDN
|
|
cdn_url = "https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js"
|
|
self.logger.warning(f"Fallback a CDN: {cdn_url}")
|
|
return cdn_url
|
|
|
|
def is_local_available(self) -> bool:
|
|
"""Verifica si MathJax está disponible localmente"""
|
|
tex_svg_file = self.es5_dir / "tex-svg.js"
|
|
return tex_svg_file.exists() and tex_svg_file.is_file()
|
|
|
|
def download_mathjax(self) -> bool:
|
|
"""
|
|
Descarga MathJax automáticamente si no está disponible
|
|
|
|
Returns:
|
|
True si la descarga fue exitosa, False si falló
|
|
"""
|
|
try:
|
|
self.logger.info(f"Descargando MathJax {self.MATHJAX_VERSION}...")
|
|
|
|
# Crear directorio si no existe
|
|
self.LOCAL_DIR.mkdir(exist_ok=True)
|
|
|
|
# Archivo temporal para la descarga
|
|
zip_path = self.LOCAL_DIR / f"mathjax-{self.MATHJAX_VERSION}.zip"
|
|
|
|
# Descargar archivo ZIP
|
|
self.logger.debug(f"Descargando desde: {self.MATHJAX_URL}")
|
|
urlretrieve(self.MATHJAX_URL, zip_path)
|
|
|
|
# Extraer archivo
|
|
self.logger.debug(f"Extrayendo en: {self.LOCAL_DIR}")
|
|
with zipfile.ZipFile(zip_path, 'r') as zip_ref:
|
|
zip_ref.extractall(self.LOCAL_DIR)
|
|
|
|
# Limpiar archivo ZIP
|
|
zip_path.unlink()
|
|
|
|
# Verificar que se extrajo correctamente
|
|
if self.is_local_available():
|
|
self.logger.info(f"✅ MathJax {self.MATHJAX_VERSION} descargado correctamente")
|
|
return True
|
|
else:
|
|
self.logger.error("❌ Error: MathJax no se extrajo correctamente")
|
|
return False
|
|
|
|
except URLError as e:
|
|
self.logger.error(f"❌ Error de red descargando MathJax: {e}")
|
|
return False
|
|
except zipfile.BadZipFile as e:
|
|
self.logger.error(f"❌ Error: archivo ZIP corrupto: {e}")
|
|
# Limpiar archivo corrupto
|
|
if zip_path.exists():
|
|
zip_path.unlink()
|
|
return False
|
|
except Exception as e:
|
|
self.logger.error(f"❌ Error inesperado descargando MathJax: {e}")
|
|
return False
|
|
|
|
def clean_local_mathjax(self):
|
|
"""
|
|
Limpia la instalación local de MathJax
|
|
(Para forzar redownload en la siguiente inicialización)
|
|
"""
|
|
try:
|
|
if self.LOCAL_DIR.exists():
|
|
import shutil
|
|
shutil.rmtree(self.LOCAL_DIR)
|
|
self.logger.info("🗑️ MathJax local limpiado")
|
|
except Exception as e:
|
|
self.logger.error(f"Error limpiando MathJax local: {e}")
|
|
|
|
def get_status_info(self) -> dict:
|
|
"""Obtiene información del estado de MathJax"""
|
|
return {
|
|
"local_available": self.is_local_available(),
|
|
"local_dir": str(self.LOCAL_DIR.absolute()),
|
|
"mathjax_dir": str(self.mathjax_dir.absolute()) if self.mathjax_dir.exists() else None,
|
|
"version": self.MATHJAX_VERSION,
|
|
"tex_svg_file": str(self.es5_dir / "tex-svg.js") if self.is_local_available() else None
|
|
} |