import gradio as gr import moviepy.editor as mp import numpy as np import librosa from PIL import Image, ImageDraw import tempfile import os import logging # Configuración de logging logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', handlers=[logging.StreamHandler()] ) logger = logging.getLogger("audio_to_video") def generate_video(audio_file, image_file): try: # 1. Cargar audio y, sr = librosa.load(audio_file) duration = librosa.get_duration(y=y, sr=sr) logger.info(f"Audio cargado: {duration:.1f} segundos") # 2. Cargar imagen img = Image.open(image_file).convert('RGB') img_w, img_h = img.size logger.info(f"Imagen cargada: {img_w}x{img_h}") # 3. Crear efecto waveform audio_envelope = np.abs(y) / np.max(np.abs(y)) # Normalizar audio_envelope *= img_h // 4 # Escalar al 25% de la altura # 4. Generar frames def make_frame(t): frame = img.copy() draw = ImageDraw.Draw(frame) time_idx = int(t * sr) start = max(0, time_idx - sr//10) end = min(len(audio_envelope), time_idx + sr//10) wave_slice = audio_envelope[start:end] points = [] for i, val in enumerate(wave_slice): x = int((i / len(wave_slice)) * img_w) y_pos = img_h//2 - int(val) y_neg = img_h//2 + int(val) points.extend([(x, y_pos), (x, y_neg)]) if len(points) > 2: draw.polygon(points, fill=(255, 0, 0, 128)) # Rojo semitransparente return np.array(frame) # 5. Crear video video = mp.VideoClip(make_frame, duration=duration) video.fps = 24 video = video.set_audio(mp.AudioFileClip(audio_file)) # 6. Guardar video with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as tmpfile: video.write_videofile( tmpfile.name, codec="libx264", audio_codec="aac", fps=24, logger=None ) # Verificar que el archivo existe if not os.path.exists(tmpfile.name): raise Exception("Error al guardar el video temporal") logger.info(f"Video guardado: {tmpfile.name}") return tmpfile.name # Retornar la ruta del archivo except Exception as e: logger.error(f"Error crítico: {str(e)}") return f"Error: {str(e)}" # Interfaz Gradio iface = gr.Interface( fn=generate_video, inputs=[ gr.Audio(type="filepath", label="Audio (WAV/MP3)"), gr.Image(type="filepath", label="Imagen de Fondo") ], outputs=gr.File(label="Descargar Video"), # Usar File para descargar title="Generador de Video Musical", description="Crea videos con efectos de audio sincronizados. Sube un audio y una imagen." ) if __name__ == "__main__": iface.queue().launch()