PacoFYM commited on
Commit
be2f1d0
·
verified ·
1 Parent(s): cfd126b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +75 -105
app.py CHANGED
@@ -1,128 +1,98 @@
1
  import os
 
2
  import torch
3
  import whisperx
4
  from pyannote.audio import Pipeline
5
  import gradio as gr
6
- import torchaudio
7
 
8
- def create_app():
9
- device = "cuda" if torch.cuda.is_available() else "cpu"
10
- hf_token = os.getenv("HF_TOKEN", "")
11
-
12
- with gr.Blocks() as app:
13
- gr.Markdown("<h1>Транскрипция и диаризация аудио</h1>")
14
- gr.Markdown(
15
- "Загрузите аудиофайл (формат WAV/MP3), нажмите **Транскрибировать**, "
16
- "отредактируйте результат и сохраните его."
17
- )
18
-
19
- # Убираем `source="upload"` — по умолчанию Audio позволяет загрузку
20
- audio_input = gr.Audio(label="Аудиофайл", type="filepath")
21
- transcribe_btn = gr.Button("Транскрибировать")
22
 
23
- # Здесь будут динамически добавляться поля для редактирования
24
- segment_container = gr.Column()
25
- save_btn = gr.Button("Сохранить результат")
26
- output_file = gr.File(label="Скачать .txt")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
 
28
- def transcribe_with_diarization(audio_path):
29
- # 1) Транскрипция WhisperX с фиксированным языком "ru"
30
- asr_model = whisperx.load_model("small", device, compute_type="float32")
31
- audio_array = whisperx.load_audio(audio_path)
32
- result = asr_model.transcribe(
33
- audio_array,
34
- batch_size=16,
35
- language="ru"
36
- )
37
- align_model, metadata = whisperx.load_align_model(
38
- language_code="ru", device=device
39
- )
40
- result = whisperx.align(
41
- result["segments"],
42
- align_model,
43
- metadata,
44
- audio_array,
45
- device=device,
46
- return_char_alignments=False
47
- )
48
 
49
- # 2) Диаризация Pyannote
50
- diar_pipeline = Pipeline.from_pretrained(
51
- "pyannote/speaker-diarization-3.1",
52
- use_auth_token=hf_token
53
- ).to(device)
54
- diarization = diar_pipeline(audio_path)
55
- result = whisperx.assign_word_speakers(diarization, result)
56
 
57
- # 3) Подготовка UI сегментов
58
- segments = result["segments"]
59
- speakers = sorted({seg["speaker"] for seg in segments})
60
 
61
- # Очищаем контейнер и добавляем новые поля
62
- segment_container.clear()
 
 
 
 
 
 
 
 
 
 
 
 
 
63
 
64
- # Поля для переименования спикеров
65
- name_inputs = {}
66
- with segment_container:
67
- gr.Markdown("**Укажите имена спикеров:**")
68
- for spk in speakers:
69
- name_inputs[spk] = gr.Textbox(
70
- label=f"Спикер {spk}",
71
- value=f"Спикер {spk}"
72
- )
73
 
74
- gr.Markdown("---")
75
- gr.Markdown("**Отредактируйте текст сегментов:**")
76
- text_inputs = []
77
- for i, seg in enumerate(segments):
78
- start, end = seg["start"], seg["end"]
79
- speaker = seg["speaker"]
80
- txt = seg["text"]
81
- # Срез аудио для сегмента
82
- seg_path = f"seg_{i}.wav"
83
- wave, sr = torchaudio.load(audio_path)
84
- torchaudio.save(
85
- seg_path,
86
- wave[:, int(start*sr):int(end*sr)],
87
- sr
88
- )
89
- with gr.Row():
90
- gr.Audio(value=seg_path, format="wav", label=None)
91
- ti = gr.Textbox(
92
- value=txt,
93
- label=f"{name_inputs[speaker].value}: {start:.1f}-{end:.1f}s",
94
- lines=2
95
- )
96
- text_inputs.append((speaker, ti))
97
 
98
- # Функция сохранения
99
- def save_result(**kwargs):
100
- # kwargs содержит сначала name_inputs, потом text_inputs
101
- names = {spk: kwargs[f"Спикер {spk}"] for spk in speakers}
102
- with open("result.txt", "w", encoding="utf-8") as f:
103
- for spk, ti in text_inputs:
104
- text = kwargs[ti.label]
105
- f.write(f"{names[spk]}: {text}\n")
106
- return "result.txt"
107
 
108
- # Создаем привязку кнопки сохранения
109
- save_btn.click(
110
- fn=save_result,
111
- inputs=list(name_inputs.values()) + [ti for _, ti in text_inputs],
112
- outputs=output_file
113
- )
114
 
115
  transcribe_btn.click(
116
  fn=transcribe_with_diarization,
117
  inputs=audio_input,
118
- outputs=[]
119
  )
120
 
121
- app.launch(
122
- server_name="0.0.0.0",
123
- server_port=7860,
124
- show_api=False
125
- )
126
 
127
  if __name__ == "__main__":
128
- create_app()
 
 
 
 
 
 
1
  import os
2
+ import tempfile
3
  import torch
4
  import whisperx
5
  from pyannote.audio import Pipeline
6
  import gradio as gr
 
7
 
8
+ # Загружаем модели один раз при старте
9
+ device = "cuda" if torch.cuda.is_available() else "cpu"
10
+ asr_model = whisperx.load_model("small", device) # модель WhisperX
11
+ diar_model = Pipeline.from_pretrained(
12
+ "pyannote/speaker-diarization-3.1",
13
+ use_auth_token=os.getenv("HF_TOKEN", "")
14
+ ).to(device)
 
 
 
 
 
 
 
15
 
16
+ def transcribe_with_diarization(audio_path):
17
+ # 1) транскрипция
18
+ result = asr_model.transcribe(
19
+ audio_path,
20
+ language="ru", # фиксируем русский, чтобы не тратить время на детект
21
+ compute_type="float32", # CPU-friendly
22
+ diarize=False
23
+ )
24
+ # 2) выравнивание (alignment)
25
+ result = whisperx.align(
26
+ result["segments"],
27
+ asr_model.audio,
28
+ asr_model.tokenizer,
29
+ device
30
+ )
31
+ # 3) диаризация
32
+ diar = diar_model({"uri": "audio", "audio": audio_path})
33
+ # вплетаем спикер-теги
34
+ segments = whisperx.diarize(result["segments"], diar)
35
 
36
+ # 4) готовим текст для raw_output (объединяем, без спикеров)
37
+ full_text = "\n".join(f"[{seg['start']:.2f}-{seg['end']:.2f}] {seg['text']}"
38
+ for seg in segments)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
 
40
+ # сохраняем в temp-файл для кнопки "Сохранить"
41
+ tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".txt", mode="w", encoding="utf-8")
42
+ for seg in segments:
43
+ tmp.write(f"{seg['speaker']}: {seg['start']:.2f}-{seg['end']:.2f}\t{seg['text']}\n")
44
+ tmp.close()
 
 
45
 
46
+ return full_text, tmp.name
 
 
47
 
48
+ def create_app():
49
+ with gr.Blocks(
50
+ title="Транскрипция и диаризация аудио",
51
+ css="""
52
+ @media(max-width:600px) {
53
+ .gradio-container { padding: 0.5rem; }
54
+ .gr-button { width: 100% !important; }
55
+ }
56
+ """
57
+ ) as app:
58
+ gr.Markdown("# 🎙️ Транскрипция и диаризация аудио")
59
+ gr.Markdown(
60
+ "Загрузите аудиофайл, нажмите **Транскрибировать**, "
61
+ "прослушайте сегменты, отредактируйте текст и присвойте имена спикерам."
62
+ )
63
 
64
+ audio_input = gr.Audio(
65
+ label="Аудиофайл",
66
+ type="filepath"
67
+ )
 
 
 
 
 
68
 
69
+ transcribe_btn = gr.Button("▶️ Транскрибировать")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
 
71
+ # ПОЛЕ для «сырого» текста сразу после транскрипции
72
+ raw_output = gr.Textbox(
73
+ label="Результат транскрипции",
74
+ placeholder="Здесь появится текст после транскрибации",
75
+ lines=6
76
+ )
 
 
 
77
 
78
+ save_btn = gr.Button("💾 Сохранить результат")
79
+ output_file = gr.File(
80
+ label="Скачать .txt",
81
+ file_count="single"
82
+ )
 
83
 
84
  transcribe_btn.click(
85
  fn=transcribe_with_diarization,
86
  inputs=audio_input,
87
+ outputs=[raw_output, output_file]
88
  )
89
 
90
+ return app
 
 
 
 
91
 
92
  if __name__ == "__main__":
93
+ create_app().launch(
94
+ server_name="0.0.0.0",
95
+ server_port=7860,
96
+ share=False,
97
+ inbrowser=False
98
+ )