Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import subprocess | |
| import os | |
| import uuid | |
| # Carpeta temporal para guardar los videos generados | |
| TEMP_DIR = "temp_videos" | |
| os.makedirs(TEMP_DIR, exist_ok=True) | |
| def get_audio_duration(audio_path): | |
| """Obtiene duración del audio en segundos usando ffprobe""" | |
| try: | |
| result = subprocess.run([ | |
| "ffprobe", "-v", "error", "-show_entries", | |
| "format=duration", "-of", | |
| "default=noprint_wrappers=1:nokey=1", audio_path | |
| ], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) | |
| return float(result.stdout.strip()) | |
| except: | |
| return None | |
| def generate_video(audio_path, image_path): | |
| if not os.path.isfile(audio_path) or not os.path.isfile(image_path): | |
| return "❌ Missing or invalid input files." | |
| duration = get_audio_duration(audio_path) | |
| if not duration: | |
| return "❌ Could not get audio duration. Check your audio file." | |
| output_filename = os.path.join("temp_videos", f"output_{uuid.uuid4().hex[:8]}.mp4") | |
| try: | |
| cmd = [ | |
| "ffmpeg", "-y", | |
| "-loop", "1", "-framerate", "1", "-i", image_path, | |
| "-i", audio_path, | |
| "-vf", "scale=trunc(iw/2)*2:trunc(ih/2)*2", # 👈 FIX AQUI | |
| "-c:v", "libx264", "-preset", "ultrafast", "-tune", "stillimage", | |
| "-c:a", "aac", "-b:a", "192k", | |
| "-t", str(duration), | |
| "-pix_fmt", "yuv420p", "-r", "1", | |
| output_filename | |
| ] | |
| result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) | |
| if os.path.exists(output_filename) and os.path.getsize(output_filename) > 0: | |
| return output_filename | |
| else: | |
| print("FFmpeg ERROR:", result.stderr) | |
| return "❌ Something went wrong while generating the video." | |
| except Exception as e: | |
| return f"⚠️ Error: {str(e)}" | |
| # Interfaz Gradio | |
| with gr.Blocks(theme="Taithrah/Minimal") as app: | |
| gr.Markdown("# 🎶 Audio to Video Tool\nUpload an audio file and an image to generate a simple MP4 video (like TunesToTube)") | |
| with gr.Row(): | |
| audio = gr.Audio(label="🎧 Upload your audio file", type="filepath") | |
| image = gr.Image(label="🖼️ Upload your image", type="filepath") | |
| submit = gr.Button("🚀 Generate Video") | |
| output = gr.Video(label="🎬 Your Video") | |
| def handle_generate(audio_path, image_path): | |
| result = generate_video(audio_path, image_path) | |
| # Si es una string con error, la mandamos como mensaje, si es ruta, se muestra el video | |
| if result.endswith(".mp4") and os.path.exists(result): | |
| return result | |
| else: | |
| return gr.update(value=None, visible=False), gr.update(value=result, visible=True) | |
| submit.click(fn=handle_generate, inputs=[audio, image], outputs=output) | |
| app.launch() |