Spaces:
Running
Running
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) | |
# Definir formatos de video y audio soportados | |
VIDEO_FORMATS = ['.mp4', '.avi', '.mov', '.mkv', '.webm', '.flv', '.wmv'] | |
AUDIO_FORMATS = ['.mp3', '.wav', '.aac', '.flac', '.ogg', '.m4a', '.wma'] | |
def detect_media_type(file_path: str) -> str: | |
"""Detectar si el archivo es de audio o video.""" | |
ext = os.path.splitext(file_path)[1].lower() | |
if ext in VIDEO_FORMATS: | |
return 'video' | |
elif ext in AUDIO_FORMATS: | |
return 'audio' | |
else: | |
return 'unsupported' | |
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_media(input_file: str, output_dir: str) -> str: | |
"""Convertir archivos multimedia con configuraciones optimizadas.""" | |
try: | |
media_type = detect_media_type(input_file) | |
base_name = os.path.basename(input_file) | |
# Elegir extensi贸n de salida seg煤n el tipo de medio | |
output_extension = 'mp4' if media_type == 'video' else 'm4a' | |
output_filename = ensure_unique_filename( | |
output_dir, | |
f"{os.path.splitext(base_name)[0]}_converted.{output_extension}" | |
) | |
output_file = os.path.join(output_dir, output_filename) | |
if media_type == 'video': | |
# Configuraciones de conversi贸n de video | |
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 | |
] | |
elif media_type == 'audio': | |
# Configuraciones de conversi贸n de audio | |
ffmpeg_command = [ | |
FFMPEG_PATH, | |
'-i', input_file, | |
'-vn', # Ignorar video | |
'-c:a', 'libfdk_aac', # Codificador AAC para M4A | |
'-profile:a', 'aac_he_v2', | |
'-b:a', '32k', # Calidad de audio alta | |
'-ar', '44100', # Frecuencia de muestreo | |
output_file | |
] | |
else: | |
raise ValueError("Formato no soportado") | |
# Ejecutar conversi贸n | |
result = subprocess.run( | |
ffmpeg_command, | |
check=True, | |
capture_output=True, | |
text=True | |
) | |
logger.info(f"Medio 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 medio: {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") | |
def process_media(file_path: str) -> str: | |
"""Procesar medio completo.""" | |
return convert_media(file_path, UPLOAD_DIR) | |
def gradio_interface(media: Optional[str]) -> Optional[str]: | |
"""Interfaz de Gradio para conversi贸n de medios.""" | |
if not media: | |
raise gr.Error("No se ha subido ning煤n medio. Por favor, sube un archivo.") | |
try: | |
converted_media = process_media(media) | |
return converted_media | |
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.File( | |
label="Subir Archivo", | |
type="filepath" | |
), | |
outputs=gr.File( | |
label="Descargar Archivo Convertido", | |
type="filepath" | |
), | |
title="馃帴馃幍 Convertidor Universal", | |
description="Convierte videos a MP4 y audios a M4A con configuraciones optimizadas", | |
theme="huggingface" | |
) | |
# Lanzar interfaz de Gradio | |
if __name__ == "__main__": | |
iface.launch(share=True) |