Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -238,6 +238,7 @@ def make_background(titre, sous_titre, texte_ecran, theme, logo_path, logo_pos,
|
|
| 238 |
_draw_text_shadow(draw, (MARGIN_X, y), l, f_text)
|
| 239 |
y += 55
|
| 240 |
|
|
|
|
| 241 |
if logo_path and os.path.exists(logo_path):
|
| 242 |
logo = Image.open(logo_path).convert("RGBA")
|
| 243 |
logo.thumbnail((260, 260))
|
|
@@ -249,10 +250,10 @@ def make_background(titre, sous_titre, texte_ecran, theme, logo_path, logo_pos,
|
|
| 249 |
else:
|
| 250 |
pos = ((W - lw)//2, 50)
|
| 251 |
bg.paste(logo, pos, logo)
|
| 252 |
-
|
| 253 |
-
|
| 254 |
-
|
| 255 |
-
|
| 256 |
|
| 257 |
# ============================================================
|
| 258 |
# SUPPRESSION DE LA PARTIE SADTALKER (plus nécessaire)
|
|
@@ -264,20 +265,26 @@ def _prepare_video_presentateur(video_path, audio_duration, position, plein_ecra
|
|
| 264 |
import moviepy.video.fx.all as vfx
|
| 265 |
|
| 266 |
try:
|
|
|
|
| 267 |
v = VideoFileClip(video_path).without_audio()
|
|
|
|
| 268 |
|
| 269 |
# Ajuster la durée à celle de l'audio
|
| 270 |
if v.duration < audio_duration:
|
| 271 |
# Si la vidéo est plus courte, la boucler
|
|
|
|
| 272 |
v = v.fx(vfx.loop, duration=audio_duration)
|
| 273 |
elif v.duration > audio_duration:
|
| 274 |
# Si la vidéo est plus longue, la couper
|
|
|
|
| 275 |
v = v.subclip(0, audio_duration)
|
| 276 |
|
| 277 |
# Ajuster la taille et la position
|
| 278 |
if plein_ecran:
|
|
|
|
| 279 |
v = v.resize((W, H)).set_position(("center", "center"))
|
| 280 |
else:
|
|
|
|
| 281 |
v = v.resize(width=520) # Taille réduite pour le coin
|
| 282 |
pos_map = {
|
| 283 |
"bottom-right": ("right", "bottom"),
|
|
@@ -288,9 +295,11 @@ def _prepare_video_presentateur(video_path, audio_duration, position, plein_ecra
|
|
| 288 |
}
|
| 289 |
v = v.set_position(pos_map.get(position, ("right", "bottom")))
|
| 290 |
|
|
|
|
| 291 |
return v
|
| 292 |
except Exception as e:
|
| 293 |
print(f"[Préparation vidéo] Erreur : {e}")
|
|
|
|
| 294 |
return None
|
| 295 |
|
| 296 |
# ============================================================
|
|
@@ -387,6 +396,7 @@ def build_capsule(titre, sous_titre, texte_voix, texte_ecran, theme,
|
|
| 387 |
# 4) Vidéo présentateur (au lieu de SadTalker)
|
| 388 |
clips = [bg]
|
| 389 |
if video_presentateur and os.path.exists(video_presentateur):
|
|
|
|
| 390 |
v_presentateur = _prepare_video_presentateur(
|
| 391 |
video_presentateur,
|
| 392 |
dur,
|
|
@@ -394,7 +404,12 @@ def build_capsule(titre, sous_titre, texte_voix, texte_ecran, theme,
|
|
| 394 |
plein
|
| 395 |
)
|
| 396 |
if v_presentateur:
|
|
|
|
| 397 |
clips.append(v_presentateur)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 398 |
|
| 399 |
# 5) Composition + export
|
| 400 |
final = CompositeVideoClip(clips).set_audio(audio.set_fps(44100))
|
|
@@ -419,16 +434,16 @@ def build_capsule(titre, sous_titre, texte_voix, texte_ecran, theme,
|
|
| 419 |
audio.close()
|
| 420 |
final.close()
|
| 421 |
bg.close()
|
| 422 |
-
if 'v_presentateur' in locals():
|
| 423 |
v_presentateur.close()
|
| 424 |
-
if os.path.exists(audio_mp):
|
| 425 |
-
|
|
|
|
|
|
|
| 426 |
except Exception as e:
|
| 427 |
print(f"[Clean] Erreur nettoyage : {e}")
|
| 428 |
gc.collect()
|
| 429 |
|
| 430 |
-
return out, f"✅ Capsule {langue.upper()} créée ({dur:.1f}s, voix {speaker or voix_type})", srt_path
|
| 431 |
-
|
| 432 |
# ============================================================
|
| 433 |
# GESTION / ASSEMBLAGE
|
| 434 |
# ============================================================
|
|
|
|
| 238 |
_draw_text_shadow(draw, (MARGIN_X, y), l, f_text)
|
| 239 |
y += 55
|
| 240 |
|
| 241 |
+
# Cherchez cette partie dans make_background() et remplacez-la :
|
| 242 |
if logo_path and os.path.exists(logo_path):
|
| 243 |
logo = Image.open(logo_path).convert("RGBA")
|
| 244 |
logo.thumbnail((260, 260))
|
|
|
|
| 250 |
else:
|
| 251 |
pos = ((W - lw)//2, 50)
|
| 252 |
bg.paste(logo, pos, logo)
|
| 253 |
+
|
| 254 |
+
out = os.path.join(TMP_DIR, f"fond_{uuid.uuid4().hex[:6]}.png")
|
| 255 |
+
bg.save(out)
|
| 256 |
+
return out
|
| 257 |
|
| 258 |
# ============================================================
|
| 259 |
# SUPPRESSION DE LA PARTIE SADTALKER (plus nécessaire)
|
|
|
|
| 265 |
import moviepy.video.fx.all as vfx
|
| 266 |
|
| 267 |
try:
|
| 268 |
+
print(f"[Préparation vidéo] Chargement: {video_path}")
|
| 269 |
v = VideoFileClip(video_path).without_audio()
|
| 270 |
+
print(f"[Préparation vidéo] Durée vidéo: {v.duration}s, Audio: {audio_duration}s")
|
| 271 |
|
| 272 |
# Ajuster la durée à celle de l'audio
|
| 273 |
if v.duration < audio_duration:
|
| 274 |
# Si la vidéo est plus courte, la boucler
|
| 275 |
+
print(f"[Préparation vidéo] Bouclage vidéo ({v.duration}s -> {audio_duration}s)")
|
| 276 |
v = v.fx(vfx.loop, duration=audio_duration)
|
| 277 |
elif v.duration > audio_duration:
|
| 278 |
# Si la vidéo est plus longue, la couper
|
| 279 |
+
print(f"[Préparation vidéo] Découpage vidéo ({v.duration}s -> {audio_duration}s)")
|
| 280 |
v = v.subclip(0, audio_duration)
|
| 281 |
|
| 282 |
# Ajuster la taille et la position
|
| 283 |
if plein_ecran:
|
| 284 |
+
print(f"[Préparation vidéo] Mode plein écran")
|
| 285 |
v = v.resize((W, H)).set_position(("center", "center"))
|
| 286 |
else:
|
| 287 |
+
print(f"[Préparation vidéo] Mode incrustation, position: {position}")
|
| 288 |
v = v.resize(width=520) # Taille réduite pour le coin
|
| 289 |
pos_map = {
|
| 290 |
"bottom-right": ("right", "bottom"),
|
|
|
|
| 295 |
}
|
| 296 |
v = v.set_position(pos_map.get(position, ("right", "bottom")))
|
| 297 |
|
| 298 |
+
print(f"[Préparation vidéo] Vidéo préparée avec succès")
|
| 299 |
return v
|
| 300 |
except Exception as e:
|
| 301 |
print(f"[Préparation vidéo] Erreur : {e}")
|
| 302 |
+
print(f"[Préparation vidéo] Traceback: {traceback.format_exc()}")
|
| 303 |
return None
|
| 304 |
|
| 305 |
# ============================================================
|
|
|
|
| 396 |
# 4) Vidéo présentateur (au lieu de SadTalker)
|
| 397 |
clips = [bg]
|
| 398 |
if video_presentateur and os.path.exists(video_presentateur):
|
| 399 |
+
print(f"[Capsule] Vidéo présentateur trouvée: {video_presentateur}")
|
| 400 |
v_presentateur = _prepare_video_presentateur(
|
| 401 |
video_presentateur,
|
| 402 |
dur,
|
|
|
|
| 404 |
plein
|
| 405 |
)
|
| 406 |
if v_presentateur:
|
| 407 |
+
print(f"[Capsule] Vidéo présentateur ajoutée avec succès")
|
| 408 |
clips.append(v_presentateur)
|
| 409 |
+
else:
|
| 410 |
+
print(f"[Capsule] Échec de préparation de la vidéo présentateur")
|
| 411 |
+
else:
|
| 412 |
+
print(f"[Capsule] Aucune vidéo présentateur fournie ou fichier introuvable: {video_presentateur}")
|
| 413 |
|
| 414 |
# 5) Composition + export
|
| 415 |
final = CompositeVideoClip(clips).set_audio(audio.set_fps(44100))
|
|
|
|
| 434 |
audio.close()
|
| 435 |
final.close()
|
| 436 |
bg.close()
|
| 437 |
+
if 'v_presentateur' in locals() and v_presentateur is not None:
|
| 438 |
v_presentateur.close()
|
| 439 |
+
if os.path.exists(audio_mp):
|
| 440 |
+
os.remove(audio_mp)
|
| 441 |
+
if audio_wav != audio_mp and os.path.exists(audio_wav):
|
| 442 |
+
os.remove(audio_wav)
|
| 443 |
except Exception as e:
|
| 444 |
print(f"[Clean] Erreur nettoyage : {e}")
|
| 445 |
gc.collect()
|
| 446 |
|
|
|
|
|
|
|
| 447 |
# ============================================================
|
| 448 |
# GESTION / ASSEMBLAGE
|
| 449 |
# ============================================================
|