Spaces:
Running
Running
| import gradio as gr | |
| import asyncio | |
| import edge_tts | |
| import requests | |
| import tempfile | |
| import os | |
| from datetime import datetime | |
| from moviepy.editor import VideoFileClip, concatenate_videoclips, AudioFileClip, CompositeAudioClip | |
| # Voz predeterminada (puedes cambiarla o cargar lista) | |
| VOCES = [{'ShortName': 'es-ES-ElviraNeural', 'Name': 'Elvira', 'Gender': 'Female'}] | |
| VOICE_NAMES = [f"{v['Name']} ({v['Gender']})" for v in VOCES] | |
| # Simula función para generar texto según prompt | |
| def generar_texto(prompt): | |
| # Aquí deberías integrar tu generador real, por ahora solo repetimos prompt | |
| return f"Este es un texto generado para el tema: {prompt}" | |
| # Simula función para buscar videos (debes conectar con Pexels u otra API) | |
| def buscar_videos(prompt): | |
| # Retorna lista simulada con links a videos (debes poner tu API real) | |
| return [ | |
| { | |
| "video_files": [{"link": "https://filesamples.com/samples/video/mp4/sample_640x360.mp4"}], | |
| "duration": 10 | |
| }, | |
| { | |
| "video_files": [{"link": "https://filesamples.com/samples/video/mp4/sample_640x360.mp4"}], | |
| "duration": 10 | |
| } | |
| ] | |
| async def crear_video(prompt, voz_index, musica_path=None): | |
| try: | |
| texto = generar_texto(prompt) | |
| voz_shortname = VOCES[voz_index]['ShortName'] | |
| # Generar audio TTS | |
| archivo_audio = "audio.mp3" | |
| await edge_tts.Communicate(texto, voz_shortname).save(archivo_audio) | |
| audio_clip = AudioFileClip(archivo_audio) | |
| duracion_audio = audio_clip.duration | |
| # Cargar música si se pasa | |
| if musica_path: | |
| musica_clip = AudioFileClip(musica_path).volumex(0.2) # Volumen bajo para música | |
| musica_clip = musica_clip.subclip(0, duracion_audio) | |
| audio_final = CompositeAudioClip([musica_clip, audio_clip]) | |
| else: | |
| audio_final = audio_clip | |
| # Descargar y preparar videos | |
| videos = buscar_videos(prompt) | |
| if not videos: | |
| return None | |
| clips = [] | |
| for v in videos[:3]: | |
| video_url = v['video_files'][0]['link'] | |
| response = requests.get(video_url, stream=True) | |
| temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") | |
| for chunk in response.iter_content(chunk_size=1024*1024): | |
| temp_file.write(chunk) | |
| temp_file.close() | |
| clip = VideoFileClip(temp_file.name).subclip(0, min(duracion_audio/len(videos), v['duration'])) | |
| clips.append(clip) | |
| video_final = concatenate_videoclips(clips) | |
| video_final = video_final.set_audio(audio_final) | |
| output_filename = f"video_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4" | |
| video_final.write_videofile(output_filename, codec="libx264", audio_codec="aac", fps=24) | |
| # Limpieza | |
| audio_clip.close() | |
| if musica_path: | |
| musica_clip.close() | |
| for c in clips: | |
| c.close() | |
| os.remove(c.filename) | |
| os.remove(archivo_audio) | |
| return output_filename | |
| except Exception as e: | |
| return f"Error: {e}" | |
| def run_crear_video(prompt, voz_nombre, musica_file): | |
| try: | |
| voz_index = VOICE_NAMES.index(voz_nombre) | |
| except ValueError: | |
| voz_index = 0 | |
| musica_path = musica_file.name if musica_file else None | |
| return asyncio.run(crear_video(prompt, voz_index, musica_path)) | |
| with gr.Blocks(title="Generador de Video con TTS y Música de Fondo") as demo: | |
| with gr.Row(): | |
| with gr.Column(): | |
| prompt = gr.Textbox(label="Tema del video", lines=2) | |
| voz = gr.Dropdown(VOICE_NAMES, label="Voz", value=VOICE_NAMES[0]) | |
| musica = gr.File(label="Sube música de fondo (opcional, mp3/wav)", file_types=[".mp3", ".wav"]) | |
| btn = gr.Button("Generar Video") | |
| with gr.Column(): | |
| salida = gr.Video(label="Video generado") | |
| btn.click(run_crear_video, inputs=[prompt, voz, musica], outputs=salida) | |
| if __name__ == "__main__": | |
| demo.launch(server_name="0.0.0.0", server_port=7860) | |