Whisper IA

Automatiser la transcription audio et vidéo avec Whisper, Nextcloud, et du Python

Dans cet article, je vais vous guider à travers la configuration et l’utilisation du modèle de transcription Whisper d’OpenAI sur un système Windows 11. Vous découvrirez comment j’ai automatisé la transcription de fichiers audio et de vidéos YouTube via un serveur domestique Nextcloud, avec une gestion simple et fluide des fichiers à partir d’un smartphone. Ce guide couvre toutes les étapes, de l’installation des dépendances à l’automatisation complète de la transcription, en incluant une solution pour contourner les restrictions YouTube.

Exemple d’utilisation

J’enregistre les parties importantes des cours de fac et prends des notes écrites pour le reste. Sur le trajet, je relis mes notes et en fais un fichier audio. Toujours sur mon smartphone, je déplace les fichiers audio dont je veux une retranscription dans un répertoire qui est sur mon cloud privé Nextcloud. Je prends soin de nommer les fichiers explicitement.

Nextcloud, l’application sur mon smartphone, synchronise le dossier et fait une copie des fichiers audio sur mon serveur. Si je veux une retranscription d’un documentaire ou d’une conférence sur YouTube, je copie l’adresse de la vidéo dans un fichier .txt que je déplace dans un autre répertoire de mon cloud prévu uniquement pour ça. Je nomme également bien ces fichiers.

Mon serveur scanne les deux répertoires. Si le script détecte des fichiers, il les transcrit et dépose la transcription sous forme de fichiers .txt et .docx dans un troisième répertoire, également dans le cloud. Je peux ainsi récupérer facilement les textes de retranscription.

Suite à cela je peu retravailler mes cours de façon automatisé avec une autre IA tel que ChatGPT, mais choisissez Français « MISTRAL » c’est bien mieux que le voleur de Bill Gates.

Configuration matérielle utilisée :

  • Système d’exploitation : Windows 11
  • GPU : Nvidia 4060 Ti 16 Go VRAM
  • CPU : AMD Ryzen 7
  • RAM : 64 Go DDR4 3600 MHz
  • Stockage : SSD NVMe

1. Préparation de l’environnement

1.1. Installation de Python

Si vous ne l’avez pas déjà fait, installez Python sur votre machine. Lors de l’installation, veillez à cocher l’option « Add Python to PATH ».

1.2. Création d’un environnement virtuel

Créez un environnement virtuel Python pour isoler les dépendances de votre projet. Voici la commande pour créer un environnement :

Bash
python -m venv whisper_env
Bash

Activez l’environnement virtuel :

Bash
whisper_env\Scripts\activate
Bash

1.3. Installation des dépendances

Pour que le script fonctionne, installez les bibliothèques suivantes dans votre environnement virtuel :

  • Whisper : Le modèle de transcription d’OpenAI.
Bash
pip install git+https://github.com/openai/whisper.git
Bash
  • PyTorch (avec CUDA pour l’accélération GPU)
Bash
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
Bash
  • python-docx : Pour convertir les fichiers .txt en .docx.
Bash
pip install python-docx
Bash
  • yt-dlp : Pour télécharger des vidéos YouTube et en extraire l’audio.
Bash
pip install yt-dlp
Bash
  • ffmpeg-python : Une interface Python pour manipuler les fichiers audio et vidéo via FFmpeg.
Bash
pip install ffmpeg-python
Bash
  • pydub : Une bibliothèque pour manipuler des fichiers audio. Ici pour les transcrire en plusieurs parties si la durée est longue afin de ne pas surcharger le processus.
Bash
pip install pydub
Bash

1.4. Installation de FFmpeg

FFmpeg est requis pour la manipulation des fichiers audio et vidéo. Il doit être installé en dehors de Python. Voici comment procéder :

  1. Téléchargez FFmpeg depuis le site officiel.
  2. Décompressez l’archive.
  3. Ajoutez FFmpeg au PATH de votre système pour qu’il soit accessible partout.

2. Contourner les restrictions YouTube avec les cookies

Certaines vidéos YouTube nécessitent une authentification ou imposent des restrictions. Pour contourner cela, nous allons utiliser un fichier de cookies. Cette solution à une faiblesse c’est qu’il faut régulièrement rafraichir le fichier en réenregistrant les cookies.

2.1. Exporter les cookies depuis votre navigateur

  1. Installez une extension comme cookies.txt.
  2. Connectez vous à votre compte YouTube dans votre navigateur.
  3. Exportez les cookies et sauvegardez le fichier sous le nom cookies.txt.

2.2. Où sauvegarder le fichier cookies.txt ?

Placez le fichier cookies.txt :

  • Dans le même répertoire que votre script Python.
  • Vérifiez que le chemin dans le script correspond bien à l’emplacement du fichier :pythonCopier le code
Python
ydl_opts = {
    'cookiefile': 'cookies.txt',  # Chemin vers le fichier cookies.txt
}
Python

3. Démarrage automatique de Whisper au lancement de Windows

Pour rendre le processus encore plus simple et transparent, j’ai configuré le démarrage automatique de Whisper au lancement de mon PC. Cela me permet de lancer Whisper et mon script de transcription sans intervention manuelle dès que mon ordinateur démarre.

3.1. Contenu du fichier .bat

Bash
@echo off
timeout /t 30

REM Activer l'environnement virtuel
echo Activating virtual environment >> F:\IA\whisper\start_log.txt
call F:\IA\whisper\venv\Scripts\activate.bat >> F:\IA\whisper\start_log.txt 2>&1

REM Se déplacer dans le répertoire du script
echo Changing directory to F:\IA\whisper >> F:\IA\whisper\start_log.txt
cd /d F:\IA\whisper >> F:\IA\whisper\start_log.txt 2>&1

REM Lancer le script Python
echo Starting transcription script >> F:\IA\whisper\start_log.txt
start /B python transcription_script.py >> F:\IA\whisper\script_log.txt 2>&1

REM Désactiver l'environnement virtuel
echo Deactivating virtual environment >> F:\IA\whisper\start_log.txt
deactivate
Bash

3.2. Ajouter le fichier .bat au démarrage de Windows

  1. Sauvegardez le fichier sous le nom start_whisper.bat.
  2. Placez un raccourci vers ce fichier dans le dossier Démarrage de Windows :
    • Appuyez sur Win + R, tapez shell:startup, validez.
    • Collez un raccourci vers votre fichier .bat.

4. Automatisation de la transcription avec Nextcloud

4.1. Gestion des fichiers via Nextcloud

J’ai mis en place un serveur Nextcloud à domicile avec un nom de domaine et un reverse proxy. Cette configuration me permet de gérer mes fichiers à distance via l’application Nextcloud sur mon smartphone.

  1. Pour les fichiers audio :
    • J’enregistre des fichiers audio sur mon smartphone et je les copie dans un répertoire Nextcloud dédié à la transcription.
    • Une fois le fichier synchronisé avec mon serveur, un script Python effectue la transcription.
  2. Pour les vidéos YouTube :
    • Je crée un fichier texte contenant l’URL de la vidéo que je souhaite transcrire. Ce fichier .txt est nommé en fonction du contenu de la vidéo.
    • Une fois ce fichier synchronisé, le script télécharge la vidéo, en extrait l’audio et génère la transcription.

5. script de transcription

Le script de transcription est au cœur de ce processus d’automatisation. Il gère la détection des fichiers audio ou des liens YouTube dans les répertoires surveillés, effectue la transcription via Whisper, et enregistre les résultats.

Python
import os
import time
import whisper
import torch
from docx import Document
import yt_dlp
from datetime import datetime
import threading
from pydub import AudioSegment

# Configuration des chemins et verrou
audio_folder = r"\\OMV\whisper\trans_audio"
text_folder = r"\\OMV\whisper\trans_texte"
video_folder = r"\\OMV\whisper\trans_video"
lock_file = os.path.join(audio_folder, "transcription.lock")
log_file = "F:\\IA\\whisper\\script_log.txt"

# Extensions audio supportées
audio_extensions = [".m4a", ".mp3", ".wav", ".flac"]

# Modèle Whisper
model_name = "large"  # Utiliser le modèle le plus précis
device = "cuda" if torch.cuda.is_available() else "cpu"
model = whisper.load_model(model_name, device=device)

# Verrou global pour la gestion des logs
log_lock = threading.Lock()

# Déclaration du fichier de log pour start_log
start_log_file = "F:\\IA\\whisper\\start_log.txt"

def log_with_timestamp(message, log_file):
    """Ajoute un message avec date et heure dans un fichier de log."""
    try:
        # Vérifie si le message est une chaîne valide
        if not isinstance(message, str):
            raise ValueError("Le message à journaliser doit être une chaîne de caractères.")

        # Vérifie si le dossier contenant le fichier log existe, sinon le crée
        log_dir = os.path.dirname(log_file)
        if log_dir and not os.path.exists(log_dir):
            os.makedirs(log_dir, exist_ok=True)  # Création récursive du dossier

        # Génère l'horodatage et écrit dans le fichier log
        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        with open(log_file, "a", encoding="utf-8") as f:
            f.write(f"[{timestamp}] {message}\n")

    except PermissionError:
        # Si les permissions posent problème
        print(f"Erreur : Impossible d'écrire dans le fichier de log : {log_file}. Vérifiez les permissions.")
    except Exception as e:
        # Si une autre erreur survient
        print(f"Erreur inattendue lors de l'écriture dans le fichier de log : {e}")

# Exemple de fonction principale où vous écrivez dans les deux fichiers de log
if __name__ == "__main__":
    # Écrit dans script_log.txt
    log_with_timestamp("Ce message va dans script_log.txt", log_file)

    # Écrit dans start_log.txt
    log_with_timestamp("Ce message va dans start_log.txt", start_log_file)

    # Un autre exemple
    log_with_timestamp("Démarrage du processus...", start_log_file)
    log_with_timestamp("Fin du script.", log_file)

# Fonction pour segmenter l'audio en parties plus courtes
def segment_audio(file_path, segment_length=60000):
    audio = AudioSegment.from_file(file_path)
    segments = []
    start = 0
    end = segment_length
    while start < len(audio):
        segment = audio[start:end]
        segments.append(segment)
        start = end
        end += segment_length
    return segments

# Fonction pour transcrire un fichier audio
def transcribe_audio(file_path):
    log_with_timestamp(f"Transcribing {file_path}...", log_file)
    segments = segment_audio(file_path)
    transcription = ""
    for i, segment in enumerate(segments):
        segment_path = f"{file_path}_segment_{i}.wav"
        segment.export(segment_path, format="wav")
        result = model.transcribe(segment_path, language='fr', task='transcribe', beam_size=5, best_of=5)
        transcription += result["text"] + " "
        os.remove(segment_path)  # Supprimer le segment temporaire
    log_with_timestamp(f"Transcription result: {transcription}", log_file)
    return transcription

# Fonction pour convertir un fichier texte en fichier docx
def txt_to_docx(txt_path, docx_path):
    log_with_timestamp(f"Converting {txt_path} to {docx_path}...", log_file)
    doc = Document()
    with open(txt_path, "r", encoding="utf-8") as f:
        doc.add_paragraph(f.read())
    doc.save(docx_path)

# Fonction pour télécharger une vidéo YouTube
def download_youtube_video(video_url, output_dir, output_file_base):
    ydl_opts = {
        'format': 'bestaudio/best',
        'outtmpl': os.path.join(output_dir, output_file_base + '.webm'),
        'postprocessors': [{
            'key': 'FFmpegExtractAudio',
            'preferredcodec': 'mp3',
            'preferredquality': '192',
        }],
        'cookiefile': 'cookies.txt',  # Utilisation des cookies pour éviter les restrictions
        'age_restricted': True,  # Gérer les vidéos avec restrictions d'âge
        'retries': 10,  # Nombre de tentatives de téléchargement
        'retry_sleep_functions': {
            'sleep': lambda x: time.sleep(x),
            'sleep_time': 30,  # Temps d'attente entre les tentatives
        },
        # 'proxy': 'http://your_proxy_here:port',  # Utilisation d'un proxy (facultatif)
    }
    try:
        with yt_dlp.YoutubeDL(ydl_opts) as ydl:
            info_dict = ydl.extract_info(video_url, download=True)
            audio_file = os.path.join(output_dir, output_file_base + '.mp3')
            video_title = info_dict.get("title", "Untitled")
            log_with_timestamp(f"Downloaded and converted: {audio_file}", log_file)
            return audio_file, video_title
    except Exception as e:
        log_with_timestamp(f"Error downloading video {video_url}: {e}", log_file)
        return None, None

# Fonction principale pour scanner les dossiers et traiter les fichiers
def scan_and_process():
    log_with_timestamp("Starting the process...", log_file)
    while True:
        # Vérification et création du fichier de verrouillage
        if os.path.exists(lock_file):
            log_with_timestamp("Transcription en cours. Attente de 5 minutes avant de scanner à nouveau...", log_file)
            time.sleep(300)
            continue

        # Vérifier si un fichier d'arrêt existe
        stop_file = os.path.join(audio_folder, "stop.txt")
        if os.path.exists(stop_file):
            log_with_timestamp("Arrêt détecté. Sortie du script...", log_file)
            os.rename(stop_file, os.path.join(audio_folder, "no_stop.txt"))
            break

        # Création du fichier de verrouillage
        try:
            with open(lock_file, "w") as lock:
                lock.write("locked")
            log_with_timestamp("Lock file created. Starting processing.", log_file)
        except Exception as e:
            log_with_timestamp(f"Erreur : Impossible de créer le fichier de verrouillage. {e}", log_file)
            return

        try:
            # Scanner et traiter les fichiers audio
            for file_name in os.listdir(audio_folder):
                if any(file_name.endswith(ext) for ext in audio_extensions):
                    audio_path = os.path.join(audio_folder, file_name)
                    txt_file_name = os.path.splitext(file_name)[0] + ".txt"
                    txt_path = os.path.join(text_folder, txt_file_name)

                    # Vérifier si la transcription existe déjà
                    if not os.path.exists(txt_path):
                        text = transcribe_audio(audio_path)
                        with open(txt_path, "w", encoding="utf-8") as txt_file:
                            txt_file.write(text)
                        log_with_timestamp(f"Saved transcription to {txt_path}", log_file)

                        # Convertir en .docx
                        docx_file_name = txt_file_name.replace(".txt", ".docx")
                        docx_path = os.path.join(text_folder, docx_file_name)
                        txt_to_docx(txt_path, docx_path)

            # Scanner et traiter les fichiers texte contenant des liens YouTube
            log_with_timestamp(f"Scanning folder: {video_folder}", log_file)
            try:
                for file_name in os.listdir(video_folder):
                    log_with_timestamp(f"Found file: {file_name}", log_file)
                    if file_name.endswith(".txt"):
                        link_path = os.path.join(video_folder, file_name)
                        with open(link_path, "r") as link_file:
                            video_url = link_file.readline().strip()
                            log_with_timestamp(f"Read video URL: {video_url}", log_file)

                        base_name = os.path.splitext(file_name)[0]
                        txt_file_name = base_name + ".txt"
                        txt_path = os.path.join(text_folder, txt_file_name)

                        if not os.path.exists(txt_path):
                            audio_path, video_title = download_youtube_video(video_url, audio_folder, base_name)
                            if audio_path:
                                text = transcribe_audio(audio_path)
                                transcription_with_metadata = f"{video_title}\n{video_url}\n\n{text}"
                                with open(txt_path, "w", encoding="utf-8") as txt_file:
                                    txt_file.write(transcription_with_metadata)
                                log_with_timestamp(f"Saved transcription to {txt_path}", log_file)

                                # Convertir en .docx
                                docx_file_name = txt_file_name.replace(".txt", ".docx")
                                docx_path = os.path.join(text_folder, docx_file_name)
                                txt_to_docx(txt_path, docx_path)

            except Exception as e:
                log_with_timestamp(f"Erreur : {e}. Nouvelle tentative dans 2 minutes...", log_file)
                time.sleep(120)

        finally:
            if os.path.exists(lock_file):
                os.remove(lock_file)
                log_with_timestamp("Lock file removed. Waiting for next scan.", log_file)
            else:
                log_with_timestamp("Lock file does not exist. Waiting for next scan.", log_file)

        # Pause avant le prochain scan
        log_with_timestamp("Attente de 5 minutes avant le prochain scan...", log_file)
        time.sleep(300)

# Point d'entrée du script
if __name__ == "__main__":
    scan_and_process()
Python

5.1. Étapes clés du script :

Le script de transcription est au cœur de ce processus d’automatisation. Voici les étapes clés mises à jour avec la gestion des restrictions YouTube :

  1. Surveillance des répertoires Nextcloud :
    • Le script surveille en continu deux répertoires spécifiques sur votre Nextcloud :
      • Un répertoire pour les fichiers audio à transcrire.
      • Un répertoire pour les fichiers .txt contenant des URL YouTube.
  2. Téléchargement des vidéos YouTube avec yt-dlp corrigé :
    • Si un fichier .txt contenant une URL est détecté, le script :
      1. Télécharge la vidéo via yt-dlp.
      2. Extrait uniquement l’audio au format .mp3 pour économiser du stockage.
      3. Contourne les restrictions YouTube grâce à l’utilisation d’un fichier de cookies (cookies.txt).
  3. Transcription automatique des fichiers audio avec Whisper :
    • Une fois l’audio récupéré (depuis une vidéo YouTube ou directement depuis un fichier audio), le script :
      1. Transcrit le contenu en texte brut grâce au modèle Whisper.
      2. Sauvegarde la transcription au format .txt.
  4. Conversion du texte en .docx :
    • Le script convertit chaque fichier .txt en un fichier Word .docx pour une meilleure lisibilité et compatibilité.
  5. Gestion des logs et des erreurs :
    • Chaque étape est consignée dans un fichier de log.
    • Les erreurs comme les restrictions YouTube (par exemple : « Sign in to confirm you’re not a bot ») sont gérées de manière robuste pour éviter que le script ne plante.
  6. Nettoyage des fichiers temporaires :
    • Les fichiers audio ou vidéos temporaires (comme les .webm issus de YouTube) sont supprimés automatiquement après transcription.
  7. Stockage des résultats :
    • Les fichiers .txt et .docx finaux sont déplacés dans un troisième répertoire Nextcloud, prêt à être synchronisé sur votre smartphone ou autre appareil.