gnosticdev commited on
Commit
506f1df
·
verified ·
1 Parent(s): e098d46

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +33 -27
app.py CHANGED
@@ -9,7 +9,7 @@ import gradio as gr
9
  import torch
10
  from transformers import GPT2Tokenizer, GPT2LMHeadModel
11
  from keybert import KeyBERT
12
- # CORRECCIÓN CRÍTICA DEFINITIVA: Eliminar 'concatenate_videoclip' (singular) de la importación
13
  from moviepy.editor import VideoFileClip, concatenate_videoclips, AudioFileClip, CompositeAudioClip, concatenate_audioclips, AudioClip
14
  import re
15
  import math
@@ -35,7 +35,7 @@ logger.info("="*80)
35
  PEXELS_API_KEY = os.environ.get("PEXELS_API_KEY")
36
  if not PEXELS_API_KEY:
37
  logger.critical("NO SE ENCONTRÓ PEXELS_API_KEY EN VARIABLES DE ENTORNO")
38
- # raise ValueError("API key de Pexels no configurada") # Descomentar para forzar fallo si no está
39
 
40
  # Inicialización de modelos
41
  MODEL_NAME = "datificate/gpt2-small-spanish"
@@ -387,21 +387,19 @@ def crear_video(prompt_type, input_text, musica_file=None):
387
  primary_voice = "es-ES-JuanNeural"
388
  fallback_voice = "es-ES-ElviraNeural" # Otra voz en español
389
  tts_success = False
390
- retries = 3 # Número de intentos
391
 
392
  for attempt in range(retries):
393
  current_voice = primary_voice if attempt == 0 else fallback_voice
394
  if attempt > 0: logger.warning(f"Reintentando TTS ({attempt+1}/{retries})...")
395
  logger.info(f"Intentando TTS con voz: {current_voice}")
396
  try:
397
- # Llamar a la función async text_to_speech
398
  tts_success = asyncio.run(text_to_speech(guion, voz_path, voice=current_voice))
399
  if tts_success:
400
  logger.info(f"TTS exitoso en intento {attempt + 1} con voz {current_voice}.")
401
- break # Salir del bucle de reintentos si tiene éxito
402
  except Exception as e:
403
- # La excepción ya se registra dentro de text_to_speech
404
- pass # Continuar al siguiente intento
405
 
406
  if not tts_success and attempt == 0 and primary_voice != fallback_voice:
407
  logger.warning(f"Fallo con voz {primary_voice}, intentando voz de respaldo: {fallback_voice}")
@@ -409,14 +407,12 @@ def crear_video(prompt_type, input_text, musica_file=None):
409
  logger.warning(f"Fallo con voz {current_voice}, reintentando...")
410
 
411
 
412
- # Verificar si el archivo fue creado después de todos los intentos
413
  if not tts_success or not os.path.exists(voz_path) or os.path.getsize(voz_path) <= 100:
414
  logger.error(f"Fallo en la generación de voz después de {retries} intentos. Archivo de audio no creado o es muy pequeño.")
415
  raise ValueError("Error generando voz a partir del guion (fallo de TTS).")
416
 
417
- temp_intermediate_files.append(voz_path) # Añadir a la lista de limpieza si se creó
418
 
419
- # Continuar cargando el archivo de audio generado
420
  audio_tts_original = AudioFileClip(voz_path)
421
 
422
  if audio_tts_original.reader is None or audio_tts_original.duration is None or audio_tts_original.duration <= 0:
@@ -709,8 +705,8 @@ def crear_video(prompt_type, input_text, musica_file=None):
709
 
710
  if musica_audio_looped:
711
  composite_audio = CompositeAudioClip([
712
- musica_audio_looped.volumex(0.2),
713
- audio_tts_original.volumex(1.0)
714
  ])
715
 
716
  if composite_audio.duration is None or composite_audio.duration <= 0:
@@ -721,7 +717,7 @@ def crear_video(prompt_type, input_text, musica_file=None):
721
  else:
722
  logger.info("Mezcla de audio completada (voz + música).")
723
  final_audio = composite_audio
724
- musica_audio = musica_audio_looped
725
 
726
  except Exception as e:
727
  logger.warning(f"Error procesando música de fondo: {str(e)}", exc_info=True)
@@ -847,12 +843,12 @@ def crear_video(prompt_type, input_text, musica_file=None):
847
  logger.info(f"Directorio temporal intermedio {temp_dir_intermediate} persistirá para que Gradio lea el video final.")
848
 
849
 
850
- # CAMBIO CRÍTICO: run_app define 4 argumentos
851
  def run_app(prompt_type, prompt_ia, prompt_manual, musica_file):
852
  logger.info("="*80)
853
  logger.info("SOLICITUD RECIBIDA EN INTERFAZ")
854
 
855
- # La lógica para elegir el texto de entrada YA ESTÁ AQUÍ y funciona con ambos inputs pasados
856
  input_text = prompt_ia if prompt_type == "Generar Guion con IA" else prompt_manual
857
 
858
  output_video = None
@@ -861,6 +857,7 @@ def run_app(prompt_type, prompt_ia, prompt_manual, musica_file):
861
 
862
  if not input_text or not input_text.strip():
863
  logger.warning("Texto de entrada vacío.")
 
864
  return None, None, gr.update(value="⚠️ Por favor, ingresa texto para el guion o el tema.", interactive=False)
865
 
866
  logger.info(f"Tipo de entrada: {prompt_type}")
@@ -872,14 +869,14 @@ def run_app(prompt_type, prompt_ia, prompt_manual, musica_file):
872
 
873
  try:
874
  logger.info("Llamando a crear_video...")
875
- # Pasar el input_text elegido y el archivo de música
876
  video_path = crear_video(prompt_type, input_text, musica_file)
877
 
878
  if video_path and os.path.exists(video_path):
879
  logger.info(f"crear_video retornó path: {video_path}")
880
  logger.info(f"Tamaño del archivo de video retornado: {os.path.getsize(video_path)} bytes")
881
- output_video = video_path
882
- output_file = video_path
883
  status_msg = gr.update(value="✅ Video generado exitosamente.", interactive=False)
884
  else:
885
  logger.error(f"crear_video no retornó un path válido o el archivo no existe: {video_path}")
@@ -893,6 +890,7 @@ def run_app(prompt_type, prompt_ia, prompt_manual, musica_file):
893
  status_msg = gr.update(value=f"❌ Error inesperado: {str(e)}", interactive=False)
894
  finally:
895
  logger.info("Fin del handler run_app.")
 
896
  return output_video, output_file, status_msg
897
 
898
 
@@ -914,11 +912,12 @@ with gr.Blocks(title="Generador de Videos con IA", theme=gr.themes.Soft(), css="
914
  )
915
 
916
  # Contenedores para los campos de texto para controlar la visibilidad
 
917
  with gr.Column(visible=True) as ia_guion_column:
918
  prompt_ia = gr.Textbox(
919
  label="Tema para IA",
920
  lines=2,
921
- placeholder="Ej: Un paisaje natural con montañas y ríos al amanecer...",
922
  max_lines=4,
923
  value=""
924
  )
@@ -927,7 +926,7 @@ with gr.Blocks(title="Generador de Videos con IA", theme=gr.themes.Soft(), css="
927
  prompt_manual = gr.Textbox(
928
  label="Tu Guion Completo",
929
  lines=5,
930
- placeholder="Ej: En este video exploraremos los misterios del océano...",
931
  max_lines=10,
932
  value=""
933
  )
@@ -960,31 +959,38 @@ with gr.Blocks(title="Generador de Videos con IA", theme=gr.themes.Soft(), css="
960
  value="Esperando entrada..."
961
  )
962
 
963
- # CAMBIO CRÍTICO: Controlar la visibilidad de las COLUMNAS contenedoras
 
964
  prompt_type.change(
965
  lambda x: (gr.update(visible=x == "Generar Guion con IA"),
966
  gr.update(visible=x == "Usar Mi Guion")),
967
  inputs=prompt_type,
968
- # Apuntar a los componentes Column padre
969
  outputs=[ia_guion_column, manual_guion_column]
970
  )
971
 
972
  # Evento click del botón de generar video
973
  generate_btn.click(
974
  # Acción 1 (síncrona): Resetear salidas y establecer estado a procesando
975
- lambda: (None, None, gr.update(value="⏳ Procesando... Esto puede tomar 2-5 minutos o más para videos largos.", interactive=False)),
 
976
  outputs=[video_output, file_output, status_output],
977
- queue=True,
978
  ).then(
979
  # Acción 2 (asíncrona): Llamar a la función principal de procesamiento
980
  run_app,
981
- # CAMBIO CRÍTICO: Pasar los 4 argumentos definidos por run_app
982
  inputs=[prompt_type, prompt_ia, prompt_manual, musica_input],
 
983
  outputs=[video_output, file_output, status_output]
984
  ).then(
985
  # Acción 3 (síncrona): Hacer visible el enlace de descarga si se retornó un archivo
986
- lambda video_path, file_path: gr.update(visible=file_path is not None),
987
- inputs=[video_output, file_output],
 
 
 
 
988
  outputs=[file_output]
989
  )
990
 
 
9
  import torch
10
  from transformers import GPT2Tokenizer, GPT2LMHeadModel
11
  from keybert import KeyBERT
12
+ # Importación correcta: Solo 'concatenate_videoclips'
13
  from moviepy.editor import VideoFileClip, concatenate_videoclips, AudioFileClip, CompositeAudioClip, concatenate_audioclips, AudioClip
14
  import re
15
  import math
 
35
  PEXELS_API_KEY = os.environ.get("PEXELS_API_KEY")
36
  if not PEXELS_API_KEY:
37
  logger.critical("NO SE ENCONTRÓ PEXELS_API_KEY EN VARIABLES DE ENTORNO")
38
+ # raise ValueError("API key de Pexels no configurada")
39
 
40
  # Inicialización de modelos
41
  MODEL_NAME = "datificate/gpt2-small-spanish"
 
387
  primary_voice = "es-ES-JuanNeural"
388
  fallback_voice = "es-ES-ElviraNeural" # Otra voz en español
389
  tts_success = False
390
+ retries = 3
391
 
392
  for attempt in range(retries):
393
  current_voice = primary_voice if attempt == 0 else fallback_voice
394
  if attempt > 0: logger.warning(f"Reintentando TTS ({attempt+1}/{retries})...")
395
  logger.info(f"Intentando TTS con voz: {current_voice}")
396
  try:
 
397
  tts_success = asyncio.run(text_to_speech(guion, voz_path, voice=current_voice))
398
  if tts_success:
399
  logger.info(f"TTS exitoso en intento {attempt + 1} con voz {current_voice}.")
400
+ break
401
  except Exception as e:
402
+ pass
 
403
 
404
  if not tts_success and attempt == 0 and primary_voice != fallback_voice:
405
  logger.warning(f"Fallo con voz {primary_voice}, intentando voz de respaldo: {fallback_voice}")
 
407
  logger.warning(f"Fallo con voz {current_voice}, reintentando...")
408
 
409
 
 
410
  if not tts_success or not os.path.exists(voz_path) or os.path.getsize(voz_path) <= 100:
411
  logger.error(f"Fallo en la generación de voz después de {retries} intentos. Archivo de audio no creado o es muy pequeño.")
412
  raise ValueError("Error generando voz a partir del guion (fallo de TTS).")
413
 
414
+ temp_intermediate_files.append(voz_path)
415
 
 
416
  audio_tts_original = AudioFileClip(voz_path)
417
 
418
  if audio_tts_original.reader is None or audio_tts_original.duration is None or audio_tts_original.duration <= 0:
 
705
 
706
  if musica_audio_looped:
707
  composite_audio = CompositeAudioClip([
708
+ musica_audio_looped.volumex(0.2), # Volumen 20% para música
709
+ audio_tts_original.volumex(1.0) # Volumen 100% para voz
710
  ])
711
 
712
  if composite_audio.duration is None or composite_audio.duration <= 0:
 
717
  else:
718
  logger.info("Mezcla de audio completada (voz + música).")
719
  final_audio = composite_audio
720
+ musica_audio = musica_audio_looped # Asignar para limpieza
721
 
722
  except Exception as e:
723
  logger.warning(f"Error procesando música de fondo: {str(e)}", exc_info=True)
 
843
  logger.info(f"Directorio temporal intermedio {temp_dir_intermediate} persistirá para que Gradio lea el video final.")
844
 
845
 
846
+ # La función run_app ahora recibe todos los inputs de texto y el archivo de música
847
  def run_app(prompt_type, prompt_ia, prompt_manual, musica_file):
848
  logger.info("="*80)
849
  logger.info("SOLICITUD RECIBIDA EN INTERFAZ")
850
 
851
+ # Elegir el texto de entrada basado en el prompt_type
852
  input_text = prompt_ia if prompt_type == "Generar Guion con IA" else prompt_manual
853
 
854
  output_video = None
 
857
 
858
  if not input_text or not input_text.strip():
859
  logger.warning("Texto de entrada vacío.")
860
+ # Retornar None para video y archivo, actualizar estado con mensaje de error
861
  return None, None, gr.update(value="⚠️ Por favor, ingresa texto para el guion o el tema.", interactive=False)
862
 
863
  logger.info(f"Tipo de entrada: {prompt_type}")
 
869
 
870
  try:
871
  logger.info("Llamando a crear_video...")
872
+ # Pasar el input_text elegido y el archivo de música a crear_video
873
  video_path = crear_video(prompt_type, input_text, musica_file)
874
 
875
  if video_path and os.path.exists(video_path):
876
  logger.info(f"crear_video retornó path: {video_path}")
877
  logger.info(f"Tamaño del archivo de video retornado: {os.path.getsize(video_path)} bytes")
878
+ output_video = video_path # Establecer valor del componente de video
879
+ output_file = video_path # Establecer valor del componente de archivo para descarga
880
  status_msg = gr.update(value="✅ Video generado exitosamente.", interactive=False)
881
  else:
882
  logger.error(f"crear_video no retornó un path válido o el archivo no existe: {video_path}")
 
890
  status_msg = gr.update(value=f"❌ Error inesperado: {str(e)}", interactive=False)
891
  finally:
892
  logger.info("Fin del handler run_app.")
893
+ # Retornar las tres salidas esperadas por el evento click
894
  return output_video, output_file, status_msg
895
 
896
 
 
912
  )
913
 
914
  # Contenedores para los campos de texto para controlar la visibilidad
915
+ # Nombrados para que coincidan con los outputs del evento change
916
  with gr.Column(visible=True) as ia_guion_column:
917
  prompt_ia = gr.Textbox(
918
  label="Tema para IA",
919
  lines=2,
920
+ placeholder="Ej: Un paisaje natural con montañas y ríos al amanecer, mostrando la belleza de la naturaleza...",
921
  max_lines=4,
922
  value=""
923
  )
 
926
  prompt_manual = gr.Textbox(
927
  label="Tu Guion Completo",
928
  lines=5,
929
+ placeholder="Ej: En este video exploraremos los misterios del océano. Veremos la vida marina fascinante y los arrecifes de coral vibrantes. ¡Acompáñanos en esta aventura subacuática!",
930
  max_lines=10,
931
  value=""
932
  )
 
959
  value="Esperando entrada..."
960
  )
961
 
962
+ # Evento para mostrar/ocultar los campos de texto según el tipo de prompt
963
+ # Apuntar a los componentes Column padre para controlar la visibilidad
964
  prompt_type.change(
965
  lambda x: (gr.update(visible=x == "Generar Guion con IA"),
966
  gr.update(visible=x == "Usar Mi Guion")),
967
  inputs=prompt_type,
968
+ # Pasar los componentes Column
969
  outputs=[ia_guion_column, manual_guion_column]
970
  )
971
 
972
  # Evento click del botón de generar video
973
  generate_btn.click(
974
  # Acción 1 (síncrona): Resetear salidas y establecer estado a procesando
975
+ # Retorna None para los 3 outputs iniciales
976
+ lambda: (None, None, gr.update(value="⏳ Procesando... Esto puede tomar varios minutos.", interactive=False)),
977
  outputs=[video_output, file_output, status_output],
978
+ queue=True, # Usar la cola de Gradio para tareas largas
979
  ).then(
980
  # Acción 2 (asíncrona): Llamar a la función principal de procesamiento
981
  run_app,
982
+ # PASAR TODOS LOS INPUTS DE LA INTERFAZ que run_app espera
983
  inputs=[prompt_type, prompt_ia, prompt_manual, musica_input],
984
+ # run_app retornará los 3 outputs esperados aquí
985
  outputs=[video_output, file_output, status_output]
986
  ).then(
987
  # Acción 3 (síncrona): Hacer visible el enlace de descarga si se retornó un archivo
988
+ # Esta función recibe las salidas de la Acción 2 (video_path, file_path, status_msg)
989
+ # Solo necesitamos video_path o file_path para decidir si mostrar el enlace
990
+ lambda video_path, file_path, status_msg: gr.update(visible=file_path is not None),
991
+ # Inputs son las salidas de la función .then() anterior
992
+ inputs=[video_output, file_output, status_output],
993
+ # Actualizamos la visibilidad del componente file_output
994
  outputs=[file_output]
995
  )
996