From 1e5105b7d658310c159c65ba318c08522506c3fb Mon Sep 17 00:00:00 2001 From: Voksik Date: Thu, 9 Oct 2025 04:40:03 +0300 Subject: [PATCH] fix glueAudio --- app.py | 10 ++++-- handlers/glueAudio.py | 64 ++++++++++++++++++++++++++++++++++----- handlers/gradioHandler.py | 26 ++++++++++------ services/fasterWhisper.py | 1 + 4 files changed, 82 insertions(+), 19 deletions(-) diff --git a/app.py b/app.py index 6dda7f7..6f0eb48 100644 --- a/app.py +++ b/app.py @@ -115,7 +115,9 @@ def main(): recognizeBtn.click( gh.handleRecognizeBtn, - inputs=[audioFiles, fastWhisperModel, device, compute_type, beamSize, vadFilter, minSilenceDurationMs, speechPadMs, temp0, temp1, temp2, wordTimestamps, noSpeechThreshold, conditionOnPreviousText, gr.State(GLUED_AUDIO_FILENAME), gr.State(OUTPUT_PATH)], + inputs=[audioFiles, fastWhisperModel, device, compute_type, beamSize, + vadFilter, minSilenceDurationMs, speechPadMs, temp0, temp1, temp2, + wordTimestamps, noSpeechThreshold, conditionOnPreviousText, gr.State(GLUED_AUDIO_FILENAME), gr.State(OUTPUT_PATH)], outputs=[recognizedText], ) @@ -123,7 +125,8 @@ def main(): # автоматический пайплайн recognizedText.change( gh.generateByCondition, - inputs=[apiKey, llmProvider, llmModel, systemPrompt, recognizedText, llmTemperature, isPipelineEnabledCheckbox, gr.State("change"), saveFileCheckbox, filename, filenamePdf, gr.State(OUTPUT_PATH)], + inputs=[apiKey, llmProvider, llmModel, systemPrompt, recognizedText, llmTemperature, + isPipelineEnabledCheckbox, gr.State("change"), saveFileCheckbox, filename, filenamePdf, gr.State(OUTPUT_PATH)], outputs=[refinedText, refinedTextMD] ) @@ -137,7 +140,8 @@ def main(): ) refineTextBtn.click( gh.generateByCondition, - inputs=[apiKey, llmProvider, llmModel, systemPrompt, recognizedText, llmTemperature, isPipelineEnabledCheckbox, gr.State("click"), saveFileCheckbox, filename, filenamePdf, gr.State(OUTPUT_PATH)], + inputs=[apiKey, llmProvider, llmModel, systemPrompt, recognizedText, llmTemperature, + isPipelineEnabledCheckbox, gr.State("click"), saveFileCheckbox, filename, filenamePdf, gr.State(OUTPUT_PATH)], outputs=[refinedText, refinedTextMD] ) diff --git a/handlers/glueAudio.py b/handlers/glueAudio.py index 55e10c1..5bd2141 100644 --- a/handlers/glueAudio.py +++ b/handlers/glueAudio.py @@ -1,11 +1,61 @@ -from pydub import AudioSegment +import subprocess +from pathlib import Path + class GlueAudio(): - def glue(self, audioFiles): - glued = AudioSegment.empty() + def glue(self, audio_files: list, output_path: str, output_filename: str) -> Path: + """ + Склеивает аудиофайлы РАЗНЫХ форматов с помощью FFmpeg и filter_complex. + Это универсальный и эффективный по памяти метод. - for audioFile in audioFiles: - audio = AudioSegment.from_file(audioFile) - glued += audio + Args: + audio_files (list): Список путей к исходным аудиофайлам. + output_path (str): Директория для сохранения итогового файла. + output_filename (str): Имя итогового склеенного файла. - return glued + Returns: + Path: Путь к созданному склеенному файлу. + """ + output_dir = Path(output_path) + output_dir.mkdir(parents=True, exist_ok=True) + final_audio_path = output_dir / output_filename + + if not audio_files: + raise ValueError("Список аудиофайлов для склейки пуст.") + + # 1. Формируем часть команды с входными файлами (-i file1 -i file2 ...) + input_args = [] + for file_path in audio_files: + input_args.extend(['-i', str(Path(file_path).resolve())]) + + # 2. Формируем строку для filter_complex + num_files = len(audio_files) + stream_specifiers = "".join([f"[{i}:a]" for i in range(num_files)]) + filter_complex_str = f"{stream_specifiers}concat=n={num_files}:v=0:a=1[outa]" + + # 3. Собираем полную команду + command = [ + 'ffmpeg', + *input_args, # Распаковываем список входных файлов + '-filter_complex', filter_complex_str, + '-map', '[outa]', + '-c:a', 'libmp3lame', + '-q:a', '2', + str(final_audio_path), + '-y' + ] + + try: + # 4. Выполняем команду + print(f"Выполнение команды FFmpeg: {' '.join(command)}") + subprocess.run(command, check=True, capture_output=True, text=True) + print("FFmpeg успешно завершил склейку.") + + except FileNotFoundError: + raise FileNotFoundError("FFmpeg не найден. Убедитесь, что он установлен и доступен в системной переменной PATH.") + except subprocess.CalledProcessError as e: + print("Ошибка при выполнении FFmpeg!") + print("Stderr:", e.stderr) + raise RuntimeError(f"Ошибка FFmpeg при склейке файлов: {e.stderr}") + + return final_audio_path \ No newline at end of file diff --git a/handlers/gradioHandler.py b/handlers/gradioHandler.py index 1615377..fa57382 100644 --- a/handlers/gradioHandler.py +++ b/handlers/gradioHandler.py @@ -12,15 +12,23 @@ class GradioHandlers: self.llm_factory = llm_factory # Сохраняем фабрику def handleRecognizeBtn( - self, audioFiles, model, device, - compute_type, beamSize, vadFilter, - minSilenceDurationMs, speechPadMs, - temp0, temp1, temp2, - wordTimestamps, noSpeechThreshold, conditionOnPreviousText, - filename, outPath): - audioFile = self.ga.glue(audioFiles) - file = self.fh.saveFile(filename, audioFile, outPath) - return self.FasterWhisper.recognize(model, device, compute_type, file, beamSize, vadFilter, minSilenceDurationMs, speechPadMs, temp0, temp1, temp2, wordTimestamps, noSpeechThreshold, conditionOnPreviousText) + self, audioFiles, model, device, compute_type, beamSize, vadFilter, + minSilenceDurationMs, speechPadMs, temp0, temp1, temp2, + wordTimestamps, noSpeechThreshold, conditionOnPreviousText, filename, outPath + ): + try: + glued_audio_path = self.ga.glue( + audio_files=[f.name for f in audioFiles], # Передаем список путей + output_path=outPath, + output_filename=filename + ) + except (FileNotFoundError, RuntimeError) as e: + # Если FFmpeg не найден или произошла ошибка, сообщаем пользователю + gr.Warning(str(e)) + return "" # Возвращаем пустую строку в текстовое поле + + # Передаем путь к склеенному файлу в FasterWhisper + return self.FasterWhisper.recognize(model, device, compute_type, str(glued_audio_path), beamSize, vadFilter, minSilenceDurationMs, speechPadMs, temp0, temp1, temp2, wordTimestamps, noSpeechThreshold, conditionOnPreviousText) # Функция улучшения текста def generateByCondition(self, api_key, llm_provider, diff --git a/services/fasterWhisper.py b/services/fasterWhisper.py index 67689af..cbe79fc 100644 --- a/services/fasterWhisper.py +++ b/services/fasterWhisper.py @@ -26,6 +26,7 @@ class FasterWhisper: for seg in segments: text += f"[{self.format_timestamp(seg.start)} -> {self.format_timestamp(seg.end)}] {seg.text}" + '\n' + print(f"[{self.format_timestamp(seg.start)} -> {self.format_timestamp(seg.end)}] {seg.text}") return text