import os import subprocess import logging from typing import Optional from fastapi import FastAPI, HTTPException import gradio as gr # Configuración de logging logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') logger = logging.getLogger(__name__) # Directorios y rutas UPLOAD_DIR = os.path.join(os.getcwd(), "uploads") FFMPEG_PATH = "./ffmpeg" # Crear directorios os.makedirs(UPLOAD_DIR, exist_ok=True) def sanitize_filename(filename: str) -> str: """Limpiar y validar nombre de archivo para prevenir riesgos de seguridad.""" return ''.join(c for c in filename if c.isalnum() or c in ('.', '_', '-')).rstrip() def ensure_unique_filename(directory: str, filename: str) -> str: """Generar un nombre de archivo único para evitar sobreescrituras.""" base, ext = os.path.splitext(filename) counter = 1 new_filename = filename while os.path.exists(os.path.join(directory, new_filename)): new_filename = f"{base}_{counter}{ext}" counter += 1 return new_filename def make_ffmpeg_executable(ffmpeg_path: str): """Asegurar permisos correctos para FFmpeg.""" try: subprocess.run(["chmod", "+x", ffmpeg_path], check=True) logger.info(f"Permisos de FFmpeg configurados para {ffmpeg_path}") except subprocess.CalledProcessError as e: logger.error(f"Error al configurar permisos de FFmpeg: {e}") raise def convert_video_to_mp4(input_file: str, output_dir: str) -> str: """Convertir video a formato MP4 con configuraciones optimizadas.""" try: # Generar nombre de archivo único output_filename = ensure_unique_filename( output_dir, os.path.basename(input_file).rsplit('.', 1)[0] + "_converted.mp4" ) output_file = os.path.join(output_dir, output_filename) # Comando FFmpeg con configuraciones optimizadas ffmpeg_command = [ FFMPEG_PATH, '-i', input_file, '-vf', 'fps=24', # Cambiar la tasa de fotogramas '-c:v', 'libx264', # Codificador de video '-c:a', 'libfdk_aac', # Codificador de audio '-profile:a', 'aac_he_v2', # Perfil de audio '-crf', '28', # Tasa de compresión '-b:a', '32k', # Tasa de bits de audio '-preset', 'slow', # Preajuste de codificación '-movflags', '+faststart', # Web optimization output_file ] # Ejecutar conversión result = subprocess.run( ffmpeg_command, check=True, capture_output=True, text=True ) logger.info(f"Video convertido exitosamente: {output_file}") return output_file except subprocess.CalledProcessError as e: logger.error(f"Error de conversión de FFmpeg: {e.stderr}") raise HTTPException(status_code=500, detail=f"Fallo en la conversión de video: {e.stderr}") except Exception as e: logger.error(f"Error inesperado durante la conversión: {e}") raise HTTPException(status_code=500, detail="Error inesperado durante la conversión de video") def process_video(file_path: str) -> str: """Procesar el video completo.""" return convert_video_to_mp4(file_path, UPLOAD_DIR) def gradio_interface(video: Optional[str]) -> Optional[str]: """Interfaz de Gradio para conversión de video.""" if not video: raise gr.Error("No se ha subido ningún video. Por favor, sube un video.") try: converted_video = process_video(video) return converted_video except Exception as e: raise gr.Error(f"Conversión fallida: {str(e)}") # Asegurar permisos de FFmpeg make_ffmpeg_executable(FFMPEG_PATH) # Crear aplicación FastAPI app = FastAPI() # Configurar interfaz de Gradio iface = gr.Interface( fn=gradio_interface, inputs=gr.Video( label="Subir Video", type="filepath", # Asegurar ruta de archivo directa sources=["upload"] # Limitar fuente de subida ), outputs=gr.File( label="Descargar MP4 Convertido", type="file" ), title="🎥 Convertidor de Video", description="Convierte videos a formato MP4 optimizado con configuraciones estándar", theme="huggingface" # Tema de HuggingFace ) # Lanzar interfaz de Gradio if __name__ == "__main__": iface.launch(share=True)