Spaces:
Sleeping
Sleeping
File size: 11,933 Bytes
43fcbe8 77c11ae 38ff849 8337d0b fa691a5 77c11ae 38ff849 77c11ae d5141b3 77c11ae 9143db2 d5141b3 fa691a5 77c11ae 38ff849 77c11ae d5141b3 38ff849 e96d058 d5141b3 d68572a 77c11ae d5141b3 38ff849 77c11ae d5141b3 77c11ae d5141b3 d68572a d5141b3 c7b9a72 d5141b3 77c11ae d5141b3 60e6f97 77c11ae d68572a d5141b3 77c11ae 7c87717 9143db2 d5141b3 77c11ae d5141b3 d68572a d5141b3 77c11ae 60e6f97 77c11ae 38ff849 d5141b3 77c11ae d5141b3 77c11ae 9143db2 7c87717 d68572a d5141b3 77c11ae 7c87717 77c11ae 38ff849 d68572a d5141b3 38ff849 fa201eb 77c11ae fa201eb 77c11ae 720c3d5 07b3b3d 30c3706 d5141b3 7c87717 c9d2e08 d5141b3 77c11ae d5141b3 77c11ae 7c87717 77c11ae d5141b3 77c11ae d5141b3 77c11ae d5141b3 77c11ae d5141b3 77c11ae d5141b3 77c11ae d5141b3 77c11ae d5141b3 77c11ae d5141b3 8337d0b d5141b3 77c11ae d5141b3 9e5ee0a d7f3a60 07b3b3d d5141b3 |
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 300 301 302 303 304 305 306 307 308 309 |
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
VOICES = asyncio.run(edge_tts.list_voices()) # Ahora funciona correctamente
VOICE_NAMES = [f"{v['Name']} ({v['Gender']}, {v['LocaleName']})" for v in VOICES]
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 = nltk.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, progress=gr.Progress()):
"""SOLUCIÓN: Añadido parámetro progress para mantener la conexión activa"""
try:
# 1. Generar o usar guion (con progreso)
progress(0.1, desc="Generando 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 (con progreso)
progress(0.3, desc="Generando 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 (con progreso)
progress(0.4, desc="Buscando videos...")
videos_data = buscar_videos_avanzado(prompt, guion)
if not videos_data:
raise Exception("No se encontraron videos relevantes")
# 5. Descargar y preparar videos (con progreso)
clips = []
total_videos = len(videos_data)
for i, video in enumerate(videos_data):
progress(0.5 + (i * 0.4 / total_videos), desc=f"Descargando video {i+1}/{total_videos}...")
# 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 (con progreso)
progress(0.8, desc="Procesando videos...")
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 (con progreso)
progress(0.9, desc="Añadiendo 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 (con progreso)
progress(0.95, desc="Exportando video final...")
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, progress=gr.Progress()):
"""SOLUCIÓN: Añadido parámetro progress para mantener la conexión activa"""
return asyncio.run(crear_video_profesional(prompt, custom_script, voz_index, musica, progress))
# 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"
)
# SOLUCIÓN: Añadido parámetro progress para mantener la conexión activa
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) |