gnosticdev commited on
Commit
d5141b3
·
verified ·
1 Parent(s): 6c4f60d

Update app.py

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