Spaces:
Starting
Starting
Update app.py
Browse files
app.py
CHANGED
@@ -34,6 +34,7 @@ logger.info("="*80)
|
|
34 |
PEXELS_API_KEY = os.environ.get("PEXELS_API_KEY")
|
35 |
if not PEXELS_API_KEY:
|
36 |
logger.critical("PEXELS_API_KEY environment variable not found.")
|
|
|
37 |
# raise ValueError("Pexels API key not configured")
|
38 |
|
39 |
# Model Initialization
|
@@ -618,7 +619,7 @@ def crear_video(prompt_type, input_text, musica_file=None):
|
|
618 |
# 6. Handle background music
|
619 |
logger.info("Processing audio...")
|
620 |
|
621 |
-
final_audio = audio_tts_original
|
622 |
|
623 |
musica_audio_looped = None
|
624 |
|
@@ -649,7 +650,6 @@ def crear_video(prompt_type, input_text, musica_file=None):
|
|
649 |
|
650 |
|
651 |
if musica_audio_looped:
|
652 |
-
# Use the looped music and the current audio_tts (which is the original)
|
653 |
composite_audio = CompositeAudioClip([
|
654 |
musica_audio_looped.volumex(0.2),
|
655 |
audio_tts_original.volumex(1.0)
|
@@ -659,12 +659,11 @@ def crear_video(prompt_type, input_text, musica_file=None):
|
|
659 |
logger.warning("Composite audio clip is invalid (None or zero duration). Using voice audio only.")
|
660 |
try: composite_audio.close()
|
661 |
except: pass
|
662 |
-
|
663 |
-
final_audio = audio_tts_original # Fallback
|
664 |
else:
|
665 |
logger.info("Audio mix completed (voice + music).")
|
666 |
-
final_audio = composite_audio
|
667 |
-
musica_audio = musica_audio_looped
|
668 |
|
669 |
except Exception as e:
|
670 |
logger.warning(f"Error processing background music: {str(e)}", exc_info=True)
|
@@ -692,13 +691,13 @@ def crear_video(prompt_type, input_text, musica_file=None):
|
|
692 |
logger.warning(f"Error adjusting final audio duration: {str(e)}")
|
693 |
|
694 |
|
695 |
-
# Final check on video_final before writing
|
696 |
video_final = video_base.set_audio(final_audio)
|
697 |
|
698 |
if video_final is None or video_final.duration is None or video_final.duration <= 0:
|
699 |
logger.critical("Final video clip (with audio) is invalid before writing (None or zero duration).")
|
700 |
raise ValueError("Final video clip is invalid before writing.")
|
701 |
|
|
|
702 |
output_filename = "final_video.mp4"
|
703 |
output_path = os.path.join(temp_dir_intermediate, output_filename)
|
704 |
logger.info(f"Writing final video to: {output_path}")
|
@@ -728,33 +727,60 @@ def crear_video(prompt_type, input_text, musica_file=None):
|
|
728 |
logger.info("Starting cleanup of clips and intermediate temporary files...")
|
729 |
|
730 |
for clip in source_clips:
|
731 |
-
try:
|
732 |
-
|
|
|
|
|
733 |
|
734 |
for clip_segment in clips_to_concatenate:
|
735 |
-
|
736 |
-
|
|
|
|
|
737 |
|
738 |
-
|
739 |
-
|
740 |
-
|
741 |
-
|
742 |
-
|
743 |
-
|
744 |
-
|
745 |
-
|
746 |
-
|
747 |
-
|
748 |
-
|
749 |
-
|
750 |
-
except Exception as e: logger.warning(f"Error closing video_final in finally: {str(e)}")
|
751 |
-
elif video_base is not None:
|
752 |
-
try: video_base.close()
|
753 |
-
except Exception as e: logger.warning(f"Error closing video_base in finally: {str(e)}")
|
754 |
|
755 |
-
|
756 |
-
|
|
|
|
|
|
|
|
|
|
|
757 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
758 |
if temp_dir_intermediate and os.path.exists(temp_dir_intermediate):
|
759 |
final_output_in_temp = os.path.join(temp_dir_intermediate, "final_video.mp4")
|
760 |
|
@@ -775,14 +801,12 @@ def run_app(prompt_type, prompt_ia, prompt_manual, musica_file):
|
|
775 |
|
776 |
input_text = prompt_ia if prompt_type == "Generar Guion con IA" else prompt_manual
|
777 |
|
778 |
-
# Initialize outputs to None and default status
|
779 |
output_video = None
|
780 |
output_file = None
|
781 |
status_msg = gr.update(value="⏳ Procesando...", interactive=False)
|
782 |
|
783 |
if not input_text or not input_text.strip():
|
784 |
logger.warning("Empty input text.")
|
785 |
-
# Return None for video and file, update status
|
786 |
return None, None, gr.update(value="⚠️ Por favor, ingresa texto para el guion o el tema.", interactive=False)
|
787 |
|
788 |
logger.info(f"Input Type: {prompt_type}")
|
@@ -799,25 +823,21 @@ def run_app(prompt_type, prompt_ia, prompt_manual, musica_file):
|
|
799 |
if video_path and os.path.exists(video_path):
|
800 |
logger.info(f"crear_video returned path: {video_path}")
|
801 |
logger.info(f"Size of returned video file: {os.path.getsize(video_path)} bytes")
|
802 |
-
output_video = video_path
|
803 |
-
output_file = video_path
|
804 |
status_msg = gr.update(value="✅ Video generado exitosamente.", interactive=False)
|
805 |
else:
|
806 |
logger.error(f"crear_video did not return a valid path or file does not exist: {video_path}")
|
807 |
-
# Leave video and file outputs as None
|
808 |
status_msg = gr.update(value="❌ Error: La generación del video falló o el archivo no se creó correctamente.", interactive=False)
|
809 |
|
810 |
except ValueError as ve:
|
811 |
logger.warning(f"Validation error during video creation: {str(ve)}")
|
812 |
-
# Leave video and file outputs as None
|
813 |
status_msg = gr.update(value=f"⚠️ Error de validación: {str(ve)}", interactive=False)
|
814 |
except Exception as e:
|
815 |
logger.critical(f"Critical error during video creation: {str(e)}", exc_info=True)
|
816 |
-
# Leave video and file outputs as None
|
817 |
status_msg = gr.update(value=f"❌ Error inesperado: {str(e)}", interactive=False)
|
818 |
finally:
|
819 |
logger.info("End of run_app handler.")
|
820 |
-
# Return all three outputs
|
821 |
return output_video, output_file, status_msg
|
822 |
|
823 |
|
@@ -867,15 +887,14 @@ with gr.Blocks(title="Generador de Videos con IA", theme=gr.themes.Soft(), css="
|
|
867 |
|
868 |
with gr.Column():
|
869 |
video_output = gr.Video(
|
870 |
-
label="Generated Video Preview",
|
871 |
interactive=False,
|
872 |
height=400
|
873 |
)
|
874 |
-
# Add the File component for download
|
875 |
file_output = gr.File(
|
876 |
label="Download Video",
|
877 |
-
interactive=False,
|
878 |
-
visible=False
|
879 |
)
|
880 |
status_output = gr.Textbox(
|
881 |
label="Status",
|
@@ -892,22 +911,18 @@ with gr.Blocks(title="Generador de Videos con IA", theme=gr.themes.Soft(), css="
|
|
892 |
outputs=[ia_guion_column, manual_guion_column]
|
893 |
)
|
894 |
|
895 |
-
# Modify the click event to return 3 outputs
|
896 |
generate_btn.click(
|
897 |
-
# Action 1: Reset outputs and set status to processing
|
898 |
lambda: (None, None, gr.update(value="⏳ Procesando... Esto puede tomar 2-5 minutos.", interactive=False)),
|
899 |
outputs=[video_output, file_output, status_output],
|
900 |
queue=True,
|
901 |
).then(
|
902 |
-
# Action 2: Call the main processing function
|
903 |
run_app,
|
904 |
inputs=[prompt_type, prompt_ia, prompt_manual, musica_input],
|
905 |
-
outputs=[video_output, file_output, status_output]
|
906 |
).then(
|
907 |
-
|
908 |
-
|
909 |
-
|
910 |
-
outputs=[file_output] # Update visibility of file_output
|
911 |
)
|
912 |
|
913 |
|
|
|
34 |
PEXELS_API_KEY = os.environ.get("PEXELS_API_KEY")
|
35 |
if not PEXELS_API_KEY:
|
36 |
logger.critical("PEXELS_API_KEY environment variable not found.")
|
37 |
+
# Uncomment to force fail if not set:
|
38 |
# raise ValueError("Pexels API key not configured")
|
39 |
|
40 |
# Model Initialization
|
|
|
619 |
# 6. Handle background music
|
620 |
logger.info("Processing audio...")
|
621 |
|
622 |
+
final_audio = audio_tts_original
|
623 |
|
624 |
musica_audio_looped = None
|
625 |
|
|
|
650 |
|
651 |
|
652 |
if musica_audio_looped:
|
|
|
653 |
composite_audio = CompositeAudioClip([
|
654 |
musica_audio_looped.volumex(0.2),
|
655 |
audio_tts_original.volumex(1.0)
|
|
|
659 |
logger.warning("Composite audio clip is invalid (None or zero duration). Using voice audio only.")
|
660 |
try: composite_audio.close()
|
661 |
except: pass
|
662 |
+
final_audio = audio_tts_original
|
|
|
663 |
else:
|
664 |
logger.info("Audio mix completed (voice + music).")
|
665 |
+
final_audio = composite_audio
|
666 |
+
musica_audio = musica_audio_looped
|
667 |
|
668 |
except Exception as e:
|
669 |
logger.warning(f"Error processing background music: {str(e)}", exc_info=True)
|
|
|
691 |
logger.warning(f"Error adjusting final audio duration: {str(e)}")
|
692 |
|
693 |
|
|
|
694 |
video_final = video_base.set_audio(final_audio)
|
695 |
|
696 |
if video_final is None or video_final.duration is None or video_final.duration <= 0:
|
697 |
logger.critical("Final video clip (with audio) is invalid before writing (None or zero duration).")
|
698 |
raise ValueError("Final video clip is invalid before writing.")
|
699 |
|
700 |
+
|
701 |
output_filename = "final_video.mp4"
|
702 |
output_path = os.path.join(temp_dir_intermediate, output_filename)
|
703 |
logger.info(f"Writing final video to: {output_path}")
|
|
|
727 |
logger.info("Starting cleanup of clips and intermediate temporary files...")
|
728 |
|
729 |
for clip in source_clips:
|
730 |
+
try:
|
731 |
+
clip.close()
|
732 |
+
except Exception as e:
|
733 |
+
logger.warning(f"Error closing source video clip in finally: {str(e)}")
|
734 |
|
735 |
for clip_segment in clips_to_concatenate:
|
736 |
+
try:
|
737 |
+
clip_segment.close()
|
738 |
+
except Exception as e:
|
739 |
+
logger.warning(f"Error closing video segment clip in finally: {str(e)}")
|
740 |
|
741 |
+
# Close audio clips: looped music, original music, then final audio (which might close its components)
|
742 |
+
if musica_audio is not None:
|
743 |
+
try:
|
744 |
+
musica_audio.close()
|
745 |
+
except Exception as e:
|
746 |
+
logger.warning(f"Error closing musica_audio (processed) in finally: {str(e)}")
|
747 |
+
|
748 |
+
if musica_audio_original is not None and musica_audio_original is not musica_audio:
|
749 |
+
try:
|
750 |
+
musica_audio_original.close()
|
751 |
+
except Exception as e:
|
752 |
+
logger.warning(f"Error closing musica_audio_original in finally: {str(e)}")
|
|
|
|
|
|
|
|
|
753 |
|
754 |
+
# Close TTS clips: potentially modified/trimmed TTS, then original TTS
|
755 |
+
# Note: audio_tts variable is only assigned audio_tts_original in this code, but keep structure for safety
|
756 |
+
if audio_tts is not None and audio_tts is not audio_tts_original:
|
757 |
+
try:
|
758 |
+
audio_tts.close()
|
759 |
+
except Exception as e:
|
760 |
+
logger.warning(f"Error closing audio_tts (processed) in finally: {str(e)}")
|
761 |
|
762 |
+
if audio_tts_original is not None:
|
763 |
+
try:
|
764 |
+
audio_tts_original.close()
|
765 |
+
except Exception as e:
|
766 |
+
logger.warning(f"Error closing audio_tts_original in finally: {str(e)}")
|
767 |
+
|
768 |
+
|
769 |
+
# Close video clips: final video (should cascade), then video base if it wasn't the final
|
770 |
+
if video_final is not None:
|
771 |
+
try:
|
772 |
+
video_final.close()
|
773 |
+
except Exception as e:
|
774 |
+
logger.warning(f"Error closing video_final in finally: {str(e)}")
|
775 |
+
# Note: video_base variable is reassigned to final_video_base. Close only if it exists and is different from video_final
|
776 |
+
elif video_base is not None and video_base is not video_final: # Check if video_base holds a different clip
|
777 |
+
try:
|
778 |
+
video_base.close()
|
779 |
+
except Exception as e:
|
780 |
+
logger.warning(f"Error closing video_base in finally: {str(e)}")
|
781 |
+
|
782 |
+
|
783 |
+
# Clean up intermediate files, but NOT the final video file
|
784 |
if temp_dir_intermediate and os.path.exists(temp_dir_intermediate):
|
785 |
final_output_in_temp = os.path.join(temp_dir_intermediate, "final_video.mp4")
|
786 |
|
|
|
801 |
|
802 |
input_text = prompt_ia if prompt_type == "Generar Guion con IA" else prompt_manual
|
803 |
|
|
|
804 |
output_video = None
|
805 |
output_file = None
|
806 |
status_msg = gr.update(value="⏳ Procesando...", interactive=False)
|
807 |
|
808 |
if not input_text or not input_text.strip():
|
809 |
logger.warning("Empty input text.")
|
|
|
810 |
return None, None, gr.update(value="⚠️ Por favor, ingresa texto para el guion o el tema.", interactive=False)
|
811 |
|
812 |
logger.info(f"Input Type: {prompt_type}")
|
|
|
823 |
if video_path and os.path.exists(video_path):
|
824 |
logger.info(f"crear_video returned path: {video_path}")
|
825 |
logger.info(f"Size of returned video file: {os.path.getsize(video_path)} bytes")
|
826 |
+
output_video = video_path
|
827 |
+
output_file = video_path
|
828 |
status_msg = gr.update(value="✅ Video generado exitosamente.", interactive=False)
|
829 |
else:
|
830 |
logger.error(f"crear_video did not return a valid path or file does not exist: {video_path}")
|
|
|
831 |
status_msg = gr.update(value="❌ Error: La generación del video falló o el archivo no se creó correctamente.", interactive=False)
|
832 |
|
833 |
except ValueError as ve:
|
834 |
logger.warning(f"Validation error during video creation: {str(ve)}")
|
|
|
835 |
status_msg = gr.update(value=f"⚠️ Error de validación: {str(ve)}", interactive=False)
|
836 |
except Exception as e:
|
837 |
logger.critical(f"Critical error during video creation: {str(e)}", exc_info=True)
|
|
|
838 |
status_msg = gr.update(value=f"❌ Error inesperado: {str(e)}", interactive=False)
|
839 |
finally:
|
840 |
logger.info("End of run_app handler.")
|
|
|
841 |
return output_video, output_file, status_msg
|
842 |
|
843 |
|
|
|
887 |
|
888 |
with gr.Column():
|
889 |
video_output = gr.Video(
|
890 |
+
label="Generated Video Preview",
|
891 |
interactive=False,
|
892 |
height=400
|
893 |
)
|
|
|
894 |
file_output = gr.File(
|
895 |
label="Download Video",
|
896 |
+
interactive=False,
|
897 |
+
visible=False
|
898 |
)
|
899 |
status_output = gr.Textbox(
|
900 |
label="Status",
|
|
|
911 |
outputs=[ia_guion_column, manual_guion_column]
|
912 |
)
|
913 |
|
|
|
914 |
generate_btn.click(
|
|
|
915 |
lambda: (None, None, gr.update(value="⏳ Procesando... Esto puede tomar 2-5 minutos.", interactive=False)),
|
916 |
outputs=[video_output, file_output, status_output],
|
917 |
queue=True,
|
918 |
).then(
|
|
|
919 |
run_app,
|
920 |
inputs=[prompt_type, prompt_ia, prompt_manual, musica_input],
|
921 |
+
outputs=[video_output, file_output, status_output]
|
922 |
).then(
|
923 |
+
lambda video_path, file_path: gr.update(visible=video_path is not None or file_path is not None), # Show download if either video or file path is returned
|
924 |
+
inputs=[video_output, file_output],
|
925 |
+
outputs=[file_output]
|
|
|
926 |
)
|
927 |
|
928 |
|