diff --git a/app.py b/app.py index 827029c..73abefe 100644 --- a/app.py +++ b/app.py @@ -115,7 +115,13 @@ def main(): recognizeBtn.click( gh.handleRecognizeBtn, +<<<<<<< HEAD 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)], +>>>>>>> 1e5105b7d658310c159c65ba318c08522506c3fb outputs=[recognizedText], ) @@ -123,7 +129,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 +144,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 5ff4d78..20a44db 100644 --- a/handlers/glueAudio.py +++ b/handlers/glueAudio.py @@ -1,17 +1,74 @@ -from pydub import AudioSegment +import subprocess +from pathlib import Path + class GlueAudio(): def glue(self, audio_files: list, output_path: str, output_filename: str) -> Path: """ +<<<<<<< HEAD Склеивает аудиофайлы с помощью FFmpeg, используя промежуточный список файлов. Этот метод чрезвычайно эффективен по памяти и скорости. +======= + Склеивает аудиофайлы РАЗНЫХ форматов с помощью FFmpeg и filter_complex. + Это универсальный и эффективный по памяти метод. +>>>>>>> 1e5105b7d658310c159c65ba318c08522506c3fb Args: audio_files (list): Список путей к исходным аудиофайлам. output_path (str): Директория для сохранения итогового файла. output_filename (str): Имя итогового склеенного файла. +<<<<<<< HEAD Returns: Path: Путь к созданному склеенному файлу. """ +======= + + 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 +>>>>>>> 1e5105b7d658310c159c65ba318c08522506c3fb diff --git a/handlers/gradioHandler.py b/handlers/gradioHandler.py index 25d91c3..5f07b15 100644 --- a/handlers/gradioHandler.py +++ b/handlers/gradioHandler.py @@ -12,6 +12,7 @@ class GradioHandlers: self.llm_factory = llm_factory def handleRecognizeBtn( +<<<<<<< HEAD self, audioFiles, model, device, compute_type, beamSize, vadFilter, minSilenceDurationMs, speechPadMs, temp0, temp1, temp2, wordTimestamps, @@ -27,9 +28,31 @@ class GradioHandlers: # Если FFmpeg не найден или произошла ошибка, сообщаем пользователю gr.Warning(str(e)) return "" # Возвращаем пустую строку в текстовое поле +======= + 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) +>>>>>>> 1e5105b7d658310c159c65ba318c08522506c3fb # Функция улучшения текста - def generateByCondition(self, api_key, llm_provider, llm_model, system_prompt, recognized_text, llm_temperature, is_pipeline_enabled, trigger, isSaveFile, filename, filenamePdf, output_path): + def generateByCondition(self, api_key, llm_provider, + llm_model, system_prompt, recognized_text, + llm_temperature, is_pipeline_enabled, trigger, + isSaveFile, filename, filenamePdf, output_path): try: # Получаем нужный провайдер через фабрику provider = self.llm_factory(llm_provider, api_key) diff --git a/services/fasterWhisper.py b/services/fasterWhisper.py index 7e32d52..cbe79fc 100644 --- a/services/fasterWhisper.py +++ b/services/fasterWhisper.py @@ -1,7 +1,11 @@ from faster_whisper import WhisperModel class FasterWhisper: - def recognize(self, model, device, compute_type, audioFile, beamSize, vadFilter, minSilenceDurationMs, speechPadMs, temp0, temp1, temp2, wordTimestamps, noSpeechThreshold, conditionOnPreviousText): + def recognize(self, model, device, compute_type, + audioFile, beamSize, vadFilter, + minSilenceDurationMs, speechPadMs, + temp0, temp1, temp2, wordTimestamps, + noSpeechThreshold, conditionOnPreviousText): model = WhisperModel(model, device=device, compute_type=compute_type) # Задаем модель segments, _ = model.transcribe( # Распознаем текст @@ -22,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