Vu en production avec Qwen sur GPU : la plupart des phrases basculaient sur
Gemini puis Edge (voix différente d'un sous-titre à l'autre).
- Vérification de lecture : les nombres en lettres (« trente », « vingt-cinq
pour cent ») et leur signe (« plus », « moins ») sont comparés comme les
chiffres écrits par Whisper, au lieu de passer pour des mots sautés.
- Prises emballées (14 s pour 34 caractères) écartées d'office ; côté
qwen-tts, max_new_tokens borne la durée à « caractères / 6 + 3 » s.
- Le moteur choisi est gardé tant qu'il lit au moins 70 % du texte
(avec avertissement), au lieu de 85 % ; 3 tentatives Qwen.
- Gemini : délai d'attente 45 s au lieu de 120 s, erreurs journalisées en repr.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Upu97wMmsRkBoj6iVM4rH6
- qwen-tts : GPU NVIDIA détecté automatiquement (bfloat16), image CUDA 12.8
(RTX 50xx comprises) via TORCH_VARIANT=cu128, docker-compose.gpu.yml
dédié (clé obligatoire, choix de la carte) et guide d'installation Unraid.
- Paramètres → « Qwen TTS (voix locale) » : adresse + clé du service, test
de connexion (modèle et carte graphique affichés), stockés en base
(app_config.qwen_tts_url / qwen_tts_api_key) et transmis à ffmpeg-api à
chaque voix ; QWEN_TTS_URL reste un défaut.
- Le service qwen-tts CPU du docker-compose principal devient optionnel
(profil qwen-local).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Upu97wMmsRkBoj6iVM4rH6
SRT : la voix n'est plus jamais accélérée (atempo sans plafond, jusqu'à 2×
et plus, rendait la voix incompréhensible). Un morceau plus long que son
sous-titre déborde et décale les suivants ; les sous-titres suivent la voix,
la vidéo s'allonge et un avertissement signale le décalage.
Rythme : style par défaut « neutre », consignes Gemini sans « rythme
entraînant », débit Edge des styles dynamique/promo ramené à +0 %/+3 %.
Nouveau moteur « qwen » : service qwen-tts (Qwen3-TTS sur CPU, gratuit,
français, ton piloté par consigne, clonage de voix depuis qwen-tts/voices).
Même vérification Whisper que Gemini, généralisée ; repli qwen → gemini →
edge, et une lecture sous 85 % du texte est écartée (mots sautés/faux).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Upu97wMmsRkBoj6iVM4rH6
- Contrôle de lecture : les nombres et l'unité qui les suit ne sont plus
comparés lettre à lettre. Whisper écrit « 11 € 99 » ou « 40 cm » là où le
texte dit « 11,99 € » ou « 40 centimètres » : une lecture correcte passait
pour des mots sautés (88–91 %) et relançait jusqu'à 3 générations.
- Découpe de la lecture SRT : la coupe entre deux sous-titres se fait dans
la pause mesurée la plus proche de la borne estimée (fins de phrases
préférées aux virgules), et non plus à la borne Whisper elle-même, fausse
quand un prix non reconnu termine la phrase : la coupe tombait alors au
milieu d'une phrase.
- Whisper reçoit 0,5 s de silence avant la voix : il laissait passer le
premier mot (« Alerte ») d'une voix qui démarre aussitôt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X3ywVwJUjjUXuvM6PwSzSi
Le contrôle lettre à lettre acceptait une lecture dès 85 %, alors qu'une
lecture complète atteint ~97 % (petites erreurs de Whisper) : une lecture à
86 % (mots sautés) passait sans nouvelle génération. Seuil porté à 92 %.
Les mots non entendus sont nommés dans les journaux et l'avertissement,
pour savoir ce qui manquait. Le modèle Whisper, déjà dans l'image, est
chargé sans interroger Hugging Face (HF_HUB_OFFLINE).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Ze4bs7tpF1KGWUk6ZZSZ4
Fins de phrases coupées :
- Le fondu final ne commence plus avant la fin de la voix (il rognait la
dernière phrase quand la vidéo finissait peu après) ; il se raccourcit au
besoin. Même règle côté Python, aperçu et rendu Remotion
- SRT : découpe au milieu de la zone la plus calme de chaque pause, mesurée
sur l'enveloppe sonore (tranches de 10 ms), avec un seuil relatif au
niveau de la voix qui respecte les fins douces ; 300 ms gardées après le
dernier son
Bruits parasites :
- Normalisation de la voix en deux passes, en mode linéaire : le mode
dynamique relevait souffles et bruits entre les phrases (mesuré : -64 dB
avant, -75 dB après dans les pauses)
- Réduction de bruit douce (afftdn), compresseur sans gain ajouté, fondus
de 5 ms aux extrémités ; mix final moins « pompant » (LRA 20)
- Voix Gemini vérifiée : les bruits ajoutés avant le premier ou après le
dernier mot sont retirés
Vérification de lecture comparée lettre à lettre : les petites erreurs de
Whisper ne déclenchent plus de régénérations inutiles.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Ze4bs7tpF1KGWUk6ZZSZ4
Intonation :
- Gemini reçoit une température basse et une graine fixe (réglables) : la
lecture ne change plus de ton d'une génération à l'autre
- SRT : tout le texte est lu d'une seule traite puis découpé phrase par
phrase, au lieu d'une génération (et d'une intonation) par sous-titre ;
un seul traitement de niveau, plus de sauts de volume entre les phrases
Mots coupés :
- La voix est découpée dans ses vrais silences (silencedetect), plus aux
bornes des mots estimées par Whisper qui rognaient les syllabes ; fondus
de 10 ms aux coupes
- Whisper n'est plus amorcé avec le texte attendu : il hallucinait des
répétitions qui masquaient les fins tronquées
Fiabilité :
- Chaque voix Gemini est réécoutée : mots sautés, fin tronquée ou consigne
lue à voix haute déclenchent une nouvelle génération (3 au plus), puis
repli sur Edge avec avertissement
- Consigne de lecture intégrale ajoutée au prompt
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Ze4bs7tpF1KGWUk6ZZSZ4
Sans musique (bouton « Passer »), la bande son de la vidéo était supprimée
dès que la voix était activée. Elle sert désormais de fond sonore : conservée
telle quelle, baissée automatiquement sous la voix, puis normalisée avec le
reste. Une musique choisie la remplace toujours.
Même comportement dans l'aperçu, le rendu Remotion et le rendu FFmpeg de
secours. Le bouton indique « Passer (garder le son de la vidéo) ».
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Ze4bs7tpF1KGWUk6ZZSZ4
Le petit logo affiché pendant la vidéo devient optionnel (activé par
défaut), sur les 4 pages Reel (vidéo et images, bureau et mobile) ainsi que
dans l'aperçu. Le grand logo de l'effet de fin reste piloté par son propre
interrupteur. Pris en charge par le rendu Remotion et par le rendu FFmpeg de
secours (show_watermark).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Ze4bs7tpF1KGWUk6ZZSZ4
- Nouveau Reel (bureau et mobile) : bouton « Importer un fichier SRT ».
Tant qu'un SRT est chargé, le texte libre et l'assistant IA sont
désactivés ; le choix de la voix (moteur, voix, ton) reste disponible.
- Service Python : chaque sous-titre est lu séparément, ses silences
retirés, puis posé à son instant de début. Une lecture trop longue est
accélérée (atempo, sans changer la hauteur) pour tenir dans la durée du
sous-titre, avec un avertissement au-delà de ×1,35. Après un repli sur
Edge, tous les sous-titres gardent la même voix.
- Sans voix, les mots de chaque sous-titre s'affichent sur son intervalle.
- Aperçu : sous-titres calés sur le minutage du fichier.
- Tests : lecture SRT (TS), fenêtres, accélération et mixage (Python).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WVqEw4Xycgf8BtwmSftE2M
Montage (étape 3) :
- Nouvelle composition ReelVideo : sous-titres animés mot à mot (3 styles :
Impact, Surligné, Épuré), logo, effet de fin et fondu, en React
- Le service Python prépare l'image (recadrage, HDR, stabilisation, dernière
image figée) et la piste son finale (/prepare-reel) ; Remotion compose
- Reels d'images sur les mêmes composants, avec le vrai minutage de la voix
- Police Montserrat embarquée (plus de dépendance à Google Fonts)
- REEL_RENDERER=ffmpeg conserve le rendu FFmpeg, plus rapide, en secours
- Script de pré-bundle réparé (échouait en silence : require en ESM)
Interface (étape 4) :
- Aperçu en direct avec @remotion/player, identique au rendu final ; la voix
testée cale les sous-titres, sinon minutage estimé
- Choix du style de sous-titres sur les 4 pages Reel
- Vraie progression : étape réelle du rendu, échecs visibles 24 h avec leur
cause ; fin de la barre simulée et du faux « publié avec succès »
Outillage (étape 5) :
- Tests vitest (minutage identique à Python, validation, sécurité) et
pytest ; CI GitHub Actions (tsc, tests, build, ruff)
- Captures d'écran, out.mp4 et scripts de test retirés de la racine
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Ze4bs7tpF1KGWUk6ZZSZ4
Service ffmpeg-api réécrit (ffmpeg-service/app/) :
- Appels FFmpeg asynchrones : le service ne se fige plus pendant un rendu
- Vidéo récupérée par téléchargement (GET /files/…) au lieu de base64 en JSON
- Vraies erreurs HTTP ; échec explicite si la voix demandée est impossible
- 30 voix Gemini + ton de lecture (dynamique, chaleureux, promo, calme),
clé en en-tête, modèle configurable avec repli
- Edge TTS 7 (minutage des mots restauré), secours en voix françaises
- Voix traitée : filtre, compression, niveau constant ; musique bouclée et
baissée automatiquement sous la voix ; mix final à -14 LUFS
- Sous-titres calés mot à mot (Whisper pour Gemini et la voix d'origine,
à la place de ffsubsync), style Montserrat, placés hors des boutons Reels
- Vidéo : plus de retouche luminosité forcée, scaling lanczos, HDR iPhone
converti, BT.709, AAC 48 kHz 192k ; la vidéo s'allonge si la voix dépasse
- Grand logo de fin affiché après la voix ; FFmpeg 7.0.2 épinglé, polices et
modèle Whisper intégrés à l'image ; tests pytest et ruff
Application :
- Sélecteur de voix partagé (4 pages) : moteur, 30 voix, ton, écoute
- Reel images : minutage réel des mots, interrupteur voix respecté
- sync-info ne génère plus de voix à chaque frappe (estimation locale)
- Stabilisation désactivée par défaut, route /reels/preview inutilisée retirée
- Log « [ReelQueue] Worker démarré » pour vérifier la version déployée
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Ze4bs7tpF1KGWUk6ZZSZ4