Woziii commited on
Commit
75ca749
1 Parent(s): e188015

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +554 -0
app.py ADDED
@@ -0,0 +1,554 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import spaces
2
+ import torch
3
+ import gradio as gr
4
+ import yt_dlp as youtube_dl
5
+ from transformers import pipeline
6
+ from transformers.pipelines.audio_utils import ffmpeg_read
7
+ from transformers import WhisperProcessor
8
+ from pyannote.audio import Pipeline as PyannotePipeline
9
+ from datetime import datetime
10
+ import tempfile
11
+ import numpy as np
12
+ from itertools import groupby
13
+ from datetime import datetime
14
+ from gradio.components import State
15
+ import os
16
+ import re
17
+
18
+
19
+
20
+ os.environ['PYTORCH_CUDA_ALLOC_CONF'] = 'max_split_size_mb:307'
21
+
22
+ try:
23
+ diarization_pipeline = PyannotePipeline.from_pretrained(
24
+ "pyannote/speaker-diarization-3.1",
25
+ use_auth_token=os.environ["HF_TOKEN"]
26
+ )
27
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
28
+ diarization_pipeline.to(device)
29
+ except Exception as e:
30
+ print(f"Error initializing diarization pipeline: {e}")
31
+ diarization_pipeline = None
32
+
33
+ # Updated Whisper model
34
+ MODEL_NAME = "openai/whisper-medium"
35
+ #BATCH_SIZE = 2 # Réduction de la taille du batch
36
+ FILE_LIMIT_MB = 1000
37
+ YT_LENGTH_LIMIT_S = 3600
38
+
39
+ device = 0 if torch.cuda.is_available() else "cpu"
40
+
41
+ pipe = pipeline(
42
+ task="automatic-speech-recognition",
43
+ model=MODEL_NAME,
44
+ #chunk_length_s=30,
45
+ device=device,
46
+ model_kwargs={"low_cpu_mem_usage": True},
47
+ )
48
+
49
+
50
+
51
+
52
+ def associate_speakers_with_timestamps(transcription_result, diarization, tolerance=0.1, min_segment_duration=0.5):
53
+ word_segments = transcription_result['chunks']
54
+ diarization_segments = list(diarization.itertracks(yield_label=True))
55
+
56
+ speaker_transcription = []
57
+ current_speaker = None
58
+ current_text = []
59
+ unassigned_words = []
60
+ last_segment_index = 0
61
+
62
+ def flush_current_segment():
63
+ nonlocal current_speaker, current_text
64
+ if current_speaker and current_text:
65
+ speaker_transcription.append((current_speaker, ' '.join(current_text)))
66
+ current_text = []
67
+
68
+ for word in word_segments:
69
+ word_start, word_end = word['timestamp']
70
+ word_text = word['text']
71
+
72
+ assigned = False
73
+ for i in range(last_segment_index, len(diarization_segments)):
74
+ segment, _, speaker = diarization_segments[i]
75
+ if segment.start - tolerance <= word_start < segment.end + tolerance:
76
+ if speaker != current_speaker:
77
+ if current_speaker and len(current_text) == 1 and len(current_text[0].split()) <= 2:
78
+ # Si le segment précédent est très court, ne changez pas de locuteur
79
+ current_text.append(word_text)
80
+ else:
81
+ flush_current_segment()
82
+ current_speaker = speaker
83
+ current_text.append(word_text)
84
+ last_segment_index = i
85
+ assigned = True
86
+ break
87
+
88
+ if not assigned:
89
+ unassigned_words.append((word_start, word_text))
90
+
91
+ # Traitement des mots non assignés
92
+ for word_start, word_text in unassigned_words:
93
+ closest_segment = min(diarization_segments, key=lambda x: abs(x[0].start - word_start))
94
+ speaker = closest_segment[2]
95
+ if speaker != current_speaker:
96
+ flush_current_segment()
97
+ current_speaker = speaker
98
+ current_text.append(word_text)
99
+
100
+ flush_current_segment()
101
+
102
+ # Fusion des segments courts
103
+ merged_transcription = []
104
+ for speaker, text in speaker_transcription:
105
+ if not merged_transcription or merged_transcription[-1][0] != speaker or len(text.split()) > 3:
106
+ merged_transcription.append((speaker, text))
107
+ else:
108
+ merged_transcription[-1] = (speaker, merged_transcription[-1][1] + " " + text)
109
+
110
+ return merged_transcription
111
+
112
+ def simplify_diarization_output(speaker_transcription):
113
+ simplified = []
114
+ for speaker, text in speaker_transcription:
115
+ simplified.append(f"{speaker}: {text}")
116
+ return "\n".join(simplified)
117
+
118
+ def parse_simplified_diarization(simplified_text):
119
+ pattern = r"(SPEAKER_\d+):\s*(.*)"
120
+ matches = re.findall(pattern, simplified_text, re.MULTILINE)
121
+ return [(speaker, text.strip()) for speaker, text in matches]
122
+
123
+ def process_transcription(*args):
124
+ generator = transcribe_and_diarize(*args)
125
+ for progress_message, raw_text, speaker_transcription in generator:
126
+ pass # Consommer le générateur jusqu'à la fin
127
+ simplified_diarization = simplify_diarization_output(speaker_transcription)
128
+ return progress_message, raw_text, simplified_diarization
129
+
130
+ def process_yt_transcription(*args):
131
+ html_embed, raw_text, speaker_transcription = yt_transcribe(*args)
132
+ simplified_diarization = simplify_diarization_output(speaker_transcription)
133
+ return html_embed, raw_text, simplified_diarization
134
+
135
+ def create_process_explanation(tab_name):
136
+ if tab_name == "Fichier audio":
137
+ return gr.Markdown("""
138
+ ### Comment fonctionne la transcription de fichier audio ?
139
+ 1. Chargez votre fichier audio en utilisant le bouton de téléchargement.
140
+ 2. Choisissez entre la transcription (même langue) ou la traduction (vers le français).
141
+ 3. Cliquez sur 'Lancer la transcription' pour démarrer le processus.
142
+ 4. Le modèle Whisper analysera votre audio et générera une transcription.
143
+ 5. Si activé, le modèle de diarisation identifiera les différents locuteurs.
144
+ 6. Examinez les résultats et modifiez-les si nécessaire.
145
+ 7. Ajoutez des métadonnées optionnelles pour enrichir votre transcription.
146
+ 8. Cliquez sur 'Générer la transcription formatée' pour obtenir le résultat final.
147
+
148
+ **Note sur la confidentialité**: Votre fichier audio est traité localement et n'est pas stocké après le traitement.
149
+ """)
150
+ elif tab_name == "Microphone":
151
+ return gr.Markdown("""
152
+ ### Comment fonctionne l'enregistrement et la transcription en direct ?
153
+ 1. Cliquez sur le bouton d'enregistrement pour commencer à capturer votre voix.
154
+ 2. Parlez clairement dans votre microphone.
155
+ 3. Cliquez à nouveau sur le bouton pour arrêter l'enregistrement.
156
+ 4. Choisissez entre la transcription ou la traduction.
157
+ 5. Cliquez sur 'Transcrire l'enregistrement' pour lancer le processus.
158
+ 6. Le modèle Whisper traitera votre enregistrement et générera une transcription.
159
+ 7. Examinez les résultats et utilisez le bouton 'Générer la transcription formatée' si nécessaire.
160
+
161
+ **Astuce**: Pour une meilleure qualité, utilisez un microphone externe et évitez les bruits de fond.
162
+ """)
163
+ elif tab_name == "YouTube":
164
+ return gr.Markdown("""
165
+ ### Comment transcrire l'audio d'une vidéo YouTube ?
166
+ 1. Collez l'URL de la vidéo YouTube dans le champ prévu.
167
+ 2. Choisissez entre la transcription ou la traduction.
168
+ 3. Cliquez sur 'Transcrire la vidéo' pour démarrer le processus.
169
+ 4. Le modèle téléchargera l'audio de la vidéo et le traitera.
170
+ 5. La transcription sera générée et affichée.
171
+ 6. Vous pouvez examiner les résultats et utiliser le bouton 'Générer la transcription formatée'.
172
+
173
+ **Note**: La durée maximale des vidéos est limitée à 1 heure pour des raisons de performance.
174
+ """)
175
+
176
+ # New functions for progress indicator
177
+ def create_progress_indicator():
178
+ return gr.State({"stage": 0, "message": "En attente de démarrage..."})
179
+
180
+ def update_progress(progress_state, stage, message):
181
+ progress_state["stage"] = stage
182
+ progress_state["message"] = message
183
+ return progress_state
184
+
185
+ def display_progress(progress_state):
186
+ stages = [
187
+ "Chargement du fichier",
188
+ "Préparation de l'audio",
189
+ "Transcription en cours",
190
+ "Diarisation (identification des locuteurs)",
191
+ "Finalisation des résultats"
192
+ ]
193
+ progress = (progress_state["stage"] / len(stages)) * 100
194
+ return gr.HTML(f"""
195
+ <style>
196
+ @keyframes pulse {{
197
+ 0% {{ box-shadow: 0 0 0 0 rgba(66, 133, 244, 0.7); }}
198
+ 70% {{ box-shadow: 0 0 0 10px rgba(66, 133, 244, 0); }}
199
+ 100% {{ box-shadow: 0 0 0 0 rgba(66, 133, 244, 0); }}
200
+ }}
201
+ </style>
202
+ <div style="margin-bottom: 20px; background-color: #f0f0f0; padding: 15px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
203
+ <h4 style="margin-bottom: 10px; color: #333; font-weight: bold;">Progression de la transcription</h4>
204
+ <div style="background-color: #e0e0e0; height: 24px; border-radius: 12px; overflow: hidden; position: relative;">
205
+ <div style="background-color: #4285F4; width: {progress}%; height: 100%; border-radius: 12px; transition: width 0.5s ease-in-out;"></div>
206
+ <div style="position: absolute; top: 0; left: 0; right: 0; bottom: 0; display: flex; align-items: center; justify-content: center; color: #333; font-weight: bold;">
207
+ {progress:.0f}%
208
+ </div>
209
+ </div>
210
+ <p style="margin-top: 10px; color: #666; font-style: italic;">{progress_state["message"]}</p>
211
+ <div style="width: 10px; height: 10px; background-color: #4285F4; border-radius: 50%; margin-top: 10px; animation: pulse 2s infinite;"></div>
212
+ </div>
213
+ """)
214
+
215
+ @spaces.GPU(duration=120)
216
+ def transcribe_and_diarize(file_path, task, progress=gr.Progress()):
217
+ progress(0, desc="Initialisation...")
218
+ yield "Chargement du fichier...", None, None
219
+
220
+ progress(0.2, desc="Préparation de l'audio...")
221
+ yield "Préparation de l'audio...", None, None
222
+
223
+ progress(0.4, desc="Laissez moi quelques minutes pour déchiffrer les voix et rédiger l'audio 🤓 ✍️ ...")
224
+ transcription_result = pipe(file_path, generate_kwargs={"task": task, "language": "fr"}, return_timestamps="word")
225
+ yield "Transcription en cours...", None, None
226
+
227
+ progress(0.6, desc=" C'est fait 😮‍💨 ! Je m'active à fusionner tout ça, un instant, J'y suis presque...")
228
+ if diarization_pipeline:
229
+ diarization = diarization_pipeline(file_path)
230
+ speaker_transcription = associate_speakers_with_timestamps(transcription_result, diarization)
231
+ else:
232
+ speaker_transcription = [(None, transcription_result['text'])]
233
+ yield "Diarisation en cours...", None, None
234
+
235
+ progress(0.8, desc="Finalisation des résultats...")
236
+ yield "Voilà!", transcription_result['text'], speaker_transcription
237
+
238
+ progress(1.0, desc="Terminé!")
239
+ return "Transcription terminée!", transcription_result['text'], speaker_transcription
240
+
241
+ def format_to_markdown(transcription_text, speaker_transcription, audio_duration=None, location=None, speaker_age=None, context=None):
242
+ metadata = {
243
+ "Date de traitement": datetime.now().strftime('%d/%m/%Y %H:%M'),
244
+ "Durée de l'audio": f"{audio_duration} secondes" if audio_duration else "[à remplir]",
245
+ "Lieu": location if location else "[non spécifié]",
246
+ "Âge de l'intervenant": f"{speaker_age} ans" if speaker_age else "[non spécifié]",
247
+ "Contexte": context if context else "[non spécifié]"
248
+ }
249
+
250
+ metadata_text = "\n".join([f"- **{key}** : '{value}'" for key, value in metadata.items()])
251
+
252
+ try:
253
+ if isinstance(speaker_transcription, str):
254
+ speaker_transcription = parse_simplified_diarization(speaker_transcription)
255
+
256
+ if isinstance(speaker_transcription, list) and all(isinstance(item, tuple) and len(item) == 2 for item in speaker_transcription):
257
+ formatted_transcription = []
258
+ for speaker, text in speaker_transcription:
259
+ formatted_transcription.append(f"**{speaker}**: {text}")
260
+ transcription_text = "\n\n".join(formatted_transcription)
261
+ else:
262
+ raise ValueError("Invalid speaker transcription format")
263
+ except Exception as e:
264
+ print(f"Error formatting speaker transcription: {e}")
265
+ transcription_text = "Error formatting speaker transcription. Using raw transcription instead.\n\n" + transcription_text
266
+
267
+ formatted_output = f"""
268
+ # Transcription Formatée
269
+
270
+ ## Métadonnées
271
+ {metadata_text}
272
+
273
+ ## Transcription
274
+ {transcription_text}
275
+ """
276
+ return formatted_output
277
+
278
+ def _return_yt_html_embed(yt_url):
279
+ video_id = yt_url.split("?v=")[-1]
280
+ HTML_str = (
281
+ f'<center> <iframe width="500" height="320" src="https://www.youtube.com/embed/{video_id}"> </iframe>'
282
+ " </center>"
283
+ )
284
+ return HTML_str
285
+
286
+ def download_yt_audio(yt_url, filename):
287
+ info_loader = youtube_dl.YoutubeDL()
288
+
289
+ try:
290
+ info = info_loader.extract_info(yt_url, download=False)
291
+ except youtube_dl.utils.DownloadError as err:
292
+ raise gr.Error(str(err))
293
+
294
+ file_length = info["duration"]
295
+
296
+ if file_length > YT_LENGTH_LIMIT_S:
297
+ yt_length_limit_hms = time.strftime("%H:%M:%S", time.gmtime(YT_LENGTH_LIMIT_S))
298
+ file_length_hms = time.strftime("%H:%M:%S", time.gmtime(file_length))
299
+ raise gr.Error(f"La durée maximale YouTube est de {yt_length_limit_hms}, la vidéo YouTube dure {file_length_hms}.")
300
+
301
+ ydl_opts = {"outtmpl": filename, "format": "bestaudio/best"}
302
+
303
+ with youtube_dl.YoutubeDL(ydl_opts) as ydl:
304
+ try:
305
+ ydl.download([yt_url])
306
+ except youtube_dl.utils.ExtractorError as err:
307
+ raise gr.Error(str(err))
308
+
309
+ @spaces.GPU(duration=120)
310
+ def yt_transcribe(yt_url, task):
311
+ html_embed_str = _return_yt_html_embed(yt_url)
312
+
313
+ with tempfile.TemporaryDirectory() as tmpdirname:
314
+ filepath = os.path.join(tmpdirname, "video.mp4")
315
+ download_yt_audio(yt_url, filepath)
316
+ with open(filepath, "rb") as f:
317
+ inputs = f.read()
318
+
319
+ inputs = ffmpeg_read(inputs, pipe.feature_extractor.sampling_rate)
320
+ inputs = {"array": inputs, "sampling_rate": pipe.feature_extractor.sampling_rate}
321
+
322
+ transcription_result = pipe(inputs, generate_kwargs={"task": task}, return_timestamps=True)
323
+ transcription_text = transcription_result['text']
324
+
325
+ if diarization_pipeline:
326
+ diarization = diarization_pipeline(filepath)
327
+ speaker_transcription = associate_speakers_with_timestamps(transcription_result, diarization)
328
+ else:
329
+ speaker_transcription = [(None, transcription_text)]
330
+
331
+ return html_embed_str, transcription_text, speaker_transcription
332
+
333
+ def create_info_box(title, content):
334
+ return gr.Markdown(f"### {title}\n{content}")
335
+
336
+ css_file_path = os.path.join(os.path.dirname(__file__), "style.css")
337
+ with open(css_file_path, "r") as f:
338
+ custom_css = f.read()
339
+
340
+ theme = gr.themes.Default().set(
341
+ body_background_fill="#f0f2f5",
342
+ body_background_fill_dark="#2c3e50",
343
+ button_primary_background_fill="#3498db",
344
+ button_primary_background_fill_dark="#2980b9",
345
+ button_secondary_background_fill="#2ecc71",
346
+ button_secondary_background_fill_dark="#27ae60",
347
+ )
348
+
349
+ demo = gr.Blocks(
350
+ theme=gr.themes.Default(),
351
+ title="Scribe - Assistant de Transcription Audio 🎙️📝",
352
+ css=custom_css
353
+ )
354
+
355
+
356
+ with demo:
357
+ gr.Markdown("# 🎙️ Scribe : Assistant de Transcription Audio Intelligent 📝")
358
+ gr.HTML(
359
+ """
360
+ <div class="logo">
361
+ <img src="https://image.noelshack.com/fichiers/2024/33/4/1723713257-dbe58773-0638-445b-a88c-3fc1f2002408.jpg" alt="Scribe Logo">
362
+ </div>
363
+ """
364
+ )
365
+ gr.Markdown("## **Bienvenue sur Scribe, votre solution professionnelle pour la transcription audio. Transformez efficacement vos fichiers audio, enregistrements en direct ou vidéos YouTube en texte précis.**")
366
+
367
+ gr.Markdown("""
368
+ ### 🔍 **Fonctionnement du Modèle** :
369
+ Scribe utilise une approche en deux étapes pour transformer l'audio en texte structuré :
370
+
371
+ 1. **Transcription avec Whisper Medium** :
372
+ - Modèle de reconnaissance vocale développé par OpenAI
373
+ - Utilise un réseau neuronal encodeur-décodeur avec attention
374
+ - Capable de traiter divers accents et bruits de fond
375
+ - Optimisé pour un équilibre entre précision et rapidité
376
+
377
+ 2. **Diarisation avec pyannote/speaker-diarization-3.1** :
378
+ - Identifie et segmente les différents locuteurs dans l'audio
379
+ - Utilise des techniques d'apprentissage profond pour l'extraction de caractéristiques vocales
380
+ - Applique un algorithme de clustering pour regrouper les segments par locuteur
381
+
382
+ Le processus complet implique :
383
+ a) Prétraitement de l'audio
384
+ b) Transcription du contenu
385
+ c) Segmentation et identification des locuteurs
386
+ d) Fusion des résultats pour une sortie structurée
387
+
388
+
389
+ ### 💡 **Conseils pour de Meilleurs Résultats**
390
+ - Utilisez des enregistrements de haute qualité avec peu de bruit de fond.
391
+ - Pour les longs enregistrements, il est recommandé de segmenter votre audio.
392
+ - Vérifiez toujours la transcription, en particulier pour les termes techniques ou les noms propres.
393
+ - Utilisez des microphones externes pour les enregistrements en direct si possible.
394
+ """)
395
+ with gr.Accordion("🔐 Sécurité des Données et Pipelines", open=False):
396
+ gr.Markdown("""
397
+
398
+ #### Qu'est-ce qu'une pipeline ?
399
+ Une pipeline dans le contexte de l'apprentissage automatique est une série d'étapes de traitement des données, allant de l'entrée brute à la sortie finale. Dans Scribe, nous utilisons deux pipelines principales :
400
+
401
+ 1. **Pipeline de Transcription** : Basée sur le modèle Whisper Medium, elle convertit l'audio en texte.
402
+ 2. **Pipeline de Diarisation** : Identifie les différents locuteurs dans l'audio.
403
+
404
+ #### Comment fonctionnent nos pipelines ?
405
+ 1. **Chargement Local** : Les modèles sont chargés localement sur votre machine ou serveur.
406
+ 2. **Traitement In-Situ** : Toutes les données sont traitées sur place, sans envoi à des serveurs externes.
407
+ 3. **Mémoire Volatile** : Les données sont stockées temporairement en mémoire vive et effacées après utilisation.
408
+
409
+ #### Sécurité et Confidentialité
410
+ - **Pas de Transmission Externe** : Vos données audio et texte restent sur votre système local.
411
+ - **Isolation** : Chaque session utilisateur est isolée des autres.
412
+ - **Nettoyage Automatique** : Les fichiers temporaires sont supprimés après chaque utilisation.
413
+ - **Mise à Jour Sécurisée** : Les modèles sont mis à jour de manière sécurisée via Hugging Face.
414
+
415
+ #### Mesures de Sécurité Supplémentaires
416
+ - Nous utilisons des tokens d'authentification sécurisés pour accéder aux modèles.
417
+ - Les fichiers YouTube sont téléchargés et traités localement, sans stockage permanent.
418
+ - Aucune donnée utilisateur n'est conservée après la fermeture de la session.
419
+
420
+ En utilisant Scribe, vous bénéficiez d'un traitement de données hautement sécurisé et respectueux de la vie privée, tout en profitant de la puissance des modèles d'IA de pointe.
421
+ """)
422
+ # ... (le reste du fichier reste inchangé)
423
+ with gr.Tabs():
424
+ with gr.Tab("Fichier audio 🎵"):
425
+ gr.Markdown("### 📂 Transcription de fichiers audio")
426
+ audio_input = gr.Audio(type="filepath", label="Chargez votre fichier audio")
427
+ task_input = gr.Radio(["transcribe", "translate"], label="Choisissez la tâche", value="transcribe")
428
+ transcribe_button = gr.Button("🚀 Lancer la transcription", elem_classes="button-primary")
429
+
430
+ progress_display = gr.Markdown(label="État de la progression")
431
+
432
+ with gr.Accordion("Résultats 📊", open=True):
433
+ raw_output = gr.Textbox(label="📝 Transcription brute", info="Texte généré par le modèle. Modifiable si nécessaire.")
434
+ speaker_output = gr.Textbox(label="👥 Diarisation (format simplifié)", info="Identification des locuteurs. Format : 'SPEAKER_XX: texte'")
435
+ with gr.Accordion("Métadonnées (optionnel) 📌", open=False):
436
+ audio_duration = gr.Textbox(label="⏱️ Durée de l'audio (mm:ss)")
437
+ location = gr.Textbox(label="📍 Lieu de l'enregistrement")
438
+ speaker_age = gr.Number(label="👤 Âge de l'intervenant principal")
439
+ context = gr.Textbox(label="📝 Contexte de l'enregistrement")
440
+
441
+ format_button = gr.Button("✨ Générer la transcription formatée", elem_classes="button-secondary")
442
+ formatted_output = gr.Markdown(label="📄 Transcription formatée :")
443
+
444
+
445
+ with gr.Tab("Microphone 🎤"):
446
+ gr.Markdown("### 🗣️ Enregistrement et transcription en direct")
447
+ mic_input = gr.Audio(type="filepath", label="Enregistrez votre voix")
448
+ mic_task_input = gr.Radio(["transcribe", "translate"], label="Choisissez la tâche", value="transcribe")
449
+ mic_transcribe_button = gr.Button("🚀 Transcrire l'enregistrement", elem_classes="button-primary")
450
+
451
+ mic_progress_display = gr.Markdown(label="État de la progression")
452
+
453
+ with gr.Accordion("Résultats 📊", open=True):
454
+ mic_raw_output = gr.Textbox(label="📝 Transcription brute", info="Texte généré par le modèle. Modifiable si nécessaire.")
455
+ mic_speaker_output = gr.Textbox(label="👥 Diarisation (format simplifié)", info="Identification des locuteurs. Format : 'SPEAKER_XX: texte'")
456
+ with gr.Accordion("Métadonnées (optionnel) 📌", open=False):
457
+ mic_audio_duration = gr.Textbox(label="⏱️ Durée de l'enregistrement (mm:ss)")
458
+ mic_location = gr.Textbox(label="📍 Lieu de l'enregistrement")
459
+ mic_speaker_age = gr.Number(label="👤 Âge de l'intervenant principal")
460
+ mic_context = gr.Textbox(label="📝 Contexte de l'enregistrement")
461
+
462
+ mic_format_button = gr.Button("✨ Générer la transcription formatée", elem_classes="button-secondary")
463
+ mic_formatted_output = gr.Markdown(label="📄 Transcription formatée :")
464
+
465
+ with gr.Tab("YouTube 🎥"):
466
+ gr.Markdown("### 🌐 Transcription à partir de vidéos YouTube")
467
+ yt_input = gr.Textbox(lines=1, placeholder="Collez l'URL d'une vidéo YouTube ici", label="🔗 URL YouTube")
468
+ yt_task_input = gr.Radio(["transcribe", "translate"], label="Choisissez la tâche", value="transcribe")
469
+ yt_transcribe_button = gr.Button("🚀 Transcrire la vidéo", elem_classes="button-primary")
470
+
471
+ yt_progress_display = gr.Markdown(label="État de la progression")
472
+
473
+ yt_html_output = gr.HTML(label="▶️ Aperçu de la vidéo")
474
+
475
+ with gr.Accordion("Résultats 📊", open=True):
476
+ yt_raw_output = gr.Textbox(label="📝 Transcription brute", info="Texte généré par le modèle. Modifiable si nécessaire.")
477
+ yt_speaker_output = gr.Textbox(label="👥 Diarisation (format simplifié)", info="Identification des locuteurs. Format : 'SPEAKER_XX: texte'")
478
+ with gr.Accordion("Métadonnées (optionnel) 📌", open=False):
479
+ yt_audio_duration = gr.Textbox(label="⏱️ Durée de la vidéo (mm:ss)")
480
+ yt_channel = gr.Textbox(label="📺 Nom de la chaîne YouTube")
481
+ yt_publish_date = gr.Textbox(label="📅 Date de publication")
482
+ yt_context = gr.Textbox(label="📝 Contexte de la vidéo")
483
+
484
+ yt_format_button = gr.Button("✨ Générer la transcription formatée", elem_classes="button-secondary")
485
+ yt_formatted_output = gr.Markdown(label="📄 Transcription formatée :")
486
+
487
+
488
+ gr.Markdown("""### 🛠️ Capacités :
489
+ - Transcription multilingue avec détection automatique de la langue
490
+ - Traduction vers le français (pour les contenus non francophones)
491
+ - Identification précise des changements de locuteurs
492
+ - Traitement de fichiers audio, enregistrements en direct et vidéos YouTube
493
+ - Gestion de divers formats audio et qualités d'enregistrement
494
+
495
+ ### ⚙️ Spécifications Techniques :
496
+ - Modèle de transcription : Whisper Medium
497
+ - Pipeline de diarisation : pyannote/speaker-diarization-3.1
498
+ - Limite de taille de fichier : _(Nous n'avons, à ce jour, pas de limite précise. Cependant, nous vous recommandons de ne pas dépasser 6 minutes. )_
499
+ - Durée maximale pour les vidéos YouTube : _(Nous n'avons, à ce jour, pas de limite précise. Cependant, pour une utilisation optimale, l'audio ne doit pas dépasser 30 minutes. )_
500
+ - Formats audio supportés : MP3, WAV, M4A, et plus
501
+ """)
502
+
503
+ with gr.Accordion("❓ README :", open=False):
504
+ gr.Markdown("""
505
+ - Concepteur : _Woziii_
506
+ - Modèles :
507
+ - [Whisper-médium](https://huggingface.co/openai/whisper-medium) : Model size - 764M params - Tensor type F32 -
508
+ - [speaker-diarization-3.1](https://huggingface.co/pyannote/speaker-diarization-3.1) : Model size - Unknow - Tensor type F32 -
509
+ - Version : _V.2.0.0-Bêta_
510
+ - Langues : FR, EN
511
+ - Copyright : [cc-by-nc-4.0]
512
+ - [En savoir +](README.md)
513
+ """)
514
+
515
+ # Connexions des boutons aux fonctions appropriées
516
+ transcribe_button.click(
517
+ process_transcription,
518
+ inputs=[audio_input, task_input],
519
+ outputs=[progress_display, raw_output, speaker_output]
520
+ )
521
+
522
+ format_button.click(
523
+ format_to_markdown,
524
+ inputs=[raw_output, speaker_output, audio_duration, location, speaker_age, context],
525
+ outputs=formatted_output
526
+ )
527
+
528
+ mic_transcribe_button.click(
529
+ process_transcription,
530
+ inputs=[mic_input, mic_task_input],
531
+ outputs=[mic_progress_display, mic_raw_output, mic_speaker_output]
532
+ )
533
+
534
+ mic_format_button.click(
535
+ format_to_markdown,
536
+ inputs=[mic_raw_output, mic_speaker_output, audio_duration, location, speaker_age, context],
537
+ outputs=mic_formatted_output
538
+ )
539
+
540
+ yt_transcribe_button.click(
541
+ process_yt_transcription,
542
+ inputs=[yt_input, yt_task_input],
543
+ outputs=[yt_html_output, yt_raw_output, yt_speaker_output]
544
+ )
545
+
546
+ yt_format_button.click(
547
+ format_to_markdown,
548
+ inputs=[yt_raw_output, yt_speaker_output, audio_duration, location, speaker_age, context],
549
+ outputs=yt_formatted_output
550
+ )
551
+
552
+
553
+ if __name__ == "__main__":
554
+ demo.queue().launch()