Spaces:
Sleeping
Sleeping
File size: 7,681 Bytes
a74c3ba a769e6b 7b676ff a74c3ba 1a099fd 7162bee c921046 b535d53 c921046 ae7bb8d a74c3ba a8ecd5f 075f0bd a8ecd5f 1a099fd a8ecd5f 7ef7a21 075f0bd a74c3ba ec37ecb a74c3ba 1a099fd a74c3ba 075f0bd a74c3ba 7162bee a342efa 3f8404b dbbf7fc 3f8404b a342efa dbbf7fc 5d5b433 a74c3ba f59212e dbbf7fc a48e3f0 f59212e dbbf7fc f59212e dbbf7fc a48e3f0 f59212e 6e05e25 a342efa 6e05e25 dbbf7fc 6e05e25 dbbf7fc 6e05e25 a48e3f0 dbbf7fc ec37ecb 075f0bd dbbf7fc 075f0bd dbbf7fc 7b676ff 4a563dc 7b676ff dbbf7fc 7b676ff 226e3bb 7b676ff dbbf7fc e1d062e dbbf7fc e1d062e 3f8404b a342efa 6e05e25 a342efa 7162bee a342efa 90c7625 a342efa 6e05e25 7162bee d0b0179 6e05e25 3f8404b 6e05e25 dd03eda 6e05e25 c921046 6e05e25 90c7625 72cecc5 6e05e25 7162bee 6e05e25 90c7625 b535d53 075f0bd 7162bee a8ecd5f eb72b93 a8ecd5f 9023c36 6e05e25 dbbf7fc a342efa 5a0569c 7b676ff 7162bee 6e05e25 a8ecd5f 7162bee 6e05e25 7162bee 6e05e25 f86f536 90c7625 6e05e25 |
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 |
import os
import re
import spaces
import shutil
import zipfile
import torch
import numpy as np
import pandas as pd
from pathlib import Path
import gradio as gr
from pydub import AudioSegment
from transformers import pipeline
# -------------------------------------------------
# 1. Configuration et Initialisation
# -------------------------------------------------
MODEL_NAME = "openai/whisper-large-v3"
device = "cuda" if torch.cuda.is_available() else "cpu"
# Initialisation du modèle Whisper
pipe = pipeline(
task="automatic-speech-recognition",
model=MODEL_NAME,
device=device,
model_kwargs={"low_cpu_mem_usage": True},
)
# Création du répertoire temporaire pour stocker les extraits audio
TEMP_DIR = "./temp_audio"
os.makedirs(TEMP_DIR, exist_ok=True)
def init_metadata_state():
return []
# -------------------------------------------------
# 2. Transcription de l'audio avec Whisper (Timestamps de fin + Marge de Sécurité)
# -------------------------------------------------
def correct_typography(text):
text = re.sub(r"\b([lLdDmMcCjJnNsStT]) ['’] (\w)", r"\1'\2", text) # Corrige les espaces autour des apostrophes
return text
@spaces.GPU(duration=120)
def transcribe_audio(audio_path):
if not audio_path:
print("[LOG] Aucun fichier audio fourni.")
return "Aucun fichier audio fourni", None, [], ""
print(f"[LOG] Début de la transcription de {audio_path}...")
result = pipe(audio_path, return_timestamps="word")
words = result.get("chunks", [])
if not words:
print("[LOG ERROR] Erreur : Aucun timestamp détecté.")
return "Erreur : Aucun timestamp détecté.", None, [], ""
raw_transcription = " ".join([w["text"] for w in words])
# 🔄 Correction typographique AVANT affichage
raw_transcription = correct_typography(raw_transcription)
# 🔄 Ajout des timestamps de fin avec marge de sécurité
MARGIN = 0.06 # 60ms
word_timestamps = []
for i, w in enumerate(words):
start_time = w["timestamp"][0]
end_time = w["timestamp"][1] if w["timestamp"][1] is not None else start_time + 0.5
# Vérifier qu'on ne dépasse pas le début du mot suivant
if i < len(words) - 1:
next_start_time = words[i + 1]["timestamp"][0]
end_time = min(end_time + MARGIN, next_start_time - 0.01) # On laisse 10ms de sécurité
word_timestamps.append((w["text"], start_time, end_time))
transcription_with_timestamps = " ".join([f"{w[0]}[{w[1]:.2f}-{w[2]:.2f}]" for w in word_timestamps])
print(f"[LOG] Transcription brute corrigée : {raw_transcription}")
return raw_transcription, word_timestamps, transcription_with_timestamps, audio_path
# -------------------------------------------------
# 3. Enregistrement des segments définis par l'utilisateur (Affichage sur Interface)
# -------------------------------------------------
def save_segments(table_data):
print("[LOG] Enregistrement des segments définis par l'utilisateur...")
formatted_data = []
confirmation_message = "**📌 Segments enregistrés :**\n"
for i, row in table_data.iterrows():
text, start_time, end_time = row["Texte"], row["Début (s)"], row["Fin (s)"]
segment_id = f"seg_{i+1:02d}"
try:
start_time = str(start_time).replace(",", ".")
end_time = str(end_time).replace(",", ".")
if not start_time.replace(".", "").isdigit() or not end_time.replace(".", "").isdigit():
raise ValueError("Valeurs de timestamps invalides")
start_time = float(start_time)
end_time = float(end_time)
if start_time < 0 or end_time <= start_time:
raise ValueError("Valeurs incohérentes")
formatted_data.append([text, start_time, end_time, segment_id])
log_message = f"- `{segment_id}` | **Texte** : {text} | ⏱ **{start_time:.2f}s - {end_time:.2f}s**"
confirmation_message += log_message + "\n"
print(f"[LOG] {log_message}")
except ValueError as e:
print(f"[LOG ERROR] Erreur de conversion des timestamps : {e}")
return pd.DataFrame(), "❌ **Erreur** : Vérifiez que les valeurs sont bien des nombres valides."
return pd.DataFrame(formatted_data, columns=["Texte", "Début (s)", "Fin (s)", "ID"]), confirmation_message
# -------------------------------------------------
# 4. Génération du fichier ZIP
# -------------------------------------------------
def generate_zip(metadata_state, audio_path, zip_name):
if isinstance(metadata_state, tuple):
metadata_state = metadata_state[0] # Extraire le DataFrame si c'est un tuple
if metadata_state is None or metadata_state.empty:
print("[LOG ERROR] Aucun segment valide trouvé pour la génération du ZIP.")
return None
zip_folder_name = f"{zip_name}_dataset"
zip_path = os.path.join(TEMP_DIR, f"{zip_folder_name}.zip")
if os.path.exists(zip_path):
os.remove(zip_path)
metadata_csv_path = os.path.join(TEMP_DIR, f"{zip_name}_metadata.csv")
# Assurer que les ID et fichiers audio correspondent
metadata_state["ID"] = [f"{zip_name}_seg_{i+1:02d}" for i in range(len(metadata_state))]
# Ajouter une colonne "Commentaires" vide
metadata_state["Commentaires"] = ""
# Réorganiser l’ordre des colonnes
metadata_state = metadata_state[["ID", "Texte", "Début (s)", "Fin (s)", "Commentaires"]]
# Sauvegarde du fichier CSV
metadata_state.to_csv(metadata_csv_path, sep="|", index=False)
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
zf.write(metadata_csv_path, "metadata.csv")
original_audio = AudioSegment.from_file(audio_path)
for _, row in metadata_state.iterrows():
start_ms, end_ms = int(row["Début (s)"] * 1000), int(row["Fin (s)"] * 1000)
segment_audio = original_audio[start_ms:end_ms]
segment_filename = f"{row['ID']}.wav"
segment_path = os.path.join(TEMP_DIR, segment_filename)
segment_audio.export(segment_path, format="wav")
zf.write(segment_path, segment_filename)
print("[LOG] Fichier ZIP généré avec succès.")
return zip_path
# -------------------------------------------------
# 5. Interface utilisateur Gradio
# -------------------------------------------------
with gr.Blocks() as demo:
gr.Markdown("# Application de Découpe Audio")
metadata_state = gr.State(init_metadata_state())
audio_input = gr.Audio(type="filepath", label="Fichier audio")
zip_name = gr.Textbox(label="Nom du fichier ZIP", interactive=True)
raw_transcription = gr.Textbox(label="Transcription", interactive=True)
transcription_timestamps = gr.Textbox(label="Transcription avec Timestamps", interactive=True)
table = gr.Dataframe(headers=["Texte", "Début (s)", "Fin (s)"], datatype=["str", "str", "str"], row_count=(1, "dynamic"))
save_button = gr.Button("Enregistrer les segments")
save_message = gr.Markdown(label="📢 **Message de confirmation**")
generate_button = gr.Button("Générer ZIP")
zip_file = gr.File(label="Télécharger le ZIP")
word_timestamps = gr.State()
audio_input.change(transcribe_audio, inputs=audio_input, outputs=[raw_transcription, word_timestamps, transcription_timestamps, audio_input])
save_button.click(save_segments, inputs=table, outputs=[metadata_state, save_message])
generate_button.click(generate_zip, inputs=[metadata_state, audio_input, zip_name], outputs=zip_file)
demo.queue().launch() |