Construire un outil de transcription audio web
mai 2026 · 9 min de lecture
Tester l'outil en live
Avant de plonger dans le guide technique, vous pouvez tester directement l'outil qui a inspiré ce guide :
C'est un transcripteur audio gratuit construit avec exactement la stack décrite ici. Il permet de :
- Coller un lien TikTok, Twitter/X ou Facebook et récupérer la transcription en quelques secondes
- Uploader un fichier audio ou vidéo (MP3, MP4, WAV, OGG, WebM, M4A, FLAC — jusqu'à 25 Mo) pour toute autre source : YouTube, Instagram, podcast, réunion enregistrée, voice memo…
Limites : 3 transcriptions gratuites par IP par jour (protection anti-abus).
Contact / questions : natblavo@ceepia.com
Ce projet est open à l'échange — si vous avez des questions sur l'implémentation après avoir lu ce guide, n'hésitez pas.
Ce guide explique comment construire un outil web qui convertit l'audio de vidéos (ou de fichiers uploadés) en texte, et le déployer publiquement. Il couvre les grandes lignes — architecture, choix techniques, déploiement, sécurité — avec des liens vers les ressources utiles.
Vue d'ensemble
L'idée est simple : l'utilisateur colle un lien ou uploade un fichier → le serveur extrait l'audio → une API de transcription renvoie le texte.
Utilisateur
│
▼
Interface web (HTML/JS)
│ POST /transcribe (URL ou fichier)
▼
Backend Python (FastAPI)
├── yt-dlp → télécharge l'audio depuis l'URL
└── API de transcription (Voxtral, Whisper, Azure…)
│
▼
Texte renvoyé à l'utilisateur
Stack recommandée
Backend
FastAPI — framework Python moderne, async natif, idéal pour des APIs légères. Gère à la fois les routes JSON et les uploads de fichiers multipart.
pip install fastapi uvicorn[standard] httpx python-multipart
Extraction audio depuis une URL
yt-dlp — le successeur de youtube-dl. Supporte plusieurs centaines de plateformes (TikTok, Twitter/X, Facebook, Twitch, Reddit…). Nécessite ffmpeg pour la conversion audio.
pip install yt-dlp
apt install ffmpeg # ou brew install ffmpeg sur Mac
⚠️ Limitation importante : YouTube et Instagram exigent désormais une authentification (cookies d'un compte connecté) pour tout téléchargement automatisé. Pour un outil public, préférez l'upload de fichier plutôt que de dépendre de ces plateformes.
Pour exporter des cookies depuis votre navigateur si nécessaire : voir cette section du wiki yt-dlp.
Transcription
Plusieurs options selon votre budget et vos besoins :
| Service | Type | Avantages | Inconvénients |
|---|---|---|---|
| Voxtral (Mistral AI) | API cloud | Très précis, multilingue, rapide | Plan Experiment gratuit (voir note ci-dessous) |
| OpenAI Whisper | Local ou API | Open source, gratuit en local | Lent sur CPU, GPU recommandé |
| Groq Whisper | API cloud | Très rapide, généreux en free tier | Moins de langues |
| Azure Speech | API cloud | Intégration facile, SLA enterprise | Interface complexe |
| AssemblyAI | API cloud | Fonctionnalités avancées (chapitres, sentiment…) | Payant |
💡 Mistral AI propose un plan "Experiment" entièrement gratuit sur AI Studio. Il donne accès à tous leurs modèles frontier (dont Voxtral pour la transcription), à la création et au déploiement d'agents, sans carte bancaire. En contrepartie, les requêtes peuvent être utilisées pour améliorer leurs modèles. C'est le moyen le plus rapide de démarrer sans dépenser un centime.
Pour une intégration rapide avec une API compatible OpenAI (Voxtral, Groq) :
import httpx
async def transcribe(audio_bytes: bytes, filename: str, api_key: str) -> str:
async with httpx.AsyncClient(timeout=120) as client:
response = await client.post(
"https://api.mistral.ai/v1/audio/transcriptions", # ou votre endpoint
headers={"Authorization": f"Bearer {api_key}"},
files={"file": (filename, audio_bytes, "audio/mpeg")},
data={"model": "voxtral-mini-2507"},
)
return response.json()["text"]
Pour faire tourner Whisper en local :
import whisper
model = whisper.load_model("base") # ou "small", "medium", "large"
result = model.transcribe("audio.mp3")
print(result["text"])
Rate limiting
Pour éviter les abus sur un outil public, limitez les requêtes par IP. Deux approches :
In-memory (simple, sans dépendance)
Adapté pour un serveur unique. Se réinitialise au redémarrage. Stockez un compteur par IP et par jour, incrémentez-le à chaque requête, et renvoyez une erreur HTTP 429 quand la limite est atteinte.
Redis (robuste, multi-instances)
Avec slowapi (wrapper de limits pour FastAPI) :
from slowapi import Limiter
from slowapi.util import get_remote_address
limiter = Limiter(key_func=get_remote_address, storage_uri="redis://localhost:6379")
@app.post("/transcribe")
@limiter.limit("3/day")
async def transcribe(request: Request, ...):
...
Bonne pratique : si votre app est derrière un reverse proxy (Nginx, Caddy), configurez le proxy pour injecter l'IP réelle du client dans un header dédié, et lisez ce header côté backend plutôt que de vous fier aux headers bruts de la requête.
Upload de fichier
Pour accepter des fichiers audio/vidéo directement (contourne les restrictions des plateformes) :
from fastapi import File, UploadFile
ALLOWED_MIME = {"audio/mpeg", "audio/mp4", "audio/wav", "audio/ogg",
"video/mp4", "video/webm", "audio/webm"}
MAX_BYTES = 25 * 1024 * 1024 # 25 Mo
@app.post("/transcribe/file")
async def transcribe_file(file: UploadFile = File(...)):
if file.content_type not in ALLOWED_MIME:
raise HTTPException(415, "Format non supporté.")
content = await file.read()
if len(content) > MAX_BYTES:
raise HTTPException(413, "Fichier trop volumineux.")
# → envoyer content à votre API de transcription
Côté frontend, avec un drag & drop :
const dropzone = document.getElementById("dropzone");
dropzone.addEventListener("drop", async (e) => {
e.preventDefault();
const file = e.dataTransfer.files[0];
const form = new FormData();
form.append("file", file);
const res = await fetch("/transcribe/file", { method: "POST", body: form });
const { transcript } = await res.json();
});
Containerisation avec Docker
Un Dockerfile minimal pour une app FastAPI + ffmpeg + yt-dlp :
FROM python:3.12-slim
RUN apt-get update && apt-get install -y --no-install-recommends ffmpeg \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
Lancer le container en n'exposant le port que sur localhost (le reverse proxy s'en charge) :
docker build -t mon-transcripteur .
docker run -d \
--name mon-transcripteur \
--restart unless-stopped \
-p 127.0.0.1:8010:8000 \
--env-file .env \
mon-transcripteur
Déploiement sur un VPS
Choisir un hébergeur
N'importe quel VPS Linux fait l'affaire. Quelques options :
- Hetzner — excellent rapport qualité/prix, datacenters en Europe (RGPD ✓)
- OVH — français, bon marché
- DigitalOcean — simple, bien documenté
- Oracle Cloud Free Tier — 2 VMs ARM gratuites à vie
Reverse proxy avec Caddy
Caddy gère automatiquement les certificats TLS (HTTPS) via Let's Encrypt. Zéro configuration manuelle.
Installer Caddy sur Ubuntu :
apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list
apt update && apt install caddy
Config minimale (/etc/caddy/Caddyfile) :
monsite.com {
reverse_proxy 127.0.0.1:8010
encode zstd gzip
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
-Server
}
}
systemctl enable --now caddy
systemctl reload caddy # après chaque modification
Caddy obtient le certificat TLS automatiquement dès que le DNS pointe vers votre serveur.
Sécurité — bonnes pratiques
- Headers HTTP : configurez
Strict-Transport-Security,X-Content-Type-Options,X-Frame-Options,Content-Security-PolicyetPermissions-Policyau niveau du reverse proxy - Rate limiting : limitez les requêtes par IP (voir section dédiée)
- Validation des entrées : vérifiez le content-type et la taille des fichiers uploadés côté serveur, jamais uniquement côté client
- Clés API : stockez-les dans des variables d'environnement (
.env), jamais dans le code source ni dans le dépôt git - Fichiers temporaires : supprimez-les immédiatement après traitement — en Python,
tempfile.TemporaryDirectory()s'en charge automatiquement - Réseau : n'exposez pas directement votre backend sur Internet ; laissez le reverse proxy gérer les connexions entrantes
Ressources
| Sujet | Lien |
|---|---|
| Extraction audio | yt-dlp |
| Backend Python | FastAPI |
| Transcription open source | OpenAI Whisper |
| Transcription API rapide | Groq (Whisper) |
| Transcription Mistral | Voxtral |
| Rate limiting FastAPI | slowapi |
| Reverse proxy + TLS auto | Caddy |
| Containerisation | Docker |
| Cookies yt-dlp (si nécessaire) | Wiki yt-dlp |