gnosticdev commited on
Commit
b5186d1
verified
1 Parent(s): ba3f899

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +34 -89
app.py CHANGED
@@ -154,24 +154,10 @@ def extract_keywords(text: str) -> list[str]:
154
  clean_text = re.sub(r"[^\w\s谩茅铆贸煤帽脕脡脥脫脷脩]", "", text.lower())
155
  kws = local_kw_model.extract_keywords(clean_text, stop_words="spanish", top_n=5)
156
  keywords = [k.replace(" ", "+") for k, _ in kws if k]
157
- return keywords if keywords else ["mystery", "conspiracy", "alien", "UFO", "secret", "cover-up", "illusion", "paranoia",
158
- "secret society", "lie", "simulation", "matrix", "terror", "darkness", "shadow", "enigma",
159
- "urban legend", "unknown", "hidden", "mistrust", "experiment", "government", "control",
160
- "surveillance", "propaganda", "deception", "whistleblower", "anomaly", "extraterrestrial",
161
- "shadow government", "cabal", "deep state", "new world order", "mind control", "brainwashing",
162
- "disinformation", "false flag", "assassin", "black ops", "anomaly", "men in black", "abduction",
163
- "hybrid", "ancient aliens", "hollow earth", "simulation theory", "alternate reality", "predictive programming",
164
- "symbolism", "occult", "eerie", "haunting", "unexplained", "forbidden knowledge", "redacted", "conspiracy theorist"]
165
  except Exception as e:
166
  logger.error(f"Error extrayendo keywords: {e}")
167
- return ["mystery", "conspiracy", "alien", "UFO", "secret", "cover-up", "illusion", "paranoia",
168
- "secret society", "lie", "simulation", "matrix", "terror", "darkness", "shadow", "enigma",
169
- "urban legend", "unknown", "hidden", "mistrust", "experiment", "government", "control",
170
- "surveillance", "propaganda", "deception", "whistleblower", "anomaly", "extraterrestrial",
171
- "shadow government", "cabal", "deep state", "new world order", "mind control", "brainwashing",
172
- "disinformation", "false flag", "assassin", "black ops", "anomaly", "men in black", "abduction",
173
- "hybrid", "ancient aliens", "hollow earth", "simulation theory", "alternate reality", "predictive programming",
174
- "symbolism", "occult", "eerie", "haunting", "unexplained", "forbidden knowledge", "redacted", "conspiracy theorist"]
175
 
176
  def search_pexels_videos(query: str, count: int = 3) -> list[dict]:
177
  if not PEXELS_API_KEY:
@@ -211,54 +197,6 @@ def download_video(url: str, folder: str) -> str | None:
211
  logger.error(f"Error descargando video {url}: {e}")
212
  return None
213
 
214
- def create_subtitle_clips(script: str, video_width: int, video_height: int, duration: float):
215
- try:
216
- sentences = [s.strip() for s in re.split(r"[.!?驴隆]", script) if s.strip()]
217
- if not sentences:
218
- return []
219
-
220
- total_words = sum(len(s.split()) for s in sentences) or 1
221
- time_per_word = duration / total_words
222
-
223
- clips = []
224
- current_time = 0.0
225
-
226
- for sentence in sentences:
227
- num_words = len(sentence.split())
228
- sentence_duration = num_words * time_per_word
229
-
230
- if sentence_duration < 0.5:
231
- continue
232
-
233
- try:
234
- txt_clip = (
235
- TextClip(
236
- sentence,
237
- fontsize=max(20, int(video_height * 0.05)),
238
- color="white",
239
- stroke_color="black",
240
- stroke_width=2,
241
- method="caption",
242
- size=(int(video_width * 0.9), None),
243
- font="Arial-Bold"
244
- )
245
- .set_start(current_time)
246
- .set_duration(sentence_duration)
247
- .set_position(("center", "bottom"))
248
- )
249
- if txt_clip is not None:
250
- clips.append(txt_clip)
251
- except Exception as e:
252
- logger.error(f"Error creando subt铆tulo para '{sentence}': {e}")
253
- continue
254
-
255
- current_time += sentence_duration
256
-
257
- return clips
258
- except Exception as e:
259
- logger.error(f"Error creando subt铆tulos: {e}")
260
- return []
261
-
262
  def loop_audio_to_duration(audio_clip: AudioFileClip, target_duration: float) -> AudioFileClip:
263
  if audio_clip is None:
264
  return None
@@ -277,6 +215,7 @@ def create_video(script_text: str, generate_script: bool, music_path: str | None
277
  temp_dir = tempfile.mkdtemp()
278
  TARGET_FPS = 24
279
  TARGET_RESOLUTION = (1280, 720)
 
280
 
281
  def normalize_clip(clip):
282
  if clip is None:
@@ -318,19 +257,22 @@ def create_video(script_text: str, generate_script: bool, music_path: str | None
318
  if video_duration < 1:
319
  raise ValueError("El audio generado es demasiado corto")
320
 
321
- # Paso 3: Buscar y descargar videos
322
  update_task_progress(task_id, "Paso 3/7: Buscando videos en Pexels...")
323
  video_paths = []
324
  keywords = extract_keywords(script)
325
 
326
- for i, keyword in enumerate(keywords[:3]):
327
- update_task_progress(task_id, f"Paso 3/7: Buscando videos para '{keyword}' ({i+1}/{len(keywords[:3])})")
 
 
 
 
 
 
328
 
329
- videos = search_pexels_videos(keyword, 2)
330
  for video_data in videos:
331
- if len(video_paths) >= 6:
332
- break
333
-
334
  video_files = video_data.get("video_files", [])
335
  if video_files:
336
  # Encontrar el video con la mejor calidad que sea MP4
@@ -349,15 +291,21 @@ def create_video(script_text: str, generate_script: bool, music_path: str | None
349
  downloaded_path = download_video(video_url, temp_dir)
350
  if downloaded_path:
351
  video_paths.append(downloaded_path)
 
 
352
 
353
  if not video_paths:
354
  raise RuntimeError("No se pudieron descargar videos de Pexels")
355
 
356
  # Paso 4: Procesar videos - MANEJO CORRECTO DE ERRORES
357
- update_task_progress(task_id, f"Paso 4/7: Procesando {len(video_paths)} videos...")
358
  video_clips = []
 
359
 
360
  for path in video_paths:
 
 
 
361
  clip = None
362
  try:
363
  # Cargar el video con verificaci贸n adicional
@@ -374,10 +322,15 @@ def create_video(script_text: str, generate_script: bool, music_path: str | None
374
  clip.close()
375
  continue
376
 
377
- # Tomar m谩ximo 8 segundos de cada clip
378
  try:
379
- duration = min(8, clip.duration)
380
- processed_clip = clip.subclip(0, duration)
 
 
 
 
 
381
  except Exception as e:
382
  logger.error(f"Error al recortar video {path}: {e}")
383
  clip.close()
@@ -388,6 +341,7 @@ def create_video(script_text: str, generate_script: bool, music_path: str | None
388
  processed_clip = normalize_clip(processed_clip)
389
  if processed_clip is not None:
390
  video_clips.append(processed_clip)
 
391
  else:
392
  if 'processed_clip' in locals():
393
  processed_clip.close()
@@ -411,11 +365,6 @@ def create_video(script_text: str, generate_script: bool, music_path: str | None
411
  # Concatenar videos
412
  base_video = concatenate_videoclips(video_clips, method="chain")
413
 
414
- # Extender video si es m谩s corto que el audio
415
- if base_video.duration < video_duration:
416
- loops_needed = math.ceil(video_duration / base_video.duration)
417
- base_video = concatenate_videoclips([base_video] * loops_needed)
418
-
419
  # Asegurar duraci贸n exacta
420
  base_video = base_video.subclip(0, video_duration)
421
 
@@ -432,14 +381,8 @@ def create_video(script_text: str, generate_script: bool, music_path: str | None
432
  else:
433
  final_audio = voice_clip
434
 
435
- # Paso 6: Agregar subt铆tulos
436
- update_task_progress(task_id, "Paso 6/7: Agregando subt铆tulos...")
437
- subtitle_clips = create_subtitle_clips(script, base_video.w, base_video.h, video_duration)
438
- if subtitle_clips:
439
- base_video = CompositeVideoClip([base_video] + subtitle_clips)
440
-
441
- # Paso 7: Renderizar video final
442
- update_task_progress(task_id, "Paso 7/7: Renderizando video final...")
443
  final_video = base_video.set_audio(final_audio)
444
 
445
  output_path = os.path.join(RESULTS_DIR, f"video_{task_id}.mp4")
@@ -455,6 +398,9 @@ def create_video(script_text: str, generate_script: bool, music_path: str | None
455
  verbose=False
456
  )
457
 
 
 
 
458
  # Limpiar clips
459
  voice_clip.close()
460
  if 'music_clip' in locals():
@@ -564,7 +510,6 @@ with gr.Blocks(title="馃幀 Generador de Videos IA", theme=gr.themes.Soft()) as d
564
  - **Edge TTS** para voz en espa帽ol
565
  - **GPT-2** para generaci贸n de guiones
566
  - **Pexels API** para videos de stock
567
- - **Subt铆tulos autom谩ticos** y efectos visuales
568
 
569
  El progreso se mostrar谩 en tiempo real.
570
  """)
 
154
  clean_text = re.sub(r"[^\w\s谩茅铆贸煤帽脕脡脥脫脷脩]", "", text.lower())
155
  kws = local_kw_model.extract_keywords(clean_text, stop_words="spanish", top_n=5)
156
  keywords = [k.replace(" ", "+") for k, _ in kws if k]
157
+ return keywords if keywords else ["mystery", "conspiracy", "alien", "UFO", "secret", "cover-up", "illusion", "paranoia"]
 
 
 
 
 
 
 
158
  except Exception as e:
159
  logger.error(f"Error extrayendo keywords: {e}")
160
+ return ["mystery", "conspiracy", "alien", "UFO", "secret", "cover-up", "illusion", "paranoia"]
 
 
 
 
 
 
 
161
 
162
  def search_pexels_videos(query: str, count: int = 3) -> list[dict]:
163
  if not PEXELS_API_KEY:
 
197
  logger.error(f"Error descargando video {url}: {e}")
198
  return None
199
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
200
  def loop_audio_to_duration(audio_clip: AudioFileClip, target_duration: float) -> AudioFileClip:
201
  if audio_clip is None:
202
  return None
 
215
  temp_dir = tempfile.mkdtemp()
216
  TARGET_FPS = 24
217
  TARGET_RESOLUTION = (1280, 720)
218
+ MAX_CLIP_DURATION = 8 # M谩ximo de segundos por clip
219
 
220
  def normalize_clip(clip):
221
  if clip is None:
 
257
  if video_duration < 1:
258
  raise ValueError("El audio generado es demasiado corto")
259
 
260
+ # Paso 3: Buscar y descargar videos (adaptado a la duraci贸n del audio)
261
  update_task_progress(task_id, "Paso 3/7: Buscando videos en Pexels...")
262
  video_paths = []
263
  keywords = extract_keywords(script)
264
 
265
+ # Calcular cu谩ntos clips necesitamos aproximadamente
266
+ estimated_clips_needed = max(1, math.ceil(video_duration / MAX_CLIP_DURATION))
267
+
268
+ for i, keyword in enumerate(keywords):
269
+ if len(video_paths) >= estimated_clips_needed * 2: # Buscar el doble para tener opciones
270
+ break
271
+
272
+ update_task_progress(task_id, f"Paso 3/7: Buscando videos para '{keyword}' ({i+1}/{len(keywords)})")
273
 
274
+ videos = search_pexels_videos(keyword, 3) # Buscar 3 videos por keyword
275
  for video_data in videos:
 
 
 
276
  video_files = video_data.get("video_files", [])
277
  if video_files:
278
  # Encontrar el video con la mejor calidad que sea MP4
 
291
  downloaded_path = download_video(video_url, temp_dir)
292
  if downloaded_path:
293
  video_paths.append(downloaded_path)
294
+ if len(video_paths) >= estimated_clips_needed * 2:
295
+ break
296
 
297
  if not video_paths:
298
  raise RuntimeError("No se pudieron descargar videos de Pexels")
299
 
300
  # Paso 4: Procesar videos - MANEJO CORRECTO DE ERRORES
301
+ update_task_progress(task_id, f"Paso 4/7: Procesando videos...")
302
  video_clips = []
303
+ total_duration = 0
304
 
305
  for path in video_paths:
306
+ if total_duration >= video_duration:
307
+ break
308
+
309
  clip = None
310
  try:
311
  # Cargar el video con verificaci贸n adicional
 
322
  clip.close()
323
  continue
324
 
325
+ # Tomar m谩ximo 8 segundos de cada clip o lo que necesitemos
326
  try:
327
+ clip_duration = min(MAX_CLIP_DURATION, clip.duration)
328
+ # Si ya tenemos suficiente duraci贸n, tomar solo lo necesario
329
+ remaining_duration = video_duration - total_duration
330
+ if remaining_duration < clip_duration:
331
+ clip_duration = remaining_duration
332
+
333
+ processed_clip = clip.subclip(0, clip_duration)
334
  except Exception as e:
335
  logger.error(f"Error al recortar video {path}: {e}")
336
  clip.close()
 
341
  processed_clip = normalize_clip(processed_clip)
342
  if processed_clip is not None:
343
  video_clips.append(processed_clip)
344
+ total_duration += processed_clip.duration
345
  else:
346
  if 'processed_clip' in locals():
347
  processed_clip.close()
 
365
  # Concatenar videos
366
  base_video = concatenate_videoclips(video_clips, method="chain")
367
 
 
 
 
 
 
368
  # Asegurar duraci贸n exacta
369
  base_video = base_video.subclip(0, video_duration)
370
 
 
381
  else:
382
  final_audio = voice_clip
383
 
384
+ # Paso 6: Renderizar video final
385
+ update_task_progress(task_id, "Paso 6/7: Renderizando video final...")
 
 
 
 
 
 
386
  final_video = base_video.set_audio(final_audio)
387
 
388
  output_path = os.path.join(RESULTS_DIR, f"video_{task_id}.mp4")
 
398
  verbose=False
399
  )
400
 
401
+ # Paso 7: Limpiar recursos
402
+ update_task_progress(task_id, "Paso 7/7: Finalizando...")
403
+
404
  # Limpiar clips
405
  voice_clip.close()
406
  if 'music_clip' in locals():
 
510
  - **Edge TTS** para voz en espa帽ol
511
  - **GPT-2** para generaci贸n de guiones
512
  - **Pexels API** para videos de stock
 
513
 
514
  El progreso se mostrar谩 en tiempo real.
515
  """)