Vous avez besoin de récupérer le texte d’une vidéo YouTube pour créer des sous-titres, résumer une conférence ou analyser du contenu ? Oubliez les services payants. Avec quelques lignes de Python et la puissance de l’IA Whisper d’OpenAI, vous pouvez créer votre propre outil de transcription automatique, capable même de traduire à la volée.
Dans cet article, nous allons construire un script robuste qui contourne les bugs récents de téléchargement YouTube (erreur HTTP 400) en utilisant la bibliothèque pytubefix.
1. Les Prérequis et l’Installation
Avant de toucher au code, nous devons installer les outils nécessaires. Ce projet repose sur deux piliers : Whisper (le cerveau de la transcription) et FFmpeg (le moteur de traitement audio).
Étape A : Installer FFmpeg (Crucial)
Whisper a absolument besoin de FFmpeg pour lire les fichiers audio. Sans lui, vous aurez une erreur « FileNotFound ».
Si vous utilisez Anaconda (recommandé), tapez ceci dans votre terminal :
conda install -c conda-forge ffmpeg
Sinon, sous Windows avec Chocolatey : choco install ffmpeg (ou installation manuelle).
Étape B : Installer les bibliothèques Python
Nous allons utiliser openai-whisper pour l’IA et pytubefix (une version corrigée de pytube) pour le téléchargement.
pip install pytubefix openai-whisper
2. Analyse du Code : Comment ça marche ?
Découpons le script pour comprendre la logique.
Les Imports et l’Initialisation
On charge les modules et on initialise le modèle Whisper. Le modèle « base » est le compromis idéal entre vitesse et précision pour commencer.
import os
from pytubefix import YouTube # Remplace pytube pour éviter les erreurs 400
import whisper
import re
from pathlib import Path
class YouTubeTranscriber:
def __init__(self, model_size="base"):
"""Initialise le transcripteur avec un modèle Whisper"""
print(f"🔄 Chargement du modèle Whisper: {model_size}")
self.model = whisper.load_model(model_size)
print("✅ Modèle chargé!\n")
Validation et Téléchargement
Cette partie est critique. pytubefix nous permet de contourner les restrictions de YouTube. La fonction validate_youtube_url gère aussi bien les liens classiques que les « Shorts ».
def validate_youtube_url(self, url):
"""Valide et normalise l'URL YouTube"""
patterns = [
r'(?:https?://)?(?:www\.)?youtube\.com/watch\?v=([a-zA-Z0-9_-]{11})',
r'(?:https?://)?(?:www\.)?youtu\.be/([a-zA-Z0-9_-]{11})',
r'(?:https?://)?(?:www\.)?youtube\.com/shorts/([a-zA-Z0-9_-]{11})'
]
for pattern in patterns:
match = re.search(pattern, url)
if match:
video_id = match.group(1)
return f"https://www.youtube.com/watch?v={video_id}", video_id
return url, None
def download_audio(self, video_url, output_path="temp"):
"""Télécharge l'audio d'une vidéo YouTube avec gestion d'erreurs améliorée"""
try:
# Validation de l'URL
validated_url, video_id = self.validate_youtube_url(video_url)
if not video_id:
print("❌ URL YouTube invalide")
return None
print(f"🔗 URL validée: {validated_url}")
# Création de l'objet YouTube
yt = YouTube(
validated_url,
use_oauth=False,
allow_oauth_cache=False
)
print(f"📹 Titre: {yt.title}")
print(f"👤 Auteur: {yt.author}")
print(f"⏱️ Durée: {yt.length // 60}min {yt.length % 60}s")
# Filtrage pour ne prendre que l'audio (plus léger)
audio_streams = yt.streams.filter(
only_audio=True,
file_extension='mp4'
).order_by('abr').desc()
if not audio_streams:
print("❌ Aucun stream audio disponible")
return None
audio_stream = audio_streams.first()
print(f"🎵 Qualité audio: {audio_stream.abr}")
# Création du dossier de sortie
Path(output_path).mkdir(parents=True, exist_ok=True)
# Téléchargement effectif
print("⬇️ Téléchargement en cours...")
audio_file = audio_stream.download(
output_path=output_path,
filename=f"{video_id}.mp4"
)
print(f"✅ Fichier téléchargé: {audio_file}\n")
return audio_file
except Exception as e:
print(f"❌ Erreur lors du téléchargement: {type(e).__name__}: {e}")
return None
La Transcription et le Nettoyage
Whisper fait le travail lourd ici. L’option fp16=False évite les erreurs sur les machines sans carte graphique NVIDIA. Le script nettoie aussi automatiquement les fichiers temporaires pour ne pas encombrer votre disque.
def transcribe_audio(self, audio_path, language="fr", task="transcribe"):
"""Transcrit l'audio en texte avec Whisper"""
try:
print("🎙️ Transcription en cours...")
print("⏳ Cela peut prendre quelques minutes selon la durée...\n")
result = self.model.transcribe(
audio_path,
language=language if task == "transcribe" else None,
task=task,
verbose=False,
fp16=False # Compatibilité CPU
)
print("✅ Transcription terminée!\n")
return result["text"], result.get("segments", [])
except Exception as e:
print(f"❌ Erreur lors de la transcription: {e}")
return None, []
def transcribe_youtube_video(self, video_url, language="fr", translate=False):
"""Processus complet : Téléchargement -> Transcription -> Nettoyage"""
print("="*60)
print("DÉBUT DU TRAITEMENT")
print("="*60 + "\n")
# 1. Téléchargement
audio_file = self.download_audio(video_url)
if not audio_file:
return None
# 2. Transcription
task = "translate" if translate else "transcribe"
transcription, segments = self.transcribe_audio(audio_file, language, task)
# 3. Nettoyage
try:
if os.path.exists(audio_file):
os.remove(audio_file)
print("🗑️ Fichier temporaire supprimé")
if os.path.exists("temp") and not os.listdir("temp"):
os.rmdir("temp")
except Exception as e:
print(f"⚠️ Impossible de supprimer les fichiers temporaires: {e}")
if transcription:
return {
"transcription": transcription,
"segments": segments,
"language": language,
"translated": translate
}
return None
3. Le Code Complet (Prêt à l’emploi)
Copiez ce code dans un fichier nommé transcribe.py.
import os
from pytubefix import YouTube # Remplace pytube
import whisper
import re
from pathlib import Path
class YouTubeTranscriber:
def __init__(self, model_size="base"):
"""Initialise le transcripteur avec un modèle Whisper"""
print(f"🔄 Chargement du modèle Whisper: {model_size}")
self.model = whisper.load_model(model_size)
print("✅ Modèle chargé!\n")
def validate_youtube_url(self, url):
"""Valide et normalise l'URL YouTube"""
patterns = [
r'(?:https?://)?(?:www\.)?youtube\.com/watch\?v=([a-zA-Z0-9_-]{11})',
r'(?:https?://)?(?:www\.)?youtu\.be/([a-zA-Z0-9_-]{11})',
r'(?:https?://)?(?:www\.)?youtube\.com/shorts/([a-zA-Z0-9_-]{11})'
]
for pattern in patterns:
match = re.search(pattern, url)
if match:
video_id = match.group(1)
return f"https://www.youtube.com/watch?v={video_id}", video_id
return url, None
def download_audio(self, video_url, output_path="temp"):
"""Télécharge l'audio d'une vidéo YouTube avec gestion d'erreurs améliorée"""
try:
# Validation de l'URL
validated_url, video_id = self.validate_youtube_url(video_url)
if not video_id:
print("❌ URL YouTube invalide")
return None
print(f"🔗 URL validée: {validated_url}")
# Création de l'objet YouTube
yt = YouTube(
validated_url,
use_oauth=False,
allow_oauth_cache=False
)
print(f"📹 Titre: {yt.title}")
print(f"👤 Auteur: {yt.author}")
print(f"⏱️ Durée: {yt.length // 60}min {yt.length % 60}s")
# Filtrage des streams audio
audio_streams = yt.streams.filter(
only_audio=True,
file_extension='mp4'
).order_by('abr').desc()
if not audio_streams:
print("❌ Aucun stream audio disponible")
return None
audio_stream = audio_streams.first()
print(f"🎵 Qualité audio: {audio_stream.abr}")
# Création du dossier de sortie
Path(output_path).mkdir(parents=True, exist_ok=True)
# Téléchargement
print("⬇️ Téléchargement en cours...")
audio_file = audio_stream.download(
output_path=output_path,
filename=f"{video_id}.mp4"
)
print(f"✅ Fichier téléchargé: {audio_file}\n")
return audio_file
except Exception as e:
print(f"❌ Erreur lors du téléchargement: {type(e).__name__}: {e}")
return None
def transcribe_audio(self, audio_path, language="fr", task="transcribe"):
"""Transcrit l'audio en texte avec Whisper"""
try:
print("🎙️ Transcription en cours...")
print("⏳ Cela peut prendre quelques minutes selon la durée...\n")
result = self.model.transcribe(
audio_path,
language=language if task == "transcribe" else None,
task=task,
verbose=False,
fp16=False # Compatibilité CPU
)
print("✅ Transcription terminée!\n")
return result["text"], result.get("segments", [])
except Exception as e:
print(f"❌ Erreur lors de la transcription: {e}")
return None, []
def transcribe_youtube_video(self, video_url, language="fr", translate=False):
"""Processus complet de transcription"""
print("="*60)
print("DÉBUT DU TRAITEMENT")
print("="*60 + "\n")
# Téléchargement
audio_file = self.download_audio(video_url)
if not audio_file:
return None
# Transcription
task = "translate" if translate else "transcribe"
transcription, segments = self.transcribe_audio(audio_file, language, task)
# Nettoyage des fichiers temporaires
try:
if os.path.exists(audio_file):
os.remove(audio_file)
print("🗑️ Fichier temporaire supprimé")
if os.path.exists("temp") and not os.listdir("temp"):
os.rmdir("temp")
except Exception as e:
print(f"⚠️ Impossible de supprimer les fichiers temporaires: {e}")
if transcription:
return {
"transcription": transcription,
"segments": segments,
"language": language,
"translated": translate
}
return None
def main():
print("\n" + "="*60)
print("🎬 YOUTUBE TRANSCRIBER AVEC WHISPER")
print("="*60 + "\n")
# Choix du modèle
print("Modèles disponibles:")
print(" tiny - Rapide, moins précis (39M)")
print(" base - Équilibré (74M) [RECOMMANDÉ]")
print(" small - Plus précis (244M)")
print(" medium - Très précis (769M)")
print(" large - Maximum de précision (1550M)")
model_choice = input("\nModèle à utiliser (défaut: base): ").strip().lower() or "base"
# Initialisation
transcriber = YouTubeTranscriber(model_choice)
# URL de la vidéo
video_url = input("📎 Entrez l'URL de la vidéo YouTube: ").strip()
if not video_url:
print("❌ URL invalide")
return
# Langue
print("\n🌍 Langues disponibles:")
languages = {
"fr": "Français",
"en": "Anglais",
"es": "Espagnol",
"de": "Allemand",
"it": "Italien",
"pt": "Portugais",
"ar": "Arabe"
}
for code, name in languages.items():
print(f" {code} - {name}")
language = input("\nCode langue (défaut: fr): ").strip() or "fr"
# Option de traduction
translate_option = input("🌐 Traduire en anglais? (o/n, défaut: n): ").strip().lower()
translate = translate_option == 'o'
# Traitement
result = transcriber.transcribe_youtube_video(video_url, language, translate)
if result:
print("\n" + "="*60)
print("📝 RÉSULTAT DE LA TRANSCRIPTION")
print("="*60 + "\n")
if translate:
print("🌐 Texte traduit en anglais:\n")
else:
print(f"🗣️ Texte en {language}:\n")
print(result["transcription"])
print("\n" + "="*60)
# Sauvegarde
save_option = input("\n💾 Sauvegarder dans un fichier? (o/n): ").strip().lower()
if save_option == 'o':
filename = f"transcription_{language}_{result.get('translated', False)}.txt"
try:
with open(filename, 'w', encoding='utf-8') as f:
f.write(f"Transcription YouTube\n")
f.write(f"Langue: {language}\n")
f.write(f"Traduit: {'Oui' if translate else 'Non'}\n")
f.write("="*60 + "\n\n")
f.write(result["transcription"])
print(f"✅ Fichier sauvegardé: {filename}")
except Exception as e:
print(f"❌ Erreur lors de la sauvegarde: {e}")
else:
print("\n" + "="*60)
print("❌ ÉCHEC DE LA TRANSCRIPTION")
print("="*60)
print("\n💡 Conseils:")
print(" 1. Vérifiez l'URL de la vidéo")
print(" 2. Testez avec une autre vidéo")
print(" 3. Vérifiez votre connexion Internet")
print(" 4. La vidéo peut être privée ou restreinte")
print(" 5. Essayez de mettre à jour: pip install --upgrade pytubefix")
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\n\n⚠️ Programme interrompu par l'utilisateur")
except Exception as e:
print(f"\n❌ Erreur inattendue: {e}")
Le Potentiel de l’Automatisation
Vous disposez désormais d’un outil de transcription robuste, capable de rivaliser avec de nombreuses solutions payantes du marché. Ce script n’est qu’une première étape : libre à vous de l’étendre pour traiter des playlists entières, générer automatiquement des fichiers de sous-titres .srt parfaitement synchronisés, ou même connecter le texte généré à un résumé automatique via une autre IA.
L’alliance de bibliothèques open-source comme pytubefix et de modèles performants comme Whisper rend ces technologies accessibles à tous. C’est la preuve qu’avec quelques lignes de Python, il est possible d’automatiser des tâches chronophages et de gagner un temps précieux. À vous de jouer pour adapter ce code et l’intégrer dans vos projets les plus créatifs !
