Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -10,23 +10,7 @@ import gradio as gr
|
|
| 10 |
import torch
|
| 11 |
from transformers import GPT2Tokenizer, GPT2LMHeadModel
|
| 12 |
from keybert import KeyBERT
|
| 13 |
-
import
|
| 14 |
-
import sys
|
| 15 |
-
try:
|
| 16 |
-
from moviepy.editor import VideoFileClip, concatenate_videoclips, AudioFileClip, CompositeAudioClip, concatenate_audioclips, AudioClip
|
| 17 |
-
logger = logging.getLogger(__name__)
|
| 18 |
-
logger.info("MoviePy importado correctamente")
|
| 19 |
-
except ImportError:
|
| 20 |
-
logger = logging.getLogger(__name__)
|
| 21 |
-
logger.info("Intentando instalar moviepy e imageio-ffmpeg...")
|
| 22 |
-
try:
|
| 23 |
-
subprocess.check_call([sys.executable, "-m", "pip", "install", "--no-cache-dir", "moviepy>=1.0.3", "imageio-ffmpeg>=0.5.1"])
|
| 24 |
-
from moviepy.editor import VideoFileClip, concatenate_videoclips, AudioFileClip, CompositeAudioClip, concatenate_audioclips, AudioClip
|
| 25 |
-
logger.info("MoviePy instalado tras reintento")
|
| 26 |
-
except Exception as e:
|
| 27 |
-
logger.critical(f"Fallo al instalar moviepy: {str(e)}")
|
| 28 |
-
logger.info("Continuando con placeholder para pruebas...")
|
| 29 |
-
moviepy = None # Placeholder para evitar errores
|
| 30 |
import re
|
| 31 |
import math
|
| 32 |
import shutil
|
|
@@ -43,6 +27,7 @@ logging.basicConfig(
|
|
| 43 |
logging.FileHandler('video_generator_full.log', encoding='utf-8')
|
| 44 |
]
|
| 45 |
)
|
|
|
|
| 46 |
logger.info("="*80)
|
| 47 |
logger.info("INICIO DE EJECUCI脫N - GENERADOR DE VIDEOS")
|
| 48 |
logger.info("="*80)
|
|
@@ -120,7 +105,7 @@ def get_voice_choices():
|
|
| 120 |
|
| 121 |
# Obtener las voces al inicio del script
|
| 122 |
AVAILABLE_VOICES = get_voice_choices()
|
| 123 |
-
DEFAULT_VOICE_ID = "es-MX-DaliaNeural" #
|
| 124 |
DEFAULT_VOICE_NAME = DEFAULT_VOICE_ID
|
| 125 |
for text, voice_id in AVAILABLE_VOICES:
|
| 126 |
if voice_id == DEFAULT_VOICE_ID:
|
|
@@ -306,9 +291,6 @@ def download_video_file(url, temp_dir):
|
|
| 306 |
return None
|
| 307 |
|
| 308 |
def loop_audio_to_length(audio_clip, target_duration):
|
| 309 |
-
if not moviepy:
|
| 310 |
-
logger.warning("MoviePy no disponible, retornando audio vac铆o")
|
| 311 |
-
return AudioClip(lambda t: 0, duration=target_duration, fps=44100) if 'AudioClip' in globals() else None
|
| 312 |
logger.debug(f"Ajustando audio | Duraci贸n actual: {audio_clip.duration:.2f}s | Objetivo: {target_duration:.2f}s")
|
| 313 |
if audio_clip is None or audio_clip.duration is None or audio_clip.duration <= 0:
|
| 314 |
logger.warning("Input audio clip is invalid")
|
|
@@ -412,19 +394,12 @@ async def crear_video_async(prompt_type, input_text, selected_voice, musica_file
|
|
| 412 |
logger.warning(f"TTS fall贸 para fragmento {i} con voz: {current_voice}")
|
| 413 |
break
|
| 414 |
if len(temp_audio_files) == len(text_chunks):
|
| 415 |
-
|
| 416 |
-
|
| 417 |
-
|
| 418 |
-
|
| 419 |
-
|
| 420 |
-
|
| 421 |
-
clip.close()
|
| 422 |
-
else:
|
| 423 |
-
logger.warning("MoviePy no disponible, uniendo audios con fallback...")
|
| 424 |
-
with open(voz_path, 'wb') as outfile:
|
| 425 |
-
for fname in temp_audio_files:
|
| 426 |
-
with open(fname, 'rb') as infile:
|
| 427 |
-
outfile.write(infile.read())
|
| 428 |
tts_success = os.path.exists(voz_path) and os.path.getsize(voz_path) > 100
|
| 429 |
temp_intermediate_files.extend(temp_audio_files)
|
| 430 |
if tts_success:
|
|
@@ -437,15 +412,11 @@ async def crear_video_async(prompt_type, input_text, selected_voice, musica_file
|
|
| 437 |
raise ValueError(f"Error generando voz. Intentos con {tts_voices_to_try} y gTTS fallaron.")
|
| 438 |
|
| 439 |
temp_intermediate_files.append(voz_path)
|
| 440 |
-
|
| 441 |
-
|
| 442 |
-
|
| 443 |
-
|
| 444 |
-
|
| 445 |
-
audio_duration = audio_tts_original.duration
|
| 446 |
-
else:
|
| 447 |
-
logger.warning("MoviePy no disponible, asumiendo duraci贸n m铆nima para audio")
|
| 448 |
-
audio_duration = 1.0 # Valor placeholder
|
| 449 |
logger.info(f"Duraci贸n audio voz: {audio_duration:.2f} segundos")
|
| 450 |
if audio_duration < 1.0:
|
| 451 |
raise ValueError("Audio de voz demasiado corto.")
|
|
@@ -492,15 +463,6 @@ async def crear_video_async(prompt_type, input_text, selected_voice, musica_file
|
|
| 492 |
raise ValueError("No se descargaron videos utilizables.")
|
| 493 |
|
| 494 |
# 5. Procesar y concatenar clips de video
|
| 495 |
-
if not moviepy:
|
| 496 |
-
logger.warning("MoviePy no disponible, retornando placeholder...")
|
| 497 |
-
output_filename = f"video_{int(datetime.now().timestamp())}.mp4"
|
| 498 |
-
persistent_path = os.path.join("/data", output_filename)
|
| 499 |
-
os.makedirs("/data", exist_ok=True)
|
| 500 |
-
open(persistent_path, 'a').close() # Crea archivo vac铆o como placeholder
|
| 501 |
-
download_url = f"https://gnosticdev-invideo-basic.hf.space/file={persistent_path}"
|
| 502 |
-
return persistent_path, download_url
|
| 503 |
-
|
| 504 |
current_duration = 0
|
| 505 |
min_clip_duration = 0.5
|
| 506 |
max_clip_segment = 10.0
|
|
@@ -766,6 +728,15 @@ with gr.Blocks(title="Generador de Videos con IA", theme=gr.themes.Soft()) as ap
|
|
| 766 |
""")
|
| 767 |
|
| 768 |
if __name__ == "__main__":
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 769 |
os.environ['GRADIO_SERVER_TIMEOUT'] = '3600'
|
| 770 |
logger.info("Iniciando aplicaci贸n Gradio...")
|
| 771 |
try:
|
|
|
|
| 10 |
import torch
|
| 11 |
from transformers import GPT2Tokenizer, GPT2LMHeadModel
|
| 12 |
from keybert import KeyBERT
|
| 13 |
+
from moviepy.editor import VideoFileClip, concatenate_videoclips, AudioFileClip, CompositeAudioClip, concatenate_audioclips, AudioClip
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
import re
|
| 15 |
import math
|
| 16 |
import shutil
|
|
|
|
| 27 |
logging.FileHandler('video_generator_full.log', encoding='utf-8')
|
| 28 |
]
|
| 29 |
)
|
| 30 |
+
logger = logging.getLogger(__name__)
|
| 31 |
logger.info("="*80)
|
| 32 |
logger.info("INICIO DE EJECUCI脫N - GENERADOR DE VIDEOS")
|
| 33 |
logger.info("="*80)
|
|
|
|
| 105 |
|
| 106 |
# Obtener las voces al inicio del script
|
| 107 |
AVAILABLE_VOICES = get_voice_choices()
|
| 108 |
+
DEFAULT_VOICE_ID = "es-MX-DaliaNeural" # Cambiado a una voz m谩s estable
|
| 109 |
DEFAULT_VOICE_NAME = DEFAULT_VOICE_ID
|
| 110 |
for text, voice_id in AVAILABLE_VOICES:
|
| 111 |
if voice_id == DEFAULT_VOICE_ID:
|
|
|
|
| 291 |
return None
|
| 292 |
|
| 293 |
def loop_audio_to_length(audio_clip, target_duration):
|
|
|
|
|
|
|
|
|
|
| 294 |
logger.debug(f"Ajustando audio | Duraci贸n actual: {audio_clip.duration:.2f}s | Objetivo: {target_duration:.2f}s")
|
| 295 |
if audio_clip is None or audio_clip.duration is None or audio_clip.duration <= 0:
|
| 296 |
logger.warning("Input audio clip is invalid")
|
|
|
|
| 394 |
logger.warning(f"TTS fall贸 para fragmento {i} con voz: {current_voice}")
|
| 395 |
break
|
| 396 |
if len(temp_audio_files) == len(text_chunks):
|
| 397 |
+
audio_clips = [AudioFileClip(f) for f in temp_audio_files]
|
| 398 |
+
concatenated_audio = concatenate_audioclips(audio_clips)
|
| 399 |
+
concatenated_audio.write_audiofile(voz_path, codec='mp3')
|
| 400 |
+
concatenated_audio.close()
|
| 401 |
+
for clip in audio_clips:
|
| 402 |
+
clip.close()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 403 |
tts_success = os.path.exists(voz_path) and os.path.getsize(voz_path) > 100
|
| 404 |
temp_intermediate_files.extend(temp_audio_files)
|
| 405 |
if tts_success:
|
|
|
|
| 412 |
raise ValueError(f"Error generando voz. Intentos con {tts_voices_to_try} y gTTS fallaron.")
|
| 413 |
|
| 414 |
temp_intermediate_files.append(voz_path)
|
| 415 |
+
audio_tts_original = AudioFileClip(voz_path)
|
| 416 |
+
if audio_tts_original.duration is None or audio_tts_original.duration <= 0:
|
| 417 |
+
raise ValueError("Audio de voz generado es inv谩lido.")
|
| 418 |
+
audio_tts = audio_tts_original
|
| 419 |
+
audio_duration = audio_tts_original.duration
|
|
|
|
|
|
|
|
|
|
|
|
|
| 420 |
logger.info(f"Duraci贸n audio voz: {audio_duration:.2f} segundos")
|
| 421 |
if audio_duration < 1.0:
|
| 422 |
raise ValueError("Audio de voz demasiado corto.")
|
|
|
|
| 463 |
raise ValueError("No se descargaron videos utilizables.")
|
| 464 |
|
| 465 |
# 5. Procesar y concatenar clips de video
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 466 |
current_duration = 0
|
| 467 |
min_clip_duration = 0.5
|
| 468 |
max_clip_segment = 10.0
|
|
|
|
| 728 |
""")
|
| 729 |
|
| 730 |
if __name__ == "__main__":
|
| 731 |
+
logger.info("Verificando dependencias...")
|
| 732 |
+
try:
|
| 733 |
+
from moviepy.editor import ColorClip
|
| 734 |
+
temp_clip = ColorClip((100,100), color=(255,0,0), duration=0.1)
|
| 735 |
+
temp_clip.close()
|
| 736 |
+
logger.info("MoviePy y FFmpeg accesibles.")
|
| 737 |
+
except Exception as e:
|
| 738 |
+
logger.critical(f"Fallo en dependencias: {e}")
|
| 739 |
+
raise
|
| 740 |
os.environ['GRADIO_SERVER_TIMEOUT'] = '3600'
|
| 741 |
logger.info("Iniciando aplicaci贸n Gradio...")
|
| 742 |
try:
|