gnosticdev commited on
Commit
eaddca7
·
verified ·
1 Parent(s): 2e366e8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +200 -6
app.py CHANGED
@@ -859,7 +859,7 @@ def crear_video(prompt_type, input_text, selected_voice, musica_file=None):
859
  except Exception as e:
860
  logger.warning(f"Error ajustando duración del audio final: {str(e)}")
861
 
862
- # 7. Crear video final (INDENTACIÓN ORIGINAL)
863
  output_filename = f"video_{int(time.time())}.mp4" # Nombre único con timestamp
864
  output_path = os.path.join(temp_dir_intermediate, output_filename)
865
 
@@ -881,7 +881,7 @@ def crear_video(prompt_type, input_text, selected_voice, musica_file=None):
881
  # Mover a ubicación permanente en /tmp
882
  permanent_path = f"/tmp/{output_filename}"
883
  try:
884
- shutil.copy(output_path, permanent_path)
885
  logger.info(f"Video guardado permanentemente en: {permanent_path}")
886
  except Exception as move_error:
887
  logger.error(f"Error moviendo archivo: {str(move_error)}. Usando path original.")
@@ -957,16 +957,210 @@ def crear_video(prompt_type, input_text, selected_voice, musica_file=None):
957
  logger.warning(f"Error cerrando video_base en finally: {str(e)}")
958
 
959
  if temp_dir_intermediate and os.path.exists(temp_dir_intermediate):
960
- final_output_in_temp = os.path.join(temp_dir_intermediate, "final_video.mp4")
961
 
962
  for path in temp_intermediate_files:
963
  try:
964
- if os.path.isfile(path) and path != final_output_in_temp:
965
  logger.debug(f"Eliminando archivo temporal intermedio: {path}")
966
  os.remove(path)
967
- elif os.path.isfile(path) and path == final_output_in_temp:
968
  logger.debug(f"Saltando eliminación del archivo de video final: {path}")
969
  except Exception as e:
970
  logger.warning(f"No se pudo eliminar archivo temporal intermedio {path}: {str(e)}")
971
 
972
- logger.info(f"Directorio temporal intermedio {temp_dir_intermediate} persistirá para que Gradio lea el video final.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
859
  except Exception as e:
860
  logger.warning(f"Error ajustando duración del audio final: {str(e)}")
861
 
862
+ # 7. Crear video final (INDENTACIÓN ORIGINAL)
863
  output_filename = f"video_{int(time.time())}.mp4" # Nombre único con timestamp
864
  output_path = os.path.join(temp_dir_intermediate, output_filename)
865
 
 
881
  # Mover a ubicación permanente en /tmp
882
  permanent_path = f"/tmp/{output_filename}"
883
  try:
884
+ shutil.copy(output_path, permanent_path) # Usamos copy() en lugar de move()
885
  logger.info(f"Video guardado permanentemente en: {permanent_path}")
886
  except Exception as move_error:
887
  logger.error(f"Error moviendo archivo: {str(move_error)}. Usando path original.")
 
957
  logger.warning(f"Error cerrando video_base en finally: {str(e)}")
958
 
959
  if temp_dir_intermediate and os.path.exists(temp_dir_intermediate):
960
+ final_output_in_temp = os.path.join(temp_dir_intermediate, output_filename)
961
 
962
  for path in temp_intermediate_files:
963
  try:
964
+ if os.path.isfile(path) and path != final_output_in_temp and path != permanent_path:
965
  logger.debug(f"Eliminando archivo temporal intermedio: {path}")
966
  os.remove(path)
967
+ elif os.path.isfile(path) and (path == final_output_in_temp or path == permanent_path):
968
  logger.debug(f"Saltando eliminación del archivo de video final: {path}")
969
  except Exception as e:
970
  logger.warning(f"No se pudo eliminar archivo temporal intermedio {path}: {str(e)}")
971
 
972
+ logger.info(f"Directorio temporal intermedio {temp_dir_intermediate} persistirá para que Gradio lea el video final.")
973
+
974
+ # run_app ahora recibe todos los inputs, incluyendo la voz seleccionada
975
+ def run_app(prompt_type, prompt_ia, prompt_manual, musica_file, selected_voice): # <-- Recibe el valor del Dropdown
976
+ logger.info("="*80)
977
+ logger.info("SOLICITUD RECIBIDA EN INTERFAZ")
978
+
979
+ # Elegir el texto de entrada basado en el prompt_type
980
+ input_text = prompt_ia if prompt_type == "Generar Guion con IA" else prompt_manual
981
+
982
+ output_video = None
983
+ output_file = None
984
+ status_msg = gr.update(value="⏳ Procesando...", interactive=False)
985
+
986
+ if not input_text or not input_text.strip():
987
+ logger.warning("Texto de entrada vacío.")
988
+ # Retornar None para video y archivo, actualizar estado con mensaje de error
989
+ return None, None, gr.update(value="⚠️ Por favor, ingresa texto para el guion o el tema.", interactive=False)
990
+
991
+ # Validar la voz seleccionada. Si no es válida, usar la por defecto.
992
+ # AVAILABLE_VOICES se obtiene al inicio. Hay que buscar si el voice_id existe en la lista de pares (nombre, id)
993
+ voice_ids_disponibles = [v[1] for v in AVAILABLE_VOICES]
994
+ if selected_voice not in voice_ids_disponibles:
995
+ logger.warning(f"Voz seleccionada inválida o no encontrada en la lista: '{selected_voice}'. Usando voz por defecto: {DEFAULT_VOICE_ID}.")
996
+ selected_voice = DEFAULT_VOICE_ID # <-- Usar el ID de la voz por defecto
997
+ else:
998
+ logger.info(f"Voz seleccionada validada: {selected_voice}")
999
+
1000
+
1001
+ logger.info(f"Tipo de entrada: {prompt_type}")
1002
+ logger.debug(f"Texto de entrada: '{input_text[:100]}...'")
1003
+ if musica_file:
1004
+ logger.info(f"Archivo de música recibido: {musica_file}")
1005
+ else:
1006
+ logger.info("No se proporcionó archivo de música.")
1007
+ logger.info(f"Voz final a usar (ID): {selected_voice}") # Loguear el ID de la voz final
1008
+
1009
+ try:
1010
+ logger.info("Llamando a crear_video...")
1011
+ # Pasar el input_text elegido, la voz seleccionada (el ID) y el archivo de música a crear_video
1012
+ video_path = crear_video(prompt_type, input_text, selected_voice, musica_file) # <-- PASAR selected_voice (ID) a crear_video
1013
+
1014
+ if video_path and os.path.exists(video_path):
1015
+ logger.info(f"crear_video retornó path: {video_path}")
1016
+ logger.info(f"Tamaño del archivo de video retornado: {os.path.getsize(video_path)} bytes")
1017
+ output_video = video_path # Establecer valor del componente de video
1018
+ output_file = video_path # Establecer valor del componente de archivo para descarga
1019
+ status_msg = gr.update(value="✅ Video generado exitosamente.", interactive=False)
1020
+ else:
1021
+ logger.error(f"crear_video no retornó un path válido o el archivo no existe: {video_path}")
1022
+ status_msg = gr.update(value="❌ Error: La generación del video falló o el archivo no se creó correctamente.", interactive=False)
1023
+
1024
+ except ValueError as ve:
1025
+ logger.warning(f"Error de validación durante la creación del video: {str(ve)}")
1026
+ status_msg = gr.update(value=f"⚠️ Error de validación: {str(ve)}", interactive=False)
1027
+ except Exception as e:
1028
+ logger.critical(f"Error crítico durante la creación del video: {str(e)}", exc_info=True)
1029
+ status_msg = gr.update(value=f"❌ Error inesperado: {str(e)}", interactive=False)
1030
+ finally:
1031
+ logger.info("Fin del handler run_app.")
1032
+ return output_video, output_file, status_msg
1033
+
1034
+
1035
+ # Interfaz de Gradio
1036
+ with gr.Blocks(title="Generador de Videos con IA", theme=gr.themes.Soft(), css="""
1037
+ .gradio-container {max-width: 800px; margin: auto;}
1038
+ h1 {text-align: center;}
1039
+ """) as app:
1040
+
1041
+ gr.Markdown("# 🎬 Generador Automático de Videos con IA")
1042
+ gr.Markdown("Genera videos cortos a partir de un tema o guion, usando imágenes de archivo de Pexels y voz generada.")
1043
+
1044
+ with gr.Row():
1045
+ with gr.Column():
1046
+ prompt_type = gr.Radio(
1047
+ ["Generar Guion con IA", "Usar Mi Guion"],
1048
+ label="Método de Entrada",
1049
+ value="Generar Guion con IA"
1050
+ )
1051
+
1052
+ # Contenedores para los campos de texto para controlar la visibilidad
1053
+ with gr.Column(visible=True) as ia_guion_column:
1054
+ prompt_ia = gr.Textbox(
1055
+ label="Tema para IA",
1056
+ lines=2,
1057
+ placeholder="Ej: Un paisaje natural con montañas y ríos al amanecer, mostrando la belleza de la naturaleza...",
1058
+ max_lines=4,
1059
+ value=""
1060
+ )
1061
+
1062
+ with gr.Column(visible=False) as manual_guion_column:
1063
+ prompt_manual = gr.Textbox(
1064
+ label="Tu Guion Completo",
1065
+ lines=5,
1066
+ 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!",
1067
+ max_lines=10,
1068
+ value=""
1069
+ )
1070
+
1071
+ musica_input = gr.Audio(
1072
+ label="Música de fondo (opcional)",
1073
+ type="filepath",
1074
+ interactive=True,
1075
+ value=None
1076
+ )
1077
+
1078
+ # --- COMPONENTE: Selección de Voz ---
1079
+ voice_dropdown = gr.Dropdown(
1080
+ label="Seleccionar Voz para Guion",
1081
+ choices=AVAILABLE_VOICES,
1082
+ value=DEFAULT_VOICE_ID,
1083
+ interactive=True
1084
+ )
1085
+ # --- FIN COMPONENTE ---
1086
+
1087
+ generate_btn = gr.Button("✨ Generar Video", variant="primary")
1088
+
1089
+ with gr.Column():
1090
+ video_output = gr.Video(
1091
+ label="Previsualización del Video Generado",
1092
+ interactive=False,
1093
+ height=400
1094
+ )
1095
+ file_output = gr.File(
1096
+ label="Descargar Archivo de Video",
1097
+ interactive=False,
1098
+ visible=False
1099
+ )
1100
+ status_output = gr.Textbox(
1101
+ label="Estado",
1102
+ interactive=False,
1103
+ show_label=False,
1104
+ placeholder="Esperando acción...",
1105
+ value="Esperando entrada..."
1106
+ )
1107
+
1108
+ # Evento para mostrar/ocultar los campos de texto según el tipo de prompt
1109
+ prompt_type.change(
1110
+ lambda x: (gr.update(visible=x == "Generar Guion con IA"),
1111
+ gr.update(visible=x == "Usar Mi Guion")),
1112
+ inputs=prompt_type,
1113
+ outputs=[ia_guion_column, manual_guion_column]
1114
+ )
1115
+
1116
+ # Evento click del botón de generar video
1117
+ generate_btn.click(
1118
+ lambda: (None, None, gr.update(value="⏳ Procesando... Esto puede tomar varios minutos.", interactive=False)),
1119
+ outputs=[video_output, file_output, status_output],
1120
+ queue=True,
1121
+ ).then(
1122
+ run_app,
1123
+ inputs=[prompt_type, prompt_ia, prompt_manual, musica_input, voice_dropdown],
1124
+ outputs=[video_output, file_output, status_output]
1125
+ ).then(
1126
+ lambda video_path, file_path, status_msg: gr.update(visible=file_path is not None),
1127
+ inputs=[video_output, file_output, status_output],
1128
+ outputs=[file_output]
1129
+ )
1130
+
1131
+ gr.Markdown("### Instrucciones:")
1132
+ gr.Markdown("""
1133
+ 1. **Clave API de Pexels:** Asegúrate de haber configurado la variable de entorno `PEXELS_API_KEY` con tu clave.
1134
+ 2. **Selecciona el tipo de entrada**: "Generar Guion con IA" o "Usar Mi Guion".
1135
+ 3. **Sube música** (opcional): Selecciona un archivo de audio (MP3, WAV, etc.).
1136
+ 4. **Selecciona la voz** deseada del desplegable.
1137
+ 5. **Haz clic en "✨ Generar Video"**.
1138
+ 6. Espera a que se procese el video. Verás el estado.
1139
+ 7. La previsualización aparecerá si es posible, y siempre un enlace **Descargar Archivo de Video** se mostrará si la generación fue exitosa.
1140
+ 8. Revisa `video_generator_full.log` para detalles si hay errores.
1141
+ """)
1142
+ gr.Markdown("---")
1143
+ gr.Markdown("Desarrollado por [Tu Nombre/Empresa/Alias - Opcional]")
1144
+
1145
+ if __name__ == "__main__":
1146
+ logger.info("Verificando dependencias críticas...")
1147
+ try:
1148
+ from moviepy.editor import ColorClip
1149
+ try:
1150
+ temp_clip = ColorClip((100,100), color=(255,0,0), duration=0.1)
1151
+ temp_clip.close()
1152
+ logger.info("Clips base de MoviePy creados y cerrados exitosamente. FFmpeg parece accesible.")
1153
+ except Exception as e:
1154
+ logger.critical(f"Fallo al crear clip base de MoviePy. A menudo indica problemas con FFmpeg/ImageMagick. Error: {e}", exc_info=True)
1155
+ except Exception as e:
1156
+ logger.critical(f"Fallo al importar MoviePy. Asegúrate de que está instalado. Error: {e}", exc_info=True)
1157
+
1158
+ # Solución para el timeout de Gradio
1159
+ os.environ['GRADIO_SERVER_TIMEOUT'] = '6000' # 600 segundos = 10 minutos
1160
+
1161
+ logger.info("Iniciando aplicación Gradio...")
1162
+ try:
1163
+ app.launch(server_name="0.0.0.0", server_port=7860, share=False)
1164
+ except Exception as e:
1165
+ logger.critical(f"No se pudo iniciar la app: {str(e)}", exc_info=True)
1166
+ raise