BOB / transcribe_audio.py
vic3610's picture
Upload 2 files
f98bbe8 verified
#!/usr/bin/env python3
"""
Script de transcription audio avec Whisper
Traite tous les fichiers audio du dossier input et génère les transcriptions dans output/transcriptions
"""
import os
import whisper
from pathlib import Path
import time
from datetime import datetime
# Bootstrap environnement portable
try:
from portable_env import setup_portable_env
setup_portable_env()
except Exception:
pass
# Ajouter FFmpeg au PATH si nécessaire
ffmpeg_paths = [
r"C:\FFmpeg\bin",
r"C:\Program Files\FFmpeg\bin",
r"C:\Users\victo\AppData\Local\Microsoft\WinGet\Packages\Gyan.FFmpeg_Microsoft.Winget.Source_8wekyb3d8bbwe\ffmpeg-8.0-full_build\bin"
]
for ffmpeg_path in ffmpeg_paths:
if os.path.exists(ffmpeg_path) and ffmpeg_path not in os.environ.get("PATH", ""):
os.environ["PATH"] = ffmpeg_path + ";" + os.environ.get("PATH", "")
# FFmpeg: déjà géré via portable_env (vendor/ffmpeg/bin)
# Configuration
INPUT_DIR = Path(os.environ.get("BOB_INPUT_DIR", Path(__file__).parent.parent / "input"))
OUTPUT_DIR = Path(os.environ.get("BOB_TRANSCRIPTIONS_DIR", Path(__file__).parent.parent / "output" / "transcriptions"))
WHISPER_MODEL = os.environ.get("WHISPER_MODEL", "small") # peut être override par GUI
SUPPORTED_FORMATS = ['.mp3', '.wav', '.m4a', '.flac', '.ogg', '.mp4', '.avi', '.mov']
def load_whisper_model(model_name):
"""Charge un modèle de transcription.
- 'faster-whisper:small|medium|large-v3|...' utilise faster_whisper.WhisperModel
- sinon utilise openai whisper.load_model
Retourne un tuple (backend, model)
backend in {"openai", "faster"}
"""
print(f"Chargement du modèle Whisper: {model_name}")
if isinstance(model_name, str) and model_name.startswith("faster-whisper"):
# Extraire la taille après le ':'
size = model_name.split(":", 1)[1] if ":" in model_name else "small"
try:
from faster_whisper import WhisperModel
except Exception as e:
raise RuntimeError(f"faster-whisper non disponible: {e}")
# Le téléchargement ira dans HF_HOME/HUGGINGFACE cache local
compute_type = "int8" # CPU-friendly par défaut; peut être ajusté
model = WhisperModel(size, device="cpu", compute_type=compute_type)
print("Modèle faster-whisper prêt.")
return ("faster", model)
else:
model = whisper.load_model(model_name)
print("Modèle OpenAI Whisper prêt.")
return ("openai", model)
def get_audio_files(input_dir):
"""Récupère tous les fichiers audio du dossier input"""
audio_files = []
if not input_dir.exists():
return audio_files
# Récupérer tous les fichiers du dossier
for file in input_dir.iterdir():
if file.is_file():
# Vérifier l'extension (case insensitive)
file_ext = file.suffix.lower()
if file_ext in [ext.lower() for ext in SUPPORTED_FORMATS]:
audio_files.append(file)
return sorted(list(set(audio_files))) # Éliminer les doublons
def transcribe_file(model, audio_file, output_dir):
"""Transcrit un fichier audio et sauvegarde le résultat"""
print(f"Transcription de: {audio_file.name}")
print(f"Chemin complet: {audio_file.absolute()}")
print(f"Fichier existe: {audio_file.exists()}")
print(f"Taille du fichier: {audio_file.stat().st_size if audio_file.exists() else 'N/A'} bytes")
try:
# Vérifier que le fichier existe
if not audio_file.exists():
print(f"✗ Fichier introuvable: {audio_file}")
return False
# Transcription avec Whisper - utiliser le chemin sans espaces si possible
audio_path = str(audio_file.absolute())
print(f"Chemin utilisé pour Whisper: {audio_path}")
start_time = time.time()
# Support des deux backends
if isinstance(model, tuple) and model and model[0] in ("openai", "faster"):
backend, engine = model
else:
# Rétrocompatibilité: ancien code passait juste l'objet
backend, engine = ("openai", model)
if backend == "openai":
result = engine.transcribe(audio_path, language="fr", verbose=False)
text = result["text"]
else:
# faster-whisper retourne des segments + info
segments, info = engine.transcribe(audio_path, language="fr")
pieces = []
for seg in segments:
pieces.append(seg.text)
text = " ".join(pieces).strip()
end_time = time.time()
# Nom du fichier de sortie
output_filename = audio_file.stem + "_transcription.txt"
output_path = output_dir / output_filename
# Sauvegarde de la transcription
with open(output_path, 'w', encoding='utf-8') as f:
f.write(f"Fichier source: {audio_file.name}\n")
f.write(f"Date de transcription: {datetime.now().strftime('%d/%m/%Y %H:%M:%S')}\n")
f.write(f"Durée de traitement: {end_time - start_time:.2f} secondes\n")
f.write(f"Modèle utilisé: {WHISPER_MODEL}\n")
f.write("-" * 50 + "\n\n")
f.write(text)
print(f"✓ Transcription sauvegardée: {output_filename}")
print(f" Durée de traitement: {end_time - start_time:.2f}s")
return True
except Exception as e:
print(f"✗ Erreur lors de la transcription de {audio_file.name}: {e}")
print(f"Type d'erreur: {type(e).__name__}")
import traceback
traceback.print_exc()
return False
def main():
"""Fonction principale"""
print("=" * 60)
print("TRANSCRIPTION AUTOMATIQUE DES BOB")
print("=" * 60)
# Vérification des dossiers - chemins absolus
script_dir = Path(__file__).parent.absolute()
input_dir = Path(os.environ.get("BOB_INPUT_DIR", script_dir.parent / "input"))
output_dir = Path(os.environ.get("BOB_TRANSCRIPTIONS_DIR", script_dir.parent / "output" / "transcriptions"))
print(f"Dossier script: {script_dir}")
print(f"Dossier input: {input_dir}")
print(f"Dossier output: {output_dir}")
print()
if not input_dir.exists():
print(f"Erreur: Le dossier input n'existe pas: {input_dir}")
return
# Création du dossier de sortie si nécessaire
output_dir.mkdir(parents=True, exist_ok=True)
# Recherche des fichiers audio
audio_files = get_audio_files(input_dir)
if not audio_files:
print(f"Aucun fichier audio trouvé dans {input_dir}")
print(f"Formats supportés: {', '.join(SUPPORTED_FORMATS)}")
return
print(f"Trouvé {len(audio_files)} fichier(s) audio à traiter:")
for i, file in enumerate(audio_files, 1):
print(f" {i}. {file.name}")
print()
# Chargement du modèle Whisper
try:
model = load_whisper_model(WHISPER_MODEL)
except Exception as e:
print(f"Erreur lors du chargement du modèle: {e}")
return
print()
# Traitement des fichiers
success_count = 0
total_start_time = time.time()
for i, audio_file in enumerate(audio_files, 1):
print(f"[{i}/{len(audio_files)}] ", end="")
if transcribe_file(model, audio_file, output_dir):
success_count += 1
print()
total_end_time = time.time()
# Résumé
print("=" * 60)
print("RÉSUMÉ")
print("=" * 60)
print(f"Fichiers traités: {len(audio_files)}")
print(f"Réussites: {success_count}")
print(f"Échecs: {len(audio_files) - success_count}")
print(f"Durée totale: {total_end_time - total_start_time:.2f} secondes")
print(f"Transcriptions sauvegardées dans: {output_dir}")
if __name__ == "__main__":
main()