Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -12,26 +12,7 @@ from transformers import GPT2Tokenizer, GPT2LMHeadModel
|
|
| 12 |
from keybert import KeyBERT
|
| 13 |
import subprocess
|
| 14 |
import sys
|
| 15 |
-
|
| 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", "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 |
-
raise
|
| 29 |
-
import re
|
| 30 |
-
import math
|
| 31 |
-
import shutil
|
| 32 |
-
import json
|
| 33 |
-
from collections import Counter
|
| 34 |
-
import time
|
| 35 |
|
| 36 |
# Configuraci贸n de logging
|
| 37 |
logging.basicConfig(
|
|
@@ -47,6 +28,31 @@ logger.info("="*80)
|
|
| 47 |
logger.info("INICIO DE EJECUCI脫N - GENERADOR DE VIDEOS")
|
| 48 |
logger.info("="*80)
|
| 49 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
# Diccionario de voces TTS disponibles organizadas por idioma
|
| 51 |
VOCES_DISPONIBLES = {
|
| 52 |
"Espa帽ol (Espa帽a)": {
|
|
@@ -120,7 +126,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,6 +312,9 @@ def download_video_file(url, temp_dir):
|
|
| 306 |
return None
|
| 307 |
|
| 308 |
def loop_audio_to_length(audio_clip, target_duration):
|
|
|
|
|
|
|
|
|
|
| 309 |
logger.debug(f"Ajustando audio | Duraci贸n actual: {audio_clip.duration:.2f}s | Objetivo: {target_duration:.2f}s")
|
| 310 |
if audio_clip is None or audio_clip.duration is None or audio_clip.duration <= 0:
|
| 311 |
logger.warning("Input audio clip is invalid")
|
|
@@ -409,12 +418,19 @@ async def crear_video_async(prompt_type, input_text, selected_voice, musica_file
|
|
| 409 |
logger.warning(f"TTS fall贸 para fragmento {i} con voz: {current_voice}")
|
| 410 |
break
|
| 411 |
if len(temp_audio_files) == len(text_chunks):
|
| 412 |
-
|
| 413 |
-
|
| 414 |
-
|
| 415 |
-
|
| 416 |
-
|
| 417 |
-
clip
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 418 |
tts_success = os.path.exists(voz_path) and os.path.getsize(voz_path) > 100
|
| 419 |
temp_intermediate_files.extend(temp_audio_files)
|
| 420 |
if tts_success:
|
|
@@ -427,11 +443,15 @@ async def crear_video_async(prompt_type, input_text, selected_voice, musica_file
|
|
| 427 |
raise ValueError(f"Error generando voz. Intentos con {tts_voices_to_try} y gTTS fallaron.")
|
| 428 |
|
| 429 |
temp_intermediate_files.append(voz_path)
|
| 430 |
-
|
| 431 |
-
|
| 432 |
-
|
| 433 |
-
|
| 434 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 435 |
logger.info(f"Duraci贸n audio voz: {audio_duration:.2f} segundos")
|
| 436 |
if audio_duration < 1.0:
|
| 437 |
raise ValueError("Audio de voz demasiado corto.")
|
|
@@ -478,6 +498,15 @@ async def crear_video_async(prompt_type, input_text, selected_voice, musica_file
|
|
| 478 |
raise ValueError("No se descargaron videos utilizables.")
|
| 479 |
|
| 480 |
# 5. Procesar y concatenar clips de video
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 481 |
current_duration = 0
|
| 482 |
min_clip_duration = 0.5
|
| 483 |
max_clip_segment = 10.0
|
|
|
|
| 12 |
from keybert import KeyBERT
|
| 13 |
import subprocess
|
| 14 |
import sys
|
| 15 |
+
import importlib.util
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
|
| 17 |
# Configuraci贸n de logging
|
| 18 |
logging.basicConfig(
|
|
|
|
| 28 |
logger.info("INICIO DE EJECUCI脫N - GENERADOR DE VIDEOS")
|
| 29 |
logger.info("="*80)
|
| 30 |
|
| 31 |
+
# Forzar reinstalaci贸n de moviepy e imageio-ffmpeg
|
| 32 |
+
logger.info("Verificando moviepy e imageio-ffmpeg...")
|
| 33 |
+
try:
|
| 34 |
+
import moviepy
|
| 35 |
+
from moviepy.editor import VideoFileClip, concatenate_videoclips, AudioFileClip, CompositeAudioClip, concatenate_audioclips, AudioClip
|
| 36 |
+
logger.info(f"MoviePy importado correctamente: {moviepy.__version__}")
|
| 37 |
+
except ImportError:
|
| 38 |
+
logger.info("MoviePy no encontrado, intentando reinstalar...")
|
| 39 |
+
try:
|
| 40 |
+
subprocess.check_call([sys.executable, "-m", "pip", "install", "--no-cache-dir", "moviepy>=1.0.3", "imageio-ffmpeg>=0.5.1"])
|
| 41 |
+
import moviepy
|
| 42 |
+
from moviepy.editor import VideoFileClip, concatenate_videoclips, AudioFileClip, CompositeAudioClip, concatenate_audioclips, AudioClip
|
| 43 |
+
logger.info(f"MoviePy instalado tras reintento: {moviepy.__version__}")
|
| 44 |
+
except Exception as e:
|
| 45 |
+
logger.critical(f"Fallo al instalar moviepy: {str(e)}")
|
| 46 |
+
logger.info("Continuando con placeholder para pruebas...")
|
| 47 |
+
moviepy = None # Placeholder para evitar errores
|
| 48 |
+
|
| 49 |
+
import re
|
| 50 |
+
import math
|
| 51 |
+
import shutil
|
| 52 |
+
import json
|
| 53 |
+
from collections import Counter
|
| 54 |
+
import time
|
| 55 |
+
|
| 56 |
# Diccionario de voces TTS disponibles organizadas por idioma
|
| 57 |
VOCES_DISPONIBLES = {
|
| 58 |
"Espa帽ol (Espa帽a)": {
|
|
|
|
| 126 |
|
| 127 |
# Obtener las voces al inicio del script
|
| 128 |
AVAILABLE_VOICES = get_voice_choices()
|
| 129 |
+
DEFAULT_VOICE_ID = "es-MX-DaliaNeural" # Voz m谩s estable
|
| 130 |
DEFAULT_VOICE_NAME = DEFAULT_VOICE_ID
|
| 131 |
for text, voice_id in AVAILABLE_VOICES:
|
| 132 |
if voice_id == DEFAULT_VOICE_ID:
|
|
|
|
| 312 |
return None
|
| 313 |
|
| 314 |
def loop_audio_to_length(audio_clip, target_duration):
|
| 315 |
+
if not moviepy:
|
| 316 |
+
logger.warning("MoviePy no disponible, retornando audio vac铆o")
|
| 317 |
+
return AudioClip(lambda t: 0, duration=target_duration, fps=44100) if 'AudioClip' in globals() else None
|
| 318 |
logger.debug(f"Ajustando audio | Duraci贸n actual: {audio_clip.duration:.2f}s | Objetivo: {target_duration:.2f}s")
|
| 319 |
if audio_clip is None or audio_clip.duration is None or audio_clip.duration <= 0:
|
| 320 |
logger.warning("Input audio clip is invalid")
|
|
|
|
| 418 |
logger.warning(f"TTS fall贸 para fragmento {i} con voz: {current_voice}")
|
| 419 |
break
|
| 420 |
if len(temp_audio_files) == len(text_chunks):
|
| 421 |
+
if moviepy:
|
| 422 |
+
audio_clips = [AudioFileClip(f) for f in temp_audio_files]
|
| 423 |
+
concatenated_audio = concatenate_audioclips(audio_clips)
|
| 424 |
+
concatenated_audio.write_audiofile(voz_path, codec='mp3')
|
| 425 |
+
concatenated_audio.close()
|
| 426 |
+
for clip in audio_clips:
|
| 427 |
+
clip.close()
|
| 428 |
+
else:
|
| 429 |
+
logger.warning("MoviePy no disponible, uniendo audios con fallback...")
|
| 430 |
+
with open(voz_path, 'wb') as outfile:
|
| 431 |
+
for fname in temp_audio_files:
|
| 432 |
+
with open(fname, 'rb') as infile:
|
| 433 |
+
outfile.write(infile.read())
|
| 434 |
tts_success = os.path.exists(voz_path) and os.path.getsize(voz_path) > 100
|
| 435 |
temp_intermediate_files.extend(temp_audio_files)
|
| 436 |
if tts_success:
|
|
|
|
| 443 |
raise ValueError(f"Error generando voz. Intentos con {tts_voices_to_try} y gTTS fallaron.")
|
| 444 |
|
| 445 |
temp_intermediate_files.append(voz_path)
|
| 446 |
+
if moviepy:
|
| 447 |
+
audio_tts_original = AudioFileClip(voz_path)
|
| 448 |
+
if audio_tts_original.duration is None or audio_tts_original.duration <= 0:
|
| 449 |
+
raise ValueError("Audio de voz generado es inv谩lido.")
|
| 450 |
+
audio_tts = audio_tts_original
|
| 451 |
+
audio_duration = audio_tts_original.duration
|
| 452 |
+
else:
|
| 453 |
+
logger.warning("MoviePy no disponible, asumiendo duraci贸n m铆nima para audio")
|
| 454 |
+
audio_duration = 1.0 # Valor placeholder
|
| 455 |
logger.info(f"Duraci贸n audio voz: {audio_duration:.2f} segundos")
|
| 456 |
if audio_duration < 1.0:
|
| 457 |
raise ValueError("Audio de voz demasiado corto.")
|
|
|
|
| 498 |
raise ValueError("No se descargaron videos utilizables.")
|
| 499 |
|
| 500 |
# 5. Procesar y concatenar clips de video
|
| 501 |
+
if not moviepy:
|
| 502 |
+
logger.warning("MoviePy no disponible, retornando placeholder...")
|
| 503 |
+
output_filename = f"video_{int(datetime.now().timestamp())}.mp4"
|
| 504 |
+
persistent_path = os.path.join("/data", output_filename)
|
| 505 |
+
os.makedirs("/data", exist_ok=True)
|
| 506 |
+
open(persistent_path, 'a').close() # Crea archivo vac铆o como placeholder
|
| 507 |
+
download_url = f"https://gnosticdev-invideo-basic.hf.space/file={persistent_path}"
|
| 508 |
+
return persistent_path, download_url
|
| 509 |
+
|
| 510 |
current_duration = 0
|
| 511 |
min_clip_duration = 0.5
|
| 512 |
max_clip_segment = 10.0
|