Spaces:
Running
Running
File size: 11,084 Bytes
43fcbe8 fa691a5 38ff849 8337d0b fa691a5 07b3b3d 38ff849 53ae22b fa691a5 53ae22b c1f9283 fa691a5 53ae22b c1f9283 fa691a5 38ff849 fa691a5 53ae22b 38ff849 77ffd33 38ff849 53ae22b 07b3b3d 53ae22b fa691a5 53ae22b fa691a5 53ae22b 07b3b3d 53ae22b fa691a5 53ae22b fa691a5 53ae22b c7b9a72 fa691a5 38ff849 53ae22b fa691a5 53ae22b 8337d0b 19224f2 53ae22b c7b9a72 53ae22b fa691a5 53ae22b fa691a5 53ae22b fa691a5 53ae22b fa691a5 53ae22b fa691a5 19224f2 fa691a5 c1f9283 fa691a5 53ae22b fa691a5 53ae22b fa691a5 c1f9283 53ae22b fa691a5 53ae22b 8337d0b 38ff849 fa691a5 38ff849 fa691a5 07b3b3d fa691a5 38ff849 fa691a5 38ff849 fa691a5 07b3b3d 53ae22b 38ff849 53ae22b 8337d0b 53ae22b fa691a5 53ae22b 38ff849 53ae22b 8337d0b 53ae22b 38ff849 53ae22b 38ff849 fa201eb 38ff849 fa201eb 38ff849 720c3d5 07b3b3d 53ae22b 07b3b3d c9d2e08 c1f9283 fa691a5 53ae22b d7f3a60 38ff849 fa691a5 53ae22b 38ff849 53ae22b fa691a5 38ff849 53ae22b fa691a5 38ff849 53ae22b 38ff849 53ae22b 38ff849 53ae22b 38ff849 53ae22b 8337d0b c1f9283 38ff849 518f669 9e5ee0a d7f3a60 07b3b3d |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 |
import os
import re
import requests
import gradio as gr
from moviepy.editor import *
import edge_tts
import tempfile
import logging
from datetime import datetime
import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
import nltk
import random
from transformers import pipeline
import torch
import asyncio # ¡Importación crítica que faltaba!
# Configuración inicial
nltk.download('punkt', quiet=True)
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
# Configuración de modelos
PEXELS_API_KEY = os.getenv("PEXELS_API_KEY")
MODEL_NAME = "DeepESP/gpt2-spanish" # Modelo en español más ligero
# Lista de voces disponibles (versión simplificada)
async def get_voices():
return await edge_tts.list_voices()
VOICES = asyncio.run(get_voices())
VOICE_NAMES = [v['ShortName'] for v in VOICES] # Solo los nombres cortos de las voces
def generar_guion_profesional(prompt):
"""Genera guiones detallados con sistema de 3 niveles"""
try:
# 1. Intento con modelo principal
generator = pipeline(
"text-generation",
model=MODEL_NAME,
device=0 if torch.cuda.is_available() else -1
)
response = generator(
f"Escribe un guion profesional para un video de YouTube sobre '{prompt}'. "
"La estructura debe incluir:\n"
"1. Introducción atractiva\n"
"2. Tres secciones detalladas con subtítulos\n"
"3. Conclusión impactante\n"
"Usa un estilo natural para narración:",
max_length=1000,
temperature=0.7,
top_k=50,
top_p=0.95,
num_return_sequences=1
)
guion = response[0]['generated_text']
# 2. Verificar calidad del guion
if len(guion.split()) < 100: # Si es muy corto
raise ValueError("Guion demasiado breve")
return guion
except Exception as e:
logger.error(f"Error generando guion: {str(e)}")
# 3. Respaldos inteligentes
temas = {
"historia": ["orígenes", "eventos clave", "impacto actual"],
"tecnología": ["funcionamiento", "aplicaciones", "futuro"],
"ciencia": ["teorías", "evidencia", "implicaciones"],
"misterio": ["enigma", "teorías", "explicaciones"],
"arte": ["orígenes", "características", "influencia"]
}
# Detectar categoría del tema
categoria = "general"
for key in temas:
if key in prompt.lower():
categoria = key
break
puntos_clave = temas.get(categoria, ["aspectos importantes", "datos relevantes", "conclusiones"])
# Generar guion de respaldo con estructura profesional
return f"""
¡Hola a todos! Bienvenidos a este análisis completo sobre {prompt}.
En este video exploraremos a fondo este fascinante tema a través de tres secciones clave.
SECCIÓN 1: {puntos_clave[0].capitalize()}
Comenzaremos analizando los {puntos_clave[0]} fundamentales.
Esto nos permitirá entender mejor la base de {prompt}.
SECCIÓN 2: {puntos_clave[1].capitalize()}
En esta parte, examinaremos los {puntos_clave[1]} más relevantes
y cómo se relacionan con el tema principal.
SECCIÓN 3: {puntos_clave[2].capitalize()}
Finalmente, exploraremos las {puntos_clave[2]}
y qué significan para el futuro de este campo.
¿Listos para profundizar? ¡Empecemos!
"""
def buscar_videos_avanzado(prompt, guion, num_videos=5):
"""Búsqueda inteligente de videos usando análisis de contenido"""
try:
# Dividir el guion en oraciones
oraciones = sent_tokenize(guion)
# Extraer palabras clave con TF-IDF
vectorizer = TfidfVectorizer(stop_words=['el', 'la', 'los', 'las', 'de', 'en', 'y', 'que'])
tfidf = vectorizer.fit_transform(oraciones)
palabras = vectorizer.get_feature_names_out()
scores = np.asarray(tfidf.sum(axis=0)).ravel()
indices_importantes = np.argsort(scores)[-5:]
palabras_clave = [palabras[i] for i in indices_importantes]
# Mezclar palabras clave del prompt y del guion
palabras_prompt = re.findall(r'\b\w{4,}\b', prompt.lower())
todas_palabras = list(set(palabras_clave + palabras_prompt))[:5]
# Buscar en Pexels
headers = {"Authorization": PEXELS_API_KEY}
response = requests.get(
f"https://api.pexels.com/videos/search?query={'+'.join(todas_palabras)}&per_page={num_videos}",
headers=headers,
timeout=15
)
videos = response.json().get('videos', [])
logger.info(f"Palabras clave usadas: {todas_palabras}")
# Seleccionar videos de mejor calidad
videos_ordenados = sorted(
videos,
key=lambda x: x.get('width', 0) * x.get('height', 0),
reverse=True
)
return videos_ordenados[:num_videos]
except Exception as e:
logger.error(f"Error en búsqueda de videos: {str(e)}")
# Búsqueda simple de respaldo
response = requests.get(
f"https://api.pexels.com/videos/search?query={prompt}&per_page={num_videos}",
headers={"Authorization": PEXELS_API_KEY},
timeout=10
)
return response.json().get('videos', [])[:num_videos]
async def crear_video_profesional(prompt, custom_script, voz_index, musica=None):
try:
# 1. Generar o usar guion
guion = custom_script if custom_script else generar_guion_profesional(prompt)
logger.info(f"Guion generado ({len(guion.split())} palabras)")
# 2. Seleccionar voz
voz_seleccionada = VOICES[voz_index]['ShortName']
# 3. Generar voz
voz_archivo = "voz.mp3"
await edge_tts.Communicate(guion, voz_seleccionada).save(voz_archivo)
audio = AudioFileClip(voz_archivo)
duracion_total = audio.duration
# 4. Buscar videos relevantes
videos_data = buscar_videos_avanzado(prompt, guion)
if not videos_data:
raise Exception("No se encontraron videos relevantes")
# 5. Descargar y preparar videos
clips = []
for video in videos_data:
# Seleccionar la mejor calidad de video
video_files = sorted(
video['video_files'],
key=lambda x: x.get('width', 0) * x.get('height', 0),
reverse=True
)
video_url = video_files[0]['link']
# Descargar video
response = requests.get(video_url, stream=True)
temp_video = tempfile.NamedTemporaryFile(delete=False, suffix='.mp4')
for chunk in response.iter_content(chunk_size=1024*1024):
temp_video.write(chunk)
temp_video.close()
# Crear clip
clip = VideoFileClip(temp_video.name)
clips.append(clip)
# 6. Calcular duración por clip
duracion_por_clip = duracion_total / len(clips)
# 7. Procesar clips de video
clips_procesados = []
for clip in clips:
# Si el clip es más corto que la duración asignada, hacer loop
if clip.duration < duracion_por_clip:
clip = clip.loop(duration=duracion_por_clip)
# Si es más largo, recortar
else:
clip = clip.subclip(0, duracion_por_clip)
clips_procesados.append(clip)
# 8. Combinar videos
video_final = concatenate_videoclips(clips_procesados)
# 9. Procesar música
if musica:
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 = CompositeAudioClip([audio, musica_clip.volumex(0.25)])
video_final = video_final.set_audio(audio)
# 10. Exportar video
output_path = f"video_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4"
video_final.write_videofile(
output_path,
codec="libx264",
audio_codec="aac",
threads=2,
preset='fast',
fps=24
)
return output_path
except Exception as e:
logger.error(f"ERROR: {str(e)}")
return None
finally:
# Limpieza de archivos temporales
if os.path.exists(voz_archivo):
os.remove(voz_archivo)
def run_async_func(prompt, custom_script, voz_index, musica=None):
return asyncio.run(crear_video_profesional(prompt, custom_script, voz_index, musica))
# Interfaz profesional
with gr.Blocks(theme=gr.themes.Soft(), title="Generador de Videos Profesional") as app:
gr.Markdown("# 🎬 GENERADOR DE VIDEOS CON IA")
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("### Configuración del Contenido")
prompt = gr.Textbox(label="Tema principal", placeholder="Ej: 'Los misterios de la antigua Grecia'")
custom_script = gr.TextArea(
label="Guion personalizado (opcional)",
placeholder="Pega aquí tu propio guion completo...",
lines=8
)
voz = gr.Dropdown(
label="Selecciona una voz",
choices=VOICE_NAMES,
value=VOICE_NAMES[0],
type="index"
)
musica = gr.File(
label="Música de fondo (opcional)",
file_types=["audio"]
)
btn = gr.Button("🚀 Generar Video", variant="primary", size="lg")
with gr.Column(scale=2):
output = gr.Video(
label="Video Resultante",
format="mp4",
interactive=False
)
gr.Examples(
examples=[
["Los secretos de las pirámides egipcias", "", 5, None],
["La inteligencia artificial en medicina", "", 3, None],
["Lugares abandonados más misteriosos", "", 8, None]
],
inputs=[prompt, custom_script, voz, musica],
label="Ejemplos: Haz clic en uno y luego en Generar"
)
btn.click(
fn=run_async_func,
inputs=[prompt, custom_script, voz, musica],
outputs=output
)
if __name__ == "__main__":
app.launch(server_name="0.0.0.0", server_port=7860) |