Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -333,14 +333,14 @@ def _write_video_with_fallback(final_clip, out_path_base, fps=25):
|
|
| 333 |
raise RuntimeError(last_err or "FFmpeg a échoué")
|
| 334 |
|
| 335 |
# ============================================================
|
| 336 |
-
# BUILD CAPSULE — Pipeline complet
|
| 337 |
# ============================================================
|
| 338 |
def build_capsule(titre, sous_titre, texte_voix, texte_ecran, theme,
|
| 339 |
image_fond=None, logo_path=None, logo_pos="haut-gauche",
|
| 340 |
fond_mode="plein écran",
|
| 341 |
image_presentateur=None, voix_type="Féminine",
|
| 342 |
position_presentateur="bottom-right", plein=False,
|
| 343 |
-
moteur_voix="
|
| 344 |
|
| 345 |
# 1) TTS (Parler-TTS multivoix ou fallback)
|
| 346 |
try:
|
|
@@ -349,9 +349,17 @@ def build_capsule(titre, sous_titre, texte_voix, texte_ecran, theme,
|
|
| 349 |
print(f"[Capsule] Erreur TTS Parler ({e}), fallback gTTS.")
|
| 350 |
audio_mp = tts_gtts(texte_voix, lang=langue)
|
| 351 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 352 |
|
| 353 |
# 2) Fond (PIL)
|
| 354 |
-
fond_path = make_background(titre, sous_titre, texte_ecran, theme,
|
|
|
|
| 355 |
|
| 356 |
# 3) MoviePy (imports lents ici seulement)
|
| 357 |
from moviepy.editor import ImageClip, AudioFileClip, CompositeVideoClip, VideoFileClip
|
|
@@ -369,8 +377,7 @@ def build_capsule(titre, sous_titre, texte_voix, texte_ecran, theme,
|
|
| 369 |
if vpath and os.path.exists(vpath):
|
| 370 |
v = VideoFileClip(vpath).without_audio().fx(vfx.loop, duration=dur)
|
| 371 |
if plein:
|
| 372 |
-
v = v.resize((W, H))
|
| 373 |
-
v = v.set_position(("center", "center"))
|
| 374 |
else:
|
| 375 |
v = v.resize(width=520)
|
| 376 |
pos_map = {
|
|
@@ -389,28 +396,31 @@ def build_capsule(titre, sous_titre, texte_voix, texte_ecran, theme,
|
|
| 389 |
out_base = os.path.join(OUT_DIR, name)
|
| 390 |
out = _write_video_with_fallback(final, out_base, fps=target_fps)
|
| 391 |
|
| 392 |
-
# 6)
|
| 393 |
srt_path = write_srt(texte_voix, dur)
|
| 394 |
capsules.append({
|
| 395 |
"file": out,
|
| 396 |
"title": titre,
|
| 397 |
"langue": langue,
|
| 398 |
-
"voice": voix_type,
|
| 399 |
"theme": theme,
|
| 400 |
"duration": round(dur, 1)
|
| 401 |
})
|
| 402 |
_save_manifest()
|
| 403 |
|
| 404 |
-
# Nettoyage
|
| 405 |
try:
|
| 406 |
-
audio.close()
|
|
|
|
|
|
|
| 407 |
if os.path.exists(audio_mp): os.remove(audio_mp)
|
| 408 |
-
if os.path.exists(audio_wav): os.remove(audio_wav)
|
| 409 |
-
except Exception:
|
| 410 |
-
|
| 411 |
gc.collect()
|
| 412 |
|
| 413 |
-
return out, f"✅ Capsule {langue.upper()} créée ({dur:.1f}s, voix {voix_type})", srt_path
|
|
|
|
| 414 |
|
| 415 |
# ============================================================
|
| 416 |
# GESTION / ASSEMBLAGE
|
|
|
|
| 333 |
raise RuntimeError(last_err or "FFmpeg a échoué")
|
| 334 |
|
| 335 |
# ============================================================
|
| 336 |
+
# BUILD CAPSULE — Pipeline complet (corrigé)
|
| 337 |
# ============================================================
|
| 338 |
def build_capsule(titre, sous_titre, texte_voix, texte_ecran, theme,
|
| 339 |
image_fond=None, logo_path=None, logo_pos="haut-gauche",
|
| 340 |
fond_mode="plein écran",
|
| 341 |
image_presentateur=None, voix_type="Féminine",
|
| 342 |
position_presentateur="bottom-right", plein=False,
|
| 343 |
+
moteur_voix="Parler-TTS (offline)", langue="fr", speaker=None):
|
| 344 |
|
| 345 |
# 1) TTS (Parler-TTS multivoix ou fallback)
|
| 346 |
try:
|
|
|
|
| 349 |
print(f"[Capsule] Erreur TTS Parler ({e}), fallback gTTS.")
|
| 350 |
audio_mp = tts_gtts(texte_voix, lang=langue)
|
| 351 |
|
| 352 |
+
# 🔧 S'assurer qu'on a un WAV standard
|
| 353 |
+
audio_wav = audio_mp
|
| 354 |
+
if not audio_mp.lower().endswith(".wav"):
|
| 355 |
+
try:
|
| 356 |
+
audio_wav = _normalize_audio_to_wav(audio_mp)
|
| 357 |
+
except Exception as e:
|
| 358 |
+
print(f"[Audio] Normalisation échouée ({e}), on garde {audio_mp}")
|
| 359 |
|
| 360 |
# 2) Fond (PIL)
|
| 361 |
+
fond_path = make_background(titre, sous_titre, texte_ecran, theme,
|
| 362 |
+
logo_path, logo_pos, image_fond, fond_mode)
|
| 363 |
|
| 364 |
# 3) MoviePy (imports lents ici seulement)
|
| 365 |
from moviepy.editor import ImageClip, AudioFileClip, CompositeVideoClip, VideoFileClip
|
|
|
|
| 377 |
if vpath and os.path.exists(vpath):
|
| 378 |
v = VideoFileClip(vpath).without_audio().fx(vfx.loop, duration=dur)
|
| 379 |
if plein:
|
| 380 |
+
v = v.resize((W, H)).set_position(("center", "center"))
|
|
|
|
| 381 |
else:
|
| 382 |
v = v.resize(width=520)
|
| 383 |
pos_map = {
|
|
|
|
| 396 |
out_base = os.path.join(OUT_DIR, name)
|
| 397 |
out = _write_video_with_fallback(final, out_base, fps=target_fps)
|
| 398 |
|
| 399 |
+
# 6) Sous-titres + manifest
|
| 400 |
srt_path = write_srt(texte_voix, dur)
|
| 401 |
capsules.append({
|
| 402 |
"file": out,
|
| 403 |
"title": titre,
|
| 404 |
"langue": langue,
|
| 405 |
+
"voice": speaker or voix_type,
|
| 406 |
"theme": theme,
|
| 407 |
"duration": round(dur, 1)
|
| 408 |
})
|
| 409 |
_save_manifest()
|
| 410 |
|
| 411 |
+
# 7) Nettoyage
|
| 412 |
try:
|
| 413 |
+
audio.close()
|
| 414 |
+
final.close()
|
| 415 |
+
bg.close()
|
| 416 |
if os.path.exists(audio_mp): os.remove(audio_mp)
|
| 417 |
+
if audio_wav != audio_mp and os.path.exists(audio_wav): os.remove(audio_wav)
|
| 418 |
+
except Exception as e:
|
| 419 |
+
print(f"[Clean] Erreur nettoyage : {e}")
|
| 420 |
gc.collect()
|
| 421 |
|
| 422 |
+
return out, f"✅ Capsule {langue.upper()} créée ({dur:.1f}s, voix {speaker or voix_type})", srt_path
|
| 423 |
+
|
| 424 |
|
| 425 |
# ============================================================
|
| 426 |
# GESTION / ASSEMBLAGE
|