Spaces:
Sleeping
Sleeping
| import os | |
| import asyncio | |
| import logging | |
| import tempfile | |
| import requests | |
| from datetime import datetime | |
| from moviepy.editor import VideoFileClip, concatenate_videoclips, AudioFileClip, CompositeAudioClip, afx | |
| import edge_tts | |
| import gradio as gr | |
| from transformers import GPT2Tokenizer, GPT2LMHeadModel | |
| import torch | |
| logging.basicConfig(level=logging.INFO) | |
| logger = logging.getLogger(__name__) | |
| # Inicializa tokenizer y modelo (puedes cambiar modelo si quieres) | |
| tokenizer = GPT2Tokenizer.from_pretrained("gpt2") | |
| model = GPT2LMHeadModel.from_pretrained("gpt2").eval() | |
| def generate_script(prompt, max_length=300): | |
| logger.info("Generando guion...") | |
| inputs = tokenizer(prompt, return_tensors="pt", truncation=False) | |
| with torch.no_grad(): | |
| outputs = model.generate( | |
| **inputs, | |
| max_length=max_length, | |
| do_sample=True, | |
| top_p=0.95, | |
| top_k=60, | |
| temperature=0.9, | |
| pad_token_id=tokenizer.eos_token_id | |
| ) | |
| text = tokenizer.decode(outputs[0], skip_special_tokens=True) | |
| logger.info(f"Guion generado, longitud: {len(text)} caracteres") | |
| return text | |
| async def text_to_speech(text, voice="es-ES-ElviraNeural", output_path="voz.mp3"): | |
| logger.info("Generando audio TTS...") | |
| communicate = edge_tts.Communicate(text, voice) | |
| await communicate.save(output_path) | |
| logger.info(f"Audio guardado en {output_path}") | |
| def download_video_sample(url): | |
| logger.info(f"Descargando video de ejemplo: {url}") | |
| tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") | |
| response = requests.get(url, stream=True) | |
| for chunk in response.iter_content(chunk_size=1024*1024): | |
| tmp.write(chunk) | |
| tmp.close() | |
| return tmp.name | |
| def loop_audio_to_length(audio_clip, target_duration): | |
| if audio_clip.duration >= target_duration: | |
| return audio_clip.subclip(0, target_duration) | |
| loops = int(target_duration // audio_clip.duration) + 1 | |
| audios = [audio_clip] * loops | |
| concatenated = concatenate_videoclips(audios, method="compose") | |
| return concatenated.subclip(0, target_duration) | |
| def crear_video(prompt, musica_url=None): | |
| # 1. Generar guion | |
| guion = generate_script(prompt, max_length=300) | |
| # 2. TTS | |
| voz_archivo = "voz.mp3" | |
| asyncio.run(text_to_speech(guion, output_path=voz_archivo)) | |
| # 3. Descargar videos de ejemplo (puedes reemplazar por tu búsqueda real) | |
| # Aquí pongo 3 clips de ejemplo (deberías poner tus URLs) | |
| video_urls = [ | |
| "https://sample-videos.com/video123/mp4/240/big_buck_bunny_240p_1mb.mp4", | |
| "https://sample-videos.com/video123/mp4/240/big_buck_bunny_240p_1mb.mp4", | |
| "https://sample-videos.com/video123/mp4/240/big_buck_bunny_240p_1mb.mp4" | |
| ] | |
| clips = [] | |
| for url in video_urls[:3]: | |
| video_path = download_video_sample(url) | |
| clip = VideoFileClip(video_path).subclip(0, 10) # máximo 10 segundos | |
| clips.append(clip) | |
| # 4. Concatenar videos | |
| video_final = concatenate_videoclips(clips, method="compose") | |
| # 5. Cargar audio TTS | |
| audio_tts = AudioFileClip(voz_archivo) | |
| # 6. Música de fondo en loop si está definida | |
| if musica_url: | |
| musica_path = download_video_sample(musica_url) | |
| musica_audio = AudioFileClip(musica_path) | |
| # Loop música a duración voz | |
| musica_loop = loop_audio_to_length(musica_audio, audio_tts.duration) | |
| # Mezclar audio TTS y música | |
| mezcla = CompositeAudioClip([musica_loop.volumex(0.3), audio_tts.volumex(1.0)]) | |
| else: | |
| mezcla = audio_tts | |
| # 7. Asignar audio al video | |
| video_final = video_final.set_audio(mezcla).subclip(0, audio_tts.duration) | |
| # 8. Guardar video final | |
| output_path = f"video_output_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4" | |
| video_final.write_videofile(output_path, fps=24, threads=2, logger=None) | |
| # 9. Limpiar archivos temporales | |
| os.remove(voz_archivo) | |
| for clip in clips: | |
| clip.close() | |
| return output_path | |
| def run_app(prompt, musica_url): | |
| logger.info(f"Entrada recibida: {prompt}") | |
| try: | |
| video_path = crear_video(prompt, musica_url if musica_url.strip() else None) | |
| logger.info(f"Video generado en: {video_path}") | |
| return video_path | |
| except Exception as e: | |
| logger.error(f"Error durante la generación: {e}") | |
| return None | |
| with gr.Blocks() as app: | |
| gr.Markdown("### Generador simple de video con texto, voz y música en loop") | |
| with gr.Row(): | |
| prompt_input = gr.Textbox(label="Introduce el tema para generar el guion", lines=2) | |
| musica_input = gr.Textbox(label="URL de música (opcional) para usar de fondo") | |
| boton = gr.Button("Generar video") | |
| salida = gr.Video(label="Video generado") | |
| boton.click(run_app, inputs=[prompt_input, musica_input], outputs=salida) | |
| if __name__ == "__main__": | |
| app.launch(server_name="0.0.0.0", server_port=7860) | |