import os import subprocess import requests import gradio as gr from moviepy.editor import * from datetime import datetime import logging import re import torch from transformers import GPT2LMHeadModel, GPT2Tokenizer # Configuración inicial logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) PEXELS_API_KEY = os.getenv("PEXELS_API_KEY") # Lista de voces válidas VOICES = [ "es-MX-DaliaNeural", "es-ES-ElviraNeural", "es-AR-ElenaNeural", "es-MX-JorgeNeural", "es-ES-AlvaroNeural", "es-AR-TomasNeural", "en-US-JennyNeural", "fr-FR-DeniseNeural", "de-DE-KatjaNeural", "it-IT-ElsaNeural", "pt-BR-FranciscaNeural", "ja-JP-NanamiNeural", "en-GB-SoniaNeural", "es-CL-CatalinaNeural", "es-CO-GonzaloNeural" ] # Cargar modelo y tokenizador de GPT-2 en español try: tokenizer = GPT2Tokenizer.from_pretrained("datificate/gpt2-small-spanish") model = GPT2LMHeadModel.from_pretrained("datificate/gpt2-small-spanish") logger.info("Modelo GPT-2 en español cargado correctamente") except Exception as e: logger.error(f"Error cargando el modelo: {str(e)}") model = None tokenizer = None def generar_guion_largo(tema, custom_script=None): """Genera un texto largo sobre el tema usando GPT-2 con configuración correcta""" if custom_script: return custom_script if model is None or tokenizer is None: return f"Texto generado automáticamente sobre {tema}. " * 50 try: # Prompt directo como solicitaste prompt = f"Escribe un texto largo y detallado sobre {tema}" inputs = tokenizer(prompt, return_tensors="pt", max_length=512, truncation=True) # Generar texto con configuración corregida outputs = model.generate( inputs.input_ids, max_length=800, do_sample=True, # Añadido para habilitar temperature y top_p temperature=0.9, top_k=50, top_p=0.95, num_return_sequences=1, pad_token_id=tokenizer.eos_token_id, early_stopping=True ) guion = tokenizer.decode(outputs[0], skip_special_tokens=True) # Limpiar texto guion = re.sub(r'<.*?>', '', guion) guion = re.sub(r'\n+', '\n', guion) guion = re.sub(r'\s+', ' ', guion).strip() logger.info(f"Guion generado: {len(guion)} caracteres") return guion except Exception as e: logger.error(f"Error generando guion: {str(e)}") return f"Texto generado automáticamente sobre {tema}. " * 50 def buscar_videos_pexels(tema, num_videos=4): """Busca videos en Pexels usando el tema directamente""" try: headers = {"Authorization": PEXELS_API_KEY} logger.info(f"Buscando videos para: {tema}") response = requests.get( f"https://api.pexels.com/videos/search?query={tema}&per_page={num_videos}", headers=headers, timeout=15 ) videos = response.json().get("videos", []) # Filtrar videos de alta calidad return sorted( videos, key=lambda x: (x.get('width', 0) * x.get('height', 0), x.get('duration', 0)), reverse=True )[:num_videos] except Exception as e: logger.error(f"Error buscando videos: {str(e)}") return [] def descargar_video(url, output_path): """Descarga un video de manera eficiente""" try: with requests.get(url, stream=True, timeout=30) as r: r.raise_for_status() with open(output_path, 'wb') as f: for chunk in r.iter_content(chunk_size=8192): f.write(chunk) return True except Exception as e: logger.error(f"Error descargando video: {str(e)}") return False def crear_video(prompt, custom_script, voz_seleccionada, musica=None): try: # 1. Generar guion largo guion = generar_guion_largo(prompt, custom_script) # 2. Generar narración voz_archivo = "voz.mp3" subprocess.run([ 'edge-tts', '--voice', voz_seleccionada, '--text', guion, '--write-media', voz_archivo ], check=True) # 3. Procesar audio principal audio = AudioFileClip(voz_archivo) duracion_total = audio.duration # 4. Buscar videos relevantes videos_data = buscar_videos_pexels(prompt) if not videos_data: logger.warning("No se encontraron videos. Usando videos genéricos...") videos_data = buscar_videos_pexels("nature") # 5. Descargar y preparar videos clips = [] for i, video in enumerate(videos_data): try: # Seleccionar la mejor calidad disponible video_file = max( video['video_files'], key=lambda x: x.get('width', 0) * x.get('height', 0) ) video_url = video_file['link'] temp_path = f"temp_video_{i}.mp4" if descargar_video(video_url, temp_path): clip = VideoFileClip(temp_path) # Calcular duración proporcional para cada clip duracion_clip = duracion_total / len(videos_data) # Ajustar clip a la duración requerida if clip.duration < duracion_clip: clip = clip.loop(duration=duracion_clip) else: clip = clip.subclip(0, duracion_clip) clips.append(clip) except Exception as e: logger.error(f"Error procesando video {i}: {str(e)}") if not clips: raise Exception("No se pudieron cargar videos válidos") # 6. Combinar videos con transiciones suaves final_clip = concatenate_videoclips(clips, method="compose") final_clip = final_clip.set_audio(audio) # 7. Aplicar música de fondo si existe if musica: try: musica_clip = AudioFileClip(musica.name) if musica_clip.duration < duracion_total: musica_clip = musica_clip.loop(duration=duracion_total) else: musica_clip = musica_clip.subclip(0, duracion_total) audio_final = CompositeAudioClip([ audio.volumex(1.0), musica_clip.volumex(0.25) ]) final_clip = final_clip.set_audio(audio_final) except Exception as e: logger.error(f"Error procesando música: {str(e)}") # 8. Exportar video final output_path = f"video_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4" final_clip.write_videofile( output_path, fps=24, codec="libx264", audio_codec="aac", threads=4, preset='fast', ffmpeg_params=['-crf', '23'] # Calidad balanceada ) logger.info(f"Video generado exitosamente: {output_path}") return output_path except Exception as e: logger.error(f"ERROR: {str(e)}") return None finally: # Limpieza garantizada if os.path.exists(voz_archivo): os.remove(voz_archivo) for i in range(5): temp_file = f"temp_video_{i}.mp4" if os.path.exists(temp_file): os.remove(temp_file) # Interfaz mejorada with gr.Blocks(title="Generador de Videos", theme=gr.themes.Soft()) as app: gr.Markdown("# 🎬 GENERADOR AUTOMÁTICO DE VIDEOS") with gr.Row(): with gr.Column(): prompt = gr.Textbox( label="Tema del video", placeholder="Ej: 'La historia de la piratería en el Caribe'", max_lines=1 ) custom_script = gr.TextArea( label="Guion personalizado (opcional)", placeholder="Pega tu guion completo aquí...", lines=8, max_lines=20 ) voz = gr.Dropdown( label="Voz Narradora", choices=VOICES, value=VOICES[0], interactive=True ) musica = gr.File( label="Música de fondo (opcional)", file_types=["audio"], type="filepath" ) btn = gr.Button("Generar Video", variant="primary") with gr.Column(): output = gr.Video( label="Video Resultado", format="mp4", interactive=False ) btn.click( fn=crear_video, inputs=[prompt, custom_script, voz, musica], outputs=output ) if __name__ == "__main__": app.launch( server_name="0.0.0.0", server_port=7860, enable_queue=True )