""" MELODIX - Verificador de Prerrequisitos Verifica que todos los componentes necesarios estén instalados y configurados. Uso: python check_requirements.py """ import sys import os import subprocess import shutil from dotenv import load_dotenv # ========================================== # CONFIGURACIÓN # ========================================== load_dotenv() COLORS = { 'GREEN': '\033[92m', 'RED': '\033[91m', 'YELLOW': '\033[93m', 'BLUE': '\033[94m', 'END': '\033[0m' } def c(text, color): """Colorea el texto""" return f"{COLORS.get(color, '')}{text}{COLORS['END']}" def check_mark(passed): """Retorna ✓ o ✗""" return c("✓", "GREEN") if passed else c("✗", "RED") # ========================================== # VERIFICACIONES # ========================================== def check_python(): """Verifica la versión de Python""" print("\n" + "=" * 50) print(" PYTHON") print("=" * 50) version = sys.version_info passed = version.major == 3 and version.minor >= 10 print(f" {check_mark(passed)} Versión: {version.major}.{version.minor}.{version.micro}") print(f" {'→ Requiere Python 3.10+' if not passed else ''}") return passed def check_packages(): """Verifica los paquetes Python instalados""" print("\n" + "=" * 50) print(" PAQUETES PYTHON") print("=" * 50) required_packages = { 'fastapi': 'FastAPI', 'uvicorn': 'Uvicorn', 'sqlalchemy': 'SQLAlchemy', 'celery': 'Celery', 'redis': 'Redis', 'librosa': 'Librosa', 'passlib': 'Passlib', 'jose': 'Python-JOSE', 'dotenv': 'python-dotenv', 'pydantic': 'Pydantic', } all_installed = True for package, name in required_packages.items(): try: __import__(package) pkg = __import__(package) version = getattr(pkg, '__version__', 'N/A') print(f" {check_mark(True)} {name} ({version})") except ImportError: print(f" {check_mark(False)} {name} - NO INSTALADO") all_installed = False # Verificar demucs try: import demucs print(f" {check_mark(True)} Demucs ({demucs.__version__ if hasattr(demucs, '__version__') else 'N/A'})") except ImportError: print(f" {check_mark(False)} Demucs - NO INSTALADO") all_installed = False return all_installed def check_redis(): """Verifica la conexión a Redis""" print("\n" + "=" * 50) print(" REDIS") print("=" * 50) try: import redis redis_url = os.getenv("REDIS_URL", "redis://localhost:6379/0") # Normalize ssl_cert_reqs to 'none' if it is 'CERT_NONE' (case-insensitive) to prevent redis-py errors if redis_url.startswith("rediss://"): if "ssl_cert_reqs=" in redis_url: redis_url = redis_url.replace("ssl_cert_reqs=CERT_NONE", "ssl_cert_reqs=none") redis_url = redis_url.replace("ssl_cert_reqs=cert_none", "ssl_cert_reqs=none") else: if "?" in redis_url: redis_url += "&ssl_cert_reqs=none" else: redis_url += "?ssl_cert_reqs=none" r = redis.from_url(redis_url) r.ping() print(f" {check_mark(True)} Conexión: {redis_url}") print(f" {check_mark(True)} Redis respondiendo: PONG") return True except ImportError: print(f" {check_mark(False)} Paquete redis NO INSTALADO") return False except Exception as e: print(f" {check_mark(False)} Conexión fallida: {e}") print(f" → ¿Está Redis corriendo? Ejecuta: redis-server") return False def check_ffmpeg(): """Verifica FFmpeg""" print("\n" + "=" * 50) print(" FFMPEG") print("=" * 50) ffmpeg_path = shutil.which("ffmpeg") if ffmpeg_path: try: result = subprocess.run( ["ffmpeg", "-version"], capture_output=True, text=True, timeout=5 ) version_line = result.stdout.split('\n')[0] print(f" {check_mark(True)} FFmpeg encontrado: {ffmpeg_path}") print(f" {check_mark(True)} {version_line}") return True except Exception as e: print(f" {check_mark(False)} Error verificando versión: {e}") return False else: print(f" {check_mark(False)} FFmpeg NO ENCONTRADO en PATH") print(f" → Instala FFmpeg: https://ffmpeg.org/download.html") print(f" → O usa Chocolatey: choco install ffmpeg") return False def check_demucs_models(): """Verifica los modelos de Demucs""" print("\n" + "=" * 50) print(" MODELOS DEMUCS") print("=" * 50) # Los modelos se descargan automáticamente la primera vez # Solo verificamos si existe el directorio de caché cache_dir = os.path.expanduser("~/.cache/huggingface/hub") if os.path.exists(cache_dir): models = [d for d in os.listdir(cache_dir) if d.startswith('models--facebook--demucs')] if models: print(f" {check_mark(True)} Modelos en caché: {len(models)} encontrado(s)") for m in models: print(f" - {m}") return True else: print(f" {check_mark(True)} Caché listo (modelos se descargarán la primera vez)") return True else: print(f" {check_mark(True)} Los modelos se descargarán automáticamente") return True def check_directories(): """Verifica los directorios necesarios""" print("\n" + "=" * 50) print(" DIRECTORIOS") print("=" * 50) base_dir = os.getenv("BASE_DIR", os.path.dirname(os.path.abspath(__file__))) dirs_to_check = [ ("BASE_DIR", base_dir), ("UPLOADS_DIR", os.getenv("UPLOADS_DIR", os.path.join(base_dir, "uploads"))), ("PROCESSED_DIR", os.getenv("PROCESSED_DIR", os.path.join(base_dir, "processed"))), ("LOGS_DIR", os.path.join(base_dir, "logs")), ] all_ok = True for name, path in dirs_to_check: exists = os.path.exists(path) writable = os.access(path, os.W_OK) if exists else False if exists and writable: print(f" {check_mark(True)} {name}: {path}") elif exists: print(f" {check_mark(False)} {name}: {path} (NO ESCRIBIBLE)") all_ok = False else: print(f" {check_mark(False)} {name}: {path} (NO EXISTE)") all_ok = False return all_ok def check_env_file(): """Verifica el archivo .env""" print("\n" + "=" * 50) print(" VARIABLES DE ENTORNO") print("=" * 50) env_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), ".env") if os.path.exists(env_file): print(f" {check_mark(True)} Archivo .env encontrado") # Verificar variables críticas critical_vars = ["SECRET_KEY", "REDIS_URL"] for var in critical_vars: value = os.getenv(var) if value: print(f" {check_mark(True)} {var}: configurado") else: print(f" {check_mark(False)} {var}: NO CONFIGURADO") return True else: print(f" {check_mark(False)} Archivo .env NO ENCONTRADO") print(f" → Copia .env.example a .env") return False def check_gpu(): """Verifica disponibilidad de GPU CUDA""" print("\n" + "=" * 50) print(" GPU / CUDA") print("=" * 50) try: import torch cuda_available = torch.cuda.is_available() if cuda_available: gpu_name = torch.cuda.get_device_name(0) gpu_memory = torch.cuda.get_device_properties(0).total_memory / (1024**3) print(f" {check_mark(True)} CUDA disponible: {gpu_name}") print(f" {check_mark(True)} Memoria GPU: {gpu_memory:.1f} GB") return True else: print(f" {check_mark(False)} CUDA NO disponible") print(f" → Se usará CPU (más lento)") print(f" → Para GPU: instala PyTorch con CUDA") return False except ImportError: print(f" {check_mark(False)} PyTorch NO INSTALADO") print(f" → Demucs lo instalará como dependencia") return False except Exception as e: print(f" {check_mark(False)} Error verificando GPU: {e}") return False # ========================================== # MAIN # ========================================== def main(): print(c("\n" + "=" * 60, "BLUE")) print(c(" MELODIX - VERIFICADOR DE PRERREQUISITOS", "BLUE")) print(c("=" * 60, "BLUE")) results = { "Python": check_python(), "Paquetes": check_packages(), "Redis": check_redis(), "FFmpeg": check_ffmpeg(), "Demucs": check_demucs_models(), "Directorios": check_directories(), ".env": check_env_file(), "GPU": check_gpu(), } # ========================================== # RESUMEN FINAL # ========================================== print("\n" + "=" * 60) print(c(" RESUMEN", "BLUE")) print("=" * 60) passed = sum(1 for v in results.values() if v) total = len(results) for name, result in results.items(): status = c("✓ PASS", "GREEN") if result else c("✗ FAIL", "RED") print(f" {status} - {name}") print("\n" + "-" * 60) if passed == total: print(c(" ¡TODO LISTO! Puedes iniciar Melodix", "GREEN")) print(c(" Ejecuta: iniciar_melodix.bat", "GREEN")) else: print(c(f" ATENCIÓN: {total - passed} verificación(es) fallida(s)", "YELLOW")) print(c(" Revisa los errores arriba y corrígelos antes de iniciar", "YELLOW")) print("=" * 60 + "\n") return 0 if passed == total else 1 if __name__ == "__main__": sys.exit(main())