CyberSDF

Reprenez la main sur votre numérique

Générer un fichier .srt horodaté avec whisper –output_format srt

un petit serveur domestique pose sur une etagere, diode allumee, piece calme

La commande whisper reunion.m4a --model small --language fr --output_format srt --output_dir ~/sous-titres écrit un seul fichier, ~/sous-titres/reunion.srt, où chaque bloc porte un numéro, une plage du type 00:00:00,000 --> 00:00:04,480, puis le texte du segment. Dans openai-whisper 20250625, la dernière version publiée sur PyPI le 26 juin 2025, --output_format accepte les valeurs txt, vtt, srt, tsv, json et all ; la valeur par défaut est all, qui écrit les cinq formats à la fois. Le writer SRT de whisper/utils.py place toujours les heures, même quand elles valent zéro, et sépare les millisecondes par une virgule, comme SubRip l’attend. Ces valeurs viennent du code source de openai-whisper 20250625 ; un environnement virtuel Python sous Debian 12, Debian 13 ou Ubuntu 24.04 installe le même paquet par pip et se comporte de la même façon.

Sommaire
  1. Le .srt est écrit dans le dossier courant, pas à côté de l’audio
  2. La procédure en cinq étapes, de la version installée au fichier relu
  3. –max_line_width sans –word_timestamps True : whisper s’arrête après avoir chargé le modèle
  4. La règle professionnelle est de 42 caractères sur deux lignes, whisper ne l’applique pas
  5. Un seul fichier avec srt, cinq avec la valeur par défaut all
  6. –highlight_words True écrit un bloc par mot, sans soulignement garanti
  7. Incruster un .srt dans la vidéo, et l’erreur Unable to open
  8. Questions fréquentes sur les fichiers SRT de whisper
whisper reunion.m4a --model small --language fr --output_format srt --output_dir ~/sous-titres

Le fichier produit a cette forme, chaque entrée étant séparée de la suivante par une ligne vide :

1
00:00:00,000 --> 00:00:04,480
Bienvenue dans cette réunion du comité de suivi.

2
00:00:04,480 --> 00:00:09,120
Le premier point porte sur les sauvegardes.

Le .srt est écrit dans le dossier courant, pas à côté de l’audio

Dans openai-whisper 20250625, --output_dir a pour défaut le dossier courant, noté d’un point dans le code, et son alias -o prend le même argument. Lancer whisper depuis ~/Documents sur un fichier rangé dans ~/Vidéos dépose donc le .srt dans ~/Documents. Le nom de sortie reprend le nom du fichier audio sans son extension : reunion.m4a donne reunion.srt. Le dossier de destination est créé quand il n’existe pas encore. Un .srt introuvable vient le plus souvent de là, la transcription elle-même s’étant bien passée.

La procédure en cinq étapes, de la version installée au fichier relu

  1. Relever la version installée et vérifier que ffmpeg répond, car whisper décode l’audio par ce binaire externe.

    python -m pip show openai-whisper
    ffmpeg -version

    Ce que vous devez voir : une ligne Version: 20250625 dans la sortie de pip, puis une première ligne commençant par ffmpeg version. Une seconde commande sans réponse signale le binaire manquant.

  2. Lancer la transcription en SRT seul, en nommant la langue et le dossier de sortie. Le modèle small annoncé par le dépôt openai/whisper compte 244 millions de paramètres et environ 2 Go de mémoire graphique, contre 809 millions et environ 6 Go pour le modèle turbo utilisé par défaut.

    whisper reunion.m4a --model small --language fr --output_format srt --output_dir ~/sous-titres

    Ce que vous devez voir : les segments défilent en sortie standard sous la forme [00:00.000 --> 00:04.480] texte reconnu, puis l’invite revient. Comme --language fr est fourni, aucune détection n’est lancée ; sans lui, la sortie commence par Detecting language using up to the first 30 seconds. Use `--language` to specify the language, suivie de Detected language: French. Aucun autre fichier que le .srt n’est écrit. Plusieurs fichiers audio peuvent être passés d’un coup dans la même commande ; l’enchaînement sur un dossier entier relève d’une petite routine automatisée. Le choix du modèle et de la mémoire se discute dans la rubrique IA, aux côtés de choisir une IA locale.

  3. Ouvrir le .srt et contrôler sa structure avant de l’envoyer à un lecteur.

    ls -l ~/sous-titres
    head -n 8 ~/sous-titres/reunion.srt
    grep -c -- ' --> ' ~/sous-titres/reunion.srt

    Ce que vous devez voir : reunion.srt dans la liste, puis les deux premiers blocs affichés par head, quatre lignes chacun, avec le numéro seul sur la première ligne, une plage horaire à virgule et à heures sur deux chiffres, le texte, puis une ligne vide. grep -c renvoie le nombre de blocs, égal au nombre de segments dans cette configuration. Le fichier est écrit en UTF-8.

  4. Refaire la passe avec la mise en forme écran quand le .srt est destiné à des yeux humains. --max_line_width et --max_line_count ne produisent rien sans --word_timestamps True, qui recalcule les bornes sur la position des mots.

    whisper reunion.m4a --model small --language fr --word_timestamps True --max_line_width 42 --max_line_count 2 --output_format srt --output_dir ~/sous-titres

    Ce que vous devez voir : les mêmes segments à l’écran, puis, au head suivant, des blocs coupés en deux lignes au plus, chaque ligne restant sous 42 caractères sauf mot plus long à lui seul. Le fichier est réécrit sous le même nom, la version précédente est remplacée.

  5. Poser le .srt à côté de la vidéo, car un lecteur ne charge seul qu’un fichier de même nom placé dans le même dossier : pour video.mp4, il cherche video.srt. Le plus court est de donner ce dossier à --output_dir, ou de déplacer le fichier ensuite avec les commandes de fichiers habituelles.

    whisper ~/Vidéos/video.mp4 --model small --language fr --output_format srt --output_dir ~/Vidéos
    cp ~/sous-titres/video.srt ~/Vidéos/

    Ce que vous devez voir : la vidéo s’ouvre avec ses sous-titres affichés, sans manipulation, et la piste cochée dans le menu Sous-titre du lecteur.

–max_line_width sans –word_timestamps True : whisper s’arrête après avoir chargé le modèle

Les quatre options de mise en forme, --highlight_words, --max_line_count, --max_line_width et --max_words_per_line, sont refusées sans horodatage de mots, et le message vient de la bibliothèque argparse :

whisper: error: --max_line_width requires --word_timestamps True

Le contrôle a lieu après load_model() dans la fonction cli() : sur une installation neuve, le modèle est donc téléchargé avant que la commande soit refusée, et le code de retour vaut 2. Le remède tient en un ajout, --word_timestamps True. L’option citée dans le message est la première de la liste interne qui est renseignée, dans cet ordre : highlight_words, max_line_count, max_line_width, max_words_per_line.

Deux combinaisons sont acceptées mais signalées par un avertissement qui n’arrête rien. Avec --max_line_count 2 seul, la transcription démarre et Python écrit UserWarning: --max_line_count has no effect without --max_line_width : les blocs ne sont pas découpés. Avec une largeur et --max_words_per_line ensemble, il écrit UserWarning: --max_words_per_line has no effect with --max_line_width.

La règle professionnelle est de 42 caractères sur deux lignes, whisper ne l’applique pas

Le guide de style French (France) de Netflix fixe 42 caractères par ligne, deux lignes au maximum, et une vitesse de lecture de 17 caractères par seconde pour un programme destiné aux adultes ; ces valeurs figurent dans le document révisé en 2025. Dans openai-whisper 20250625, rien de tel n’est actif par défaut : la largeur de ligne vaut None, remplacée en interne par 1000 caractères, et le texte est découpé sur une fenêtre glissante de 30 secondes. Sans option de mise en forme, un passage de trente secondes ressort en un seul bloc sur une seule ligne, que le lecteur rogne à l’affichage.

Avec les deux options, la mécanique de whisper/utils.py tient en trois règles : la ligne est coupée avant le mot qui dépasserait 42 caractères, un nouveau bloc s’ouvre quand le nombre de lignes atteint --max_line_count, et un silence de plus de 3 secondes, valeur écrite en dur dans le fichier, ouvre aussi un nouveau bloc. Les deux options vont ensemble : avec --max_line_width seul, la coupure de ligne s’applique mais ni le découpage en blocs ni la règle des 3 secondes, la variable preserve_segments restant vraie.

Un seul fichier avec srt, cinq avec la valeur par défaut all

Formats écrits par openai-whisper 20250625, relevés dans whisper/utils.py
Format Extension Horodatage Contenu du fichier
srt .srt virgule décimale, heures toujours présentes blocs numérotés à partir de 1
vtt .vtt point décimal, heures omises quand elles valent zéro en-tête WEBVTT, blocs sans numéro
txt .txt aucun texte des segments, un par ligne
tsv .tsv millisecondes entières ligne d’en-tête start, end, text
json .json secondes décimales segments complets, mots inclus avec –word_timestamps True

La branche de développement du dépôt ajoute un sixième writer, jsonl, qui écrit un segment par ligne. Il n’est pas dans le paquet 20250625, et la valeur est refusée par une installation publiée :

whisper: error: argument --output_format: invalid choice: 'jsonl' (choose from 'txt', 'vtt', 'srt', 'tsv', 'json', 'all')

Le même message sort avec --output_format subrip, nom du format et non du logiciel, ou avec deux formats séparés par une virgule. Une seule valeur par exécution, ou all.

–highlight_words True écrit un bloc par mot, sans soulignement garanti

Avec --highlight_words True, le writer SRT cesse de regrouper le texte par segment : chaque mot produit une entrée, qui répète toute la réplique et entoure le mot courant de balises <u>. Le fichier compte alors autant de blocs que de mots, avec la même phrase répétée d’un bloc au suivant. Le rendu dépend du lecteur : la table des formats de sous-titres du wiki VideoLAN attribue à SubRip (*.srt) la mention « Text Styling: No », donc le soulignement n’est pas affiché par VLC.

Incruster un .srt dans la vidéo, et l’erreur Unable to open

Le filtre subtitles de ffmpeg, décrit dans son code source comme « Render text subtitles onto input video using the libass library », prend le fichier par son option filename et grave le texte dans les images :

ffmpeg -i video.mp4 -vf subtitles=video.srt -c:a copy video-incrustee.mp4

Quand le chemin donné au filtre n’est pas résolu, ffmpeg s’arrête sur une ligne qui se termine par le nom du fichier :

Unable to open video.srt

Le chemin est résolu depuis le dossier d’où ffmpeg est lancé, pas depuis celui où la vidéo a été trouvée. Un chemin complet règle le cas, et l’opération peut être relancée telle quelle après correction. Le son est recopié sans réencodage par -c:a copy, seul le flux vidéo est réécrit pour porter le texte.

Questions fréquentes sur les fichiers SRT de whisper

Pourquoi le .srt ne se charge-t-il pas tout seul dans le lecteur ?

Parce que le fichier externe doit porter le même nom que la vidéo et se trouver dans le même dossier : video.mp4 appelle video.srt. Comme --output_dir de whisper vaut le dossier courant par défaut, un .srt produit depuis un autre dossier reste invisible au lecteur. Le contrôle tient en une commande : ls -l ~/Vidéos/video.mp4 ~/Vidéos/video.srt.

Le .srt contient « Sous-titres réalisés par la communauté d’Amara.org », d’où vient ce texte ?

Ce n’est pas une ligne du fichier audio mais une hallucination du modèle, produite sur les passages sans parole ou couverts par de la musique. Un rapport d’anomalie de whisper.cpp ouvert le 22 décembre 2024 en donne un exemple en français, la phrase remplaçant la transcription et se répétant cinq fois. Deux leviers documentés dans openai-whisper : --condition_on_previous_text False, qui rend le modèle moins enclin aux boucles de répétition, et --hallucination_silence_threshold 2 avec --word_timestamps True, qui saute les silences précédant une hallucination.

Peut-on obtenir l’horodatage de chaque mot dans le .srt ?

Le SRT reste un format par blocs : avec --word_timestamps True, les bornes de chaque bloc sont recalculées sur la position des mots, plus fines que celles du segment, mais les mots ne sont pas écrits un par un dans le fichier. Le détail mot par mot part dans le .json de la même exécution, dans le tableau words de chaque segment ; c’est ce fichier qui sert de base à un traitement automatique.

Faut-il relancer la transcription pour changer 42 et 2 ?

Pas si le .json a été conservé avec les horodatages de mots, le writer SRT de whisper acceptant d’être rappelé sur ce fichier sans repasser par le modèle.

python - <<'EOF'
import json
from whisper.utils import get_writer
result = json.load(open("reunion.json", encoding="utf-8"))
writer = get_writer("srt", ".")
writer(result, "reunion.m4a", max_line_width=42, max_line_count=2)
EOF

Le fichier reunion.srt du dossier courant est réécrit avec la nouvelle mise en forme, la transcription n’étant pas relancée.

Un .srt de zéro octet alors que la commande s’est bien terminée, que s’est-il passé ?

Le fichier de sortie est créé par le writer avant de recevoir du contenu : une transcription qui ne produit aucun segment, sur un enregistrement sans parole détectée, laisse donc un fichier vide, avec un code de retour 0 et aucun message. Le contrôle se fait sur la taille du fichier, avec un ls -l, et non sur le code de retour de la commande.