audio-a-video / back app.py
gnosticdev's picture
Rename app.py to back app.py
e28ca61 verified
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()