File size: 8,104 Bytes
f98bbe8 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 |
#!/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() |