/video-subtitle — Outil de sous-titrage vidéo
Transcrire l'audio d'une vidéo en sous-titres SRT à l'aide d'un modèle Whisper local, puis les incruster dans la vidéo.
Invocable par l'utilisateur
Lorsque l'utilisateur tape /video-subtitle, exécutez cette compétence.
Prérequis
Cette compétence nécessite deux outils. Vérifiez leur disponibilité avant de procéder :
1. mlx-whisper (Apple Silicon) ou openai-whisper
Détectez lequel est disponible :
which mlx_whisper whisper 2>/dev/null
Si aucun n'est trouvé, guidez l'utilisateur pour en installer un :
- Mac Apple Silicon (recommandé) :
pip install mlx-whisper - Toute plateforme :
pip install openai-whisper
2. ffmpeg avec libass
Vérifiez le support de libass :
ffmpeg -filters 2>&1 | grep subtitles
Si aucune sortie (libass manquant), le filtre subtitles est indisponible :
- macOS :
brew install ffmpeg-full(keg-only, utilisez/opt/homebrew/opt/ffmpeg-full/bin/ffmpeg) - Linux :
sudo apt install ffmpeg(inclut généralement libass) - Solution de repli : Si l'utilisateur a ffmpeg classique sans libass, intégrez doucement le SRT comme piste de sous-titres au lieu de l'incruster.
Sélection du modèle Whisper
Découvrez les modèles en cache :
ls ~/.cache/huggingface/hub/ 2>/dev/null | grep whisper
Préférez le modèle le plus grand disponible. Si aucun n'est en cache, utilisez :
- mlx-whisper :
mlx-community/whisper-large-v3-mlx - openai-whisper :
large-v3
Modes
/video-subtitle chemin/vers/video.mov→ Pipeline complet (transcription + correction + incrustation)/video-subtitle srt chemin/vers/video.mov→ SRT uniquement (transcription, sans incrustation)/video-subtitle burn chemin/vers/video.mov chemin/vers/sub.srt→ Incrustation uniquement (utiliser un SRT existant)/video-subtitle(sans argument) → Interactif, demander le chemin de la vidéo
Pipeline complet
Étape 1 : Vérifier la vidéo
Si aucun chemin n'est fourni, demandez-le avec AskUserQuestion. Confirmez que le fichier existe et lisez les métadonnées :
ffmpeg -i "<video_path>" 2>&1 | grep -E "Duration|Stream.*(Video|Audio)"
Signalez la durée, la résolution et la taille du fichier à l'utilisateur.
Étape 2 : Extraire l'audio
ffmpeg -i "<video_path>" -vn -acodec pcm_s16le -ar 16000 -ac 1 "<output_dir>/<basename>_audio.wav" -y
Le WAV mono 16 kHz est le format d'entrée optimal pour Whisper.
Étape 3 : Confirmer la langue
Demandez à l'utilisateur avec AskUserQuestion. Options courantes :
| Langue | Drapeau |
|---|---|
| Chinois | --language zh |
| Anglais | --language en |
| Japonais | --language ja |
| Détection auto | omettre --language |
Étape 4 : Transcrire
mlx-whisper :
mlx_whisper --model "<model>" --language <lang> --output-format srt --output-dir "<output_dir>" "<audio_wav>"
openai-whisper :
whisper "<audio_wav>" --model large-v3 --language <lang> --output_format srt --output_dir "<output_dir>"
Dites à l'utilisateur : « Transcription en cours, veuillez patienter... »
Étape 5 : Nettoyer le SRT
Lisez le SRT généré et corrigez :
- Supprimer les hallucinations : Supprimez les entrées avec des horodatages de durée nulle (ex.
00:00:57,640 --> 00:00:57,640) ou un texte vide - Corriger les noms propres : Whisper reconnaît mal les noms de marques et les termes techniques — utilisez le contexte de la conversation pour corriger
- Fusionner les fragments : Combinez les entrées plus courtes que 0,5s qui sont sémantiquement liées
- Re numéroter : Exécutez cet extrait Python pour re numéroter toutes les entrées :
python3 -c "
import re
with open('<srt_path>', 'r') as f:
content = f.read()
blocks = re.split(r'\n\n+', content.strip())
out = []
for i, block in enumerate(blocks, 1):
lines = block.strip().split('\n')
if len(lines) >= 2:
lines[0] = str(i)
out.append('\n'.join(lines))
with open('<srt_path>', 'w') as f:
f.write('\n\n'.join(out) + '\n')
"
Présentez le SRT corrigé à l'utilisateur et demandez si d'autres modifications sont nécessaires avant l'incrustation.
Étape 6 : Incruster les sous-titres
Déterminez le bon binaire ffmpeg (préférez ffmpeg-full si ffmpeg classique manque de libass) :
FFMPEG="ffmpeg"
if ! ffmpeg -filters 2>&1 | grep -q subtitles; then
if [ -x /opt/homebrew/opt/ffmpeg-full/bin/ffmpeg ]; then
FFMPEG="/opt/homebrew/opt/ffmpeg-full/bin/ffmpeg"
fi
fi
Puis incrustez :
$FFMPEG \
-i "<video_path>" \
-vf "subtitles=<srt_path>:force_style='FontSize=24,PrimaryColour=&H00FFFFFF,OutlineColour=&H00000000,Outline=2,MarginV=30'" \
-c:v libx264 -preset medium -crf 23 \
-c:a aac -b:a 128k \
-pix_fmt yuv420p \
"<output_dir>/<basename>_subtitled.mp4" -y
Options force_style :
| Paramètre | Valeur par défaut | Description |
|---|---|---|
| FontSize | 24 | Taille de police |
| PrimaryColour | &H00FFFFFF | Texte blanc |
| OutlineColour | &H00000000 | Contour noir |
| Outline | 2 | Épaisseur du contour |
| MarginV | 30 | Marge inférieure |
Important : Le chemin du fichier SRT ne doit pas contenir de caractères spéciaux qui perturbent l'analyseur de filtre ffmpeg. Utilisez des chemins relatifs ou un lien symbolique si nécessaire.
Étape 7 : Terminé
Signalez le chemin et la taille du fichier de sortie. Suggérez open <chemin> pour prévisualiser. Rappelez à l'utilisateur que le fichier intermédiaire _audio.wav peut être supprimé.
Dépannage
| Problème | Cause | Solution |
|---|---|---|
No such filter: 'subtitles' | ffmpeg manque libass | Utilisez ffmpeg-full ou installez libass |
No such filter: 'drawtext' | ffmpeg manque libfreetype | Utilisez ffmpeg-full |
| Entrées SRT en double/vides | Hallucination de Whisper | Nettoyez à l'étape 5 |
| Les caractères chinois s'affichent comme des carrés | Police CJK manquante | macOS : repli automatique sur PingFang. Linux : apt install fonts-noto-cjk |
Error parsing filterchain | Caractères spéciaux dans le chemin SRT | Utilisez un chemin relatif, évitez les espaces |
Règles de qualité
- Transcription fidèle : Ne transcrivez que ce qui a été réellement dit — n'ajoutez ni ne réécrivez le sens
- Correction des noms propres : Corrigez les erreurs courantes de Whisper, mais confirmez avec l'utilisateur
- Rythme des sous-titres : Maximum deux lignes par entrée, maximum ~20 caractères CJK par ligne
- Nettoyage : Rappelez à l'utilisateur de supprimer les fichiers intermédiaires (_audio.wav) une fois terminé