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

Update app.py

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