From 2f7cd32e4f16849241dba57f6724090187641761 Mon Sep 17 00:00:00 2001 From: overcrash <3681221+overcrash66@users.noreply.github.com> Date: Sat, 23 Nov 2024 22:49:22 -0400 Subject: [PATCH 1/5] add web app --- OpenTranslator/translator.py | 193 ++++++++++++++++++++++++++++++ Screenshot.png | Bin 0 -> 63146 bytes WebUI.py | 219 +++++++++++++++++++++++++++++++++++ readme.md | 9 ++ requirements.txt | 3 +- 5 files changed, 423 insertions(+), 1 deletion(-) create mode 100644 OpenTranslator/translator.py create mode 100644 Screenshot.png create mode 100644 WebUI.py diff --git a/OpenTranslator/translator.py b/OpenTranslator/translator.py new file mode 100644 index 0000000..8832e27 --- /dev/null +++ b/OpenTranslator/translator.py @@ -0,0 +1,193 @@ +from transformers import WhisperProcessor, WhisperForConditionalGeneration +import torchaudio +import logging +import librosa +import torch +import sounddevice as sd +from OpenTranslator.sentence_translator import SentenceTranslator +from transformers import MBartForConditionalGeneration, MBart50TokenizerFast +from TTS.api import TTS +import time +from autosub.onlineTranslator import Ctr_Autosub +from gtts import gTTS +import os +import unicodedata + +device = torch.device("cuda" if torch.cuda.is_available() else "cpu") + +class CustomTranslator: + def __init__(self, output_dir="output"): + self.target_language = "" + self.source_language = "" + self.translation_method = "" + self.output_dir = output_dir + os.makedirs(self.output_dir, exist_ok=True) + # Initialize other attributes as needed + + def load_models(self): + self.processor = WhisperProcessor.from_pretrained("openai/whisper-large-v3") + self.model = WhisperForConditionalGeneration.from_pretrained("openai/whisper-large-v3").to(device) + # self.tts = TTS("tts_models/multilingual/multi-dataset/xtts_v2").to(device) + + def process_audio_chunk(self, input_path, target_language, src_language, chunk_idx, output_path, translation_method): + try: + if translation_method == 'Online' or translation_method == 'Hybrid': + print('Starting Online translation!') + src_language = src_language # Assuming src_lang is defined elsewhere + transcripts = Ctr_Autosub.generate_subtitles(source_path=input_path, output=output_path, src_language=src_language) + + print('Transcripts: ' + str(transcripts)) + translator = SentenceTranslator(src=src_language, dst=target_language) + translated = [] + for byte_string in transcripts: + if byte_string is not None: + translated_text = translator(byte_string) + translated.append(translated_text) + + translated_text = ' '.join(translated) + print('Translated text: ' + str(translated_text)) + + Translation_chunk_output_path = os.path.join(self.output_dir, f"{os.path.splitext(os.path.basename(output_path))[0]}_Translation_chunk{chunk_idx + 1}.wav") + print(str(Translation_chunk_output_path)) + if translation_method == 'Hybrid': + self.generate_audio(translated_text, Translation_chunk_output_path, target_language, input_path) + if translation_method == 'Online': + tts = gTTS(translated_text, lang=target_language, slow=False) + tts.save(Translation_chunk_output_path) + return translated_text + + if translation_method == 'Local': + self.load_models() + start_time = time.time() + # Load input audio file using librosa + input_waveform, input_sampling_rate = librosa.load(input_path, sr=None, mono=True) + + # Convert NumPy array to PyTorch tensor if needed + if not isinstance(input_waveform, torch.Tensor): + input_waveform = torch.tensor(input_waveform) + + forced_decoder_ids = self.processor.get_decoder_prompt_ids(language=target_language, task="translate") + + # Ensure the input audio has a proper frame rate + if input_sampling_rate != 16000: + resampler = torchaudio.transforms.Resample(orig_freq=input_sampling_rate, new_freq=16000) + input_waveform = resampler(input_waveform) + + # Process the input audio with the processor + input_features = self.processor(input_waveform.numpy(), sampling_rate=16000, return_tensors="pt") + + # Move input features to the device used by the model + input_features = {k: v.to(device) for k, v in input_features.items()} + + # Generate token ids + predicted_ids = self.model.generate(input_features["input_features"], forced_decoder_ids=forced_decoder_ids) + + # Decode token ids to text + transcription = self.processor.batch_decode(predicted_ids, skip_special_tokens=True)[0] + + del input_waveform, input_sampling_rate + + end_time = time.time() + execution_time = (end_time - start_time) / 60 + print(f"Transcription Execution time: {execution_time:.2f} minutes") + + # Fix a bug: Text validation check if we have duplicate successive words + words = transcription.split() + cleaned_words = [words[0]] + + for word in words[1:]: + if word != cleaned_words[-1]: + cleaned_words.append(word) + + cleaned_str = ' '.join(cleaned_words) + + transcription = cleaned_str + + # Fix duplicate successive sentences + sentences = transcription.split('.') + cleaned_sentences = [sentences[0]] + + for sentence in sentences[1:]: + if sentence != cleaned_sentences[-1]: + cleaned_sentences.append(sentence) + + cleaned_transcription = '.'.join(cleaned_sentences) + + transcription = cleaned_transcription + print('Speech recognition and translate to English text: ' + str(transcription)) + + Translation_chunk_output_path = os.path.join(self.output_dir, f"{os.path.splitext(os.path.basename(output_path))[0]}_Translation_chunk{chunk_idx + 1}.wav") + + # If target language is English, skip text translation + if target_language != 'en': + # Local text translation + print("Local text translation started..") + start_time = time.time() + tt = MBartForConditionalGeneration.from_pretrained("SnypzZz/Llama2-13b-Language-translate").to(device) + tokenizer = MBart50TokenizerFast.from_pretrained("SnypzZz/Llama2-13b-Language-translate", src_lang="en_XX", device=device) + + # Tokenize and convert to PyTorch tensor + inputs = tokenizer(transcription, return_tensors="pt") + input_ids = inputs["input_ids"].to(device) + + # Map target languages to model language codes + language_mapping = { + "en": "en_XX", + "es": "es_XX", + "fr": "fr_XX", + "de": "de_DE", + "ja": "ja_XX", + "ko": "ko_KR", + "tr": "tr_TR", + "ar": "ar_AR", + "ru": "ru_RU", + "he": "he_IL", + "hi": "hi_IN", + "it": "it_IT", + "pt": "pt_XX", + "zh": "zh_CN", + "cs": "cs_CZ", + "nl": "nl_XX", + "pl": "pl_PL", + } + + # Set the target language based on the mapping + model_target_language = language_mapping.get(target_language, "en_XX") + + # Generate tokens on the GPU + generated_tokens = tt.generate(input_ids=input_ids, forced_bos_token_id=tokenizer.lang_code_to_id[model_target_language]) + + # Decode and join the translated text + translated_text = tokenizer.batch_decode(generated_tokens, skip_special_tokens=True) + translated_text = ", ".join(translated_text) + + logging.info(f"Processing successful. Translated text: {translated_text}") + end_time = time.time() + execution_time = (end_time - start_time) / 60 + print(f"Local Translation Execution time: {execution_time:.2f} minutes") + + if target_language == 'en': + translated_text = transcription + + # Generate final audio output from translated text + self.generate_audio(translated_text, Translation_chunk_output_path, target_language, input_path) + + # Log success + logging.info(f"Translation successful for {input_path}. Translated text: {transcription}") + return translated_text + + except Exception as e: + # Log errors + logging.error(f"Error processing audio: {e}") + raise # Re-raise the exception + + def generate_audio(self, text, output_path, target_language, input_path): + print("Generate audio") + + # Text to speech to a file + start_time = time.time() + self.tts = TTS("tts_models/multilingual/multi-dataset/xtts_v2").to(device) + self.tts.tts_to_file(text=text, speaker_wav=input_path, language=target_language, file_path=output_path) + end_time = time.time() + execution_time = (end_time - start_time) / 60 + print(f"Generate_audio Execution time: {execution_time:.2f} minutes") diff --git a/Screenshot.png b/Screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..635f406fc83c87cc5796d50694836a739c5bb08d GIT binary patch literal 63146 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 MTyDPl=I+CP2l|P`EC2ui literal 0 HcmV?d00001 diff --git a/WebUI.py b/WebUI.py new file mode 100644 index 0000000..09f1496 --- /dev/null +++ b/WebUI.py @@ -0,0 +1,219 @@ +import gradio as gr +import os +import subprocess +import threading +import webbrowser +from pydub import AudioSegment +from pydub.utils import mediainfo +from OpenTranslator.translator import CustomTranslator +import unicodedata +import librosa + +current_dir = os.path.dirname(os.path.abspath(__file__)) +# Initialize the translator instance with an output directory +output_dir = os.path.join(current_dir, "output") + +translator_instance = CustomTranslator(output_dir=output_dir) + +# Define the languages dictionary +languages = { + "English": "en", + "Spanish": "es", + "French": "fr", + "German": "de", + "Japanese": "ja", + "Korean": "ko", + "Turkish": "tr", + "Arabic": "ar", + "Russian": "ru", + "Hebrew": "he", + "Hindi": "hi", + "Italian": "it", + "Portuguese": "pt", + "Chinese (Mandarin)": "zh", + "Czech": "cs", + "Dutch": "nl", + "Polish": "pl" +} + +# Define the source languages dictionary +Src_lang = { + "English": "en", + "Spanish": "es", + "French": "fr", + "German": "de", + "Japanese": "ja", + "Korean": "ko", + "Turkish": "tr", + "Arabic": "ar", + "Russian": "ru", + "Hebrew": "he", + "Hindi": "hi", + "Italian": "it", + "Portuguese": "pt", + "Chinese (Mandarin)": "zh", + "Czech": "cs", + "Dutch": "nl", + "Polish": "pl" +} + +# Define the translation options +TextTranslationOption = ["Local", "Online", "Hybrid"] + +# Function to handle file uploads +def upload_file(file): + global audio_path + audio_path = file.name + return f"Selected File Title: {os.path.basename(audio_path)}" + +# Function to run the translation process +def run_translation(translation_method, source_lang, target_lang): + output_path = os.path.join(output_dir, f"{os.path.splitext(os.path.basename(audio_path))[0]}_translated.mp3") + input_file = audio_path + print(audio_path) + input_duration = get_audio_duration(input_file) + print('input_duration: '+str(input_duration)) + if input_duration > 30: + max_chunk_duration = 30 + num_chunks = int(input_duration / max_chunk_duration) + chunk_files = [] + Translation_chunk_files = [] + + for chunk_idx in range(num_chunks): + print('duration more then 30- num_chunks: '+str(num_chunks)) + print('duration more then 30- chunk_idx'+str(chunk_idx)) + start_time = chunk_idx * max_chunk_duration + end_time = min((chunk_idx + 1) * max_chunk_duration, input_duration) + chunk_output_path = os.path.join(output_dir, f"{os.path.splitext(os.path.basename(output_path))[0]}_chunk{chunk_idx + 1}.wav") + + split_audio_chunk(input_file, chunk_output_path, start_time, end_time) + + try: + translation_result = translator_instance.process_audio_chunk(chunk_output_path, + target_lang, source_lang, + chunk_idx, output_path, translation_method) + except Exception as e: + print(f"{e}") + return "An Error occurred!" + + chunk_files.append(chunk_output_path) + Translation_chunk_output_path = os.path.join(output_dir, f"{os.path.splitext(os.path.basename(output_path))[0]}_Translation_chunk{chunk_idx + 1}.wav") + #Translation_chunk_output_path = os.path.join(output_dir, f"w_Translation_chunk{chunk_idx + 1}.wav") + Translation_chunk_files.append(Translation_chunk_output_path) + + final_output_path = os.path.join(output_dir, f"{os.path.splitext(os.path.basename(output_path))[0]}-temp.wav") + + if translation_method == 'Local' or translation_method == 'Hybrid': + merge_audio_files(Translation_chunk_files, final_output_path) + if translation_method == 'Online': + merge_online_audio_files(Translation_chunk_files, final_output_path) + + subprocess.run(['ffmpeg', '-i', final_output_path, '-codec:a', 'libmp3lame', output_path], check=True) + os.remove(final_output_path) + + delete_chunk_files(chunk_files) + delete_chunk_files(Translation_chunk_files) + chunk_files = [] # List to store individual chunk files + Translation_chunk_files = [] + + return 'Translation complete'+ ' file saved to: ' +str(output_path) + + if input_duration <= 30 and translation_method == 'Local': + + chunk_output_path = input_file + chunk_idx = 0 + print('duration less then 30') + try: + translation_result = translator_instance.process_audio_chunk(chunk_output_path, + target_lang, source_lang, + chunk_idx, output_path, translation_method) + except Exception as e: + print(f"{e}") + return "An Error occurred!" + + Translation_chunk_output_path = os.path.join(output_dir, f"{os.path.splitext(os.path.basename(output_path))[0]}_Translation_chunk1.wav") + + subprocess.run(['ffmpeg', '-i', Translation_chunk_output_path, '-codec:a', 'libmp3lame', output_path], check=True) + os.remove(Translation_chunk_output_path) + + return 'Translation complete' + ' file saved to: ' +str(output_path) + + if input_duration <= 30 and (translation_method == 'Online' or translation_method == 'Hybrid'): + return "For online translation: you need to use an audio file longer than 30 sec!" + +# Function to split audio into a chunk using ffmpeg +def split_audio_chunk(input_path, output_path, start_time, end_time): + ffmpeg_cmd = f'ffmpeg -i "{input_path}" -ss {start_time} -to {end_time} -c copy "{output_path}"' + subprocess.call(ffmpeg_cmd, shell=True) + +# Function to get the duration of an audio file +def get_audio_duration(file_path): + audio_info = librosa.get_duration(filename=file_path) + duration_seconds = audio_info + return duration_seconds + +# Function to merge audio files +def merge_audio_files(input_files, output_file): + merged_audio = AudioSegment.silent(duration=0) + for input_file in input_files: + try: + chunk_audio = AudioSegment.from_file(input_file, format="wav") + merged_audio += chunk_audio + except FileNotFoundError as e: + print(f"Error merging audio file {input_file}: {e}") + except Exception as e: + print(f"Error merging audio file {input_file}: {e}") + merged_audio.export(output_file, format="wav") + +# Function to merge online audio files +def merge_online_audio_files(input_files, output_file): + merged_audio = AudioSegment.silent(duration=0) + for input_file in input_files: + try: + chunk_audio = AudioSegment.from_file(input_file, format="mp3") + merged_audio += chunk_audio + except FileNotFoundError as e: + print(f"Error merging audio file {input_file}: {e}") + except Exception as e: + print(f"Error merging audio file {input_file}: {e}") + merged_audio.export(output_file, format="mp3") + +# Function to delete chunk files +def delete_chunk_files(files): + for file in files: + try: + os.remove(file) + except FileNotFoundError as e: + print(f"Error deleting file {file}: {e}") + except Exception as e: + print(f"Error deleting file {file}: {e}") + + +# Define the Gradio interface +with gr.Blocks() as demo: + gr.Markdown("# Open Translator") + + with gr.Row(): + with gr.Column(): + gr.Markdown("## Select Translation Method:") + translation_method = gr.Dropdown(choices=TextTranslationOption, value=TextTranslationOption[0], label="Translation Method") + + gr.Markdown("## For online or hybrid translation: Select Source Audio file Language:") + source_lang = gr.Dropdown(choices=list(Src_lang.values()), value="he", label="Source Language") + + gr.Markdown("## Select Audio File:") + audio_file = gr.File(type="filepath", label="Upload Audio File") + file_title = gr.Textbox(label="Selected File Title") + audio_file.upload(upload_file, audio_file, file_title) + + gr.Markdown("## Select Target Language:") + target_lang = gr.Dropdown(choices=list(languages.values()), value="ar", label="Target Language") + + translate_button = gr.Button("translate") + + with gr.Column(): + translated_text = gr.Textbox(label="", lines=20, interactive=False) + + translate_button.click(run_translation, inputs=[translation_method, source_lang, target_lang], outputs=translated_text) + +demo.launch() \ No newline at end of file diff --git a/readme.md b/readme.md index 300a5e4..dd68c95 100644 --- a/readme.md +++ b/readme.md @@ -88,10 +88,19 @@ pip install -r requirements.txt python OpenTranslator.py ``` +Or audio file translation only using a WEB UI (gradio) + +``` +python WebUI.py + +``` + ## GUI Preview ![Redesigned (Custom)](Screenshot2.png) +![Web](Screenshot.png) + ## Configuration - You can customize the translation models and other settings by modifying the script. diff --git a/requirements.txt b/requirements.txt index 9292364..d67f18a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -16,4 +16,5 @@ cutlet mecab unidic-lite google-api-python-client -gtts \ No newline at end of file +gtts +gradio \ No newline at end of file From cab90f6a401c76807febeead35c67bf65457847e Mon Sep 17 00:00:00 2001 From: overcrash <3681221+overcrash66@users.noreply.github.com> Date: Sat, 23 Nov 2024 22:52:11 -0400 Subject: [PATCH 2/5] Update Screenshot2.png --- Screenshot2.png | Bin 18674 -> 19488 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Screenshot2.png b/Screenshot2.png index d70208b646f47671f5864bc788a301a86b0c81be..73762314b7065252b566661eb8ad10999b968203 100644 GIT binary patch literal 19488 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 literal 18674 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 Date: Sat, 23 Nov 2024 23:02:27 -0400 Subject: [PATCH 3/5] Update WebUI.py --- WebUI.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WebUI.py b/WebUI.py index 09f1496..f5205b6 100644 --- a/WebUI.py +++ b/WebUI.py @@ -216,4 +216,4 @@ def delete_chunk_files(files): translate_button.click(run_translation, inputs=[translation_method, source_lang, target_lang], outputs=translated_text) -demo.launch() \ No newline at end of file +demo.launch(server_name="127.0.0.2", server_port=7861) \ No newline at end of file From c4a7098a783b211ba463ccdc8b84a2157557b0c1 Mon Sep 17 00:00:00 2001 From: overcrash <3681221+overcrash66@users.noreply.github.com> Date: Sat, 23 Nov 2024 23:40:26 -0400 Subject: [PATCH 4/5] remove online and Hybrid translation from web UI --- OpenTranslator/translator.py | 30 +------------------- WebUI.py | 54 ++++-------------------------------- readme.md | 4 ++- 3 files changed, 10 insertions(+), 78 deletions(-) diff --git a/OpenTranslator/translator.py b/OpenTranslator/translator.py index 8832e27..7426ce4 100644 --- a/OpenTranslator/translator.py +++ b/OpenTranslator/translator.py @@ -4,12 +4,9 @@ import librosa import torch import sounddevice as sd -from OpenTranslator.sentence_translator import SentenceTranslator from transformers import MBartForConditionalGeneration, MBart50TokenizerFast from TTS.api import TTS import time -from autosub.onlineTranslator import Ctr_Autosub -from gtts import gTTS import os import unicodedata @@ -29,33 +26,8 @@ def load_models(self): self.model = WhisperForConditionalGeneration.from_pretrained("openai/whisper-large-v3").to(device) # self.tts = TTS("tts_models/multilingual/multi-dataset/xtts_v2").to(device) - def process_audio_chunk(self, input_path, target_language, src_language, chunk_idx, output_path, translation_method): + def process_audio_chunk(self, input_path, target_language, chunk_idx, output_path, translation_method): try: - if translation_method == 'Online' or translation_method == 'Hybrid': - print('Starting Online translation!') - src_language = src_language # Assuming src_lang is defined elsewhere - transcripts = Ctr_Autosub.generate_subtitles(source_path=input_path, output=output_path, src_language=src_language) - - print('Transcripts: ' + str(transcripts)) - translator = SentenceTranslator(src=src_language, dst=target_language) - translated = [] - for byte_string in transcripts: - if byte_string is not None: - translated_text = translator(byte_string) - translated.append(translated_text) - - translated_text = ' '.join(translated) - print('Translated text: ' + str(translated_text)) - - Translation_chunk_output_path = os.path.join(self.output_dir, f"{os.path.splitext(os.path.basename(output_path))[0]}_Translation_chunk{chunk_idx + 1}.wav") - print(str(Translation_chunk_output_path)) - if translation_method == 'Hybrid': - self.generate_audio(translated_text, Translation_chunk_output_path, target_language, input_path) - if translation_method == 'Online': - tts = gTTS(translated_text, lang=target_language, slow=False) - tts.save(Translation_chunk_output_path) - return translated_text - if translation_method == 'Local': self.load_models() start_time = time.time() diff --git a/WebUI.py b/WebUI.py index f5205b6..69dd3b3 100644 --- a/WebUI.py +++ b/WebUI.py @@ -36,29 +36,8 @@ "Polish": "pl" } -# Define the source languages dictionary -Src_lang = { - "English": "en", - "Spanish": "es", - "French": "fr", - "German": "de", - "Japanese": "ja", - "Korean": "ko", - "Turkish": "tr", - "Arabic": "ar", - "Russian": "ru", - "Hebrew": "he", - "Hindi": "hi", - "Italian": "it", - "Portuguese": "pt", - "Chinese (Mandarin)": "zh", - "Czech": "cs", - "Dutch": "nl", - "Polish": "pl" -} - # Define the translation options -TextTranslationOption = ["Local", "Online", "Hybrid"] +TextTranslationOption = ["Local"] # Function to handle file uploads def upload_file(file): @@ -67,7 +46,7 @@ def upload_file(file): return f"Selected File Title: {os.path.basename(audio_path)}" # Function to run the translation process -def run_translation(translation_method, source_lang, target_lang): +def run_translation(translation_method, target_lang): output_path = os.path.join(output_dir, f"{os.path.splitext(os.path.basename(audio_path))[0]}_translated.mp3") input_file = audio_path print(audio_path) @@ -90,7 +69,7 @@ def run_translation(translation_method, source_lang, target_lang): try: translation_result = translator_instance.process_audio_chunk(chunk_output_path, - target_lang, source_lang, + target_lang, chunk_idx, output_path, translation_method) except Exception as e: print(f"{e}") @@ -103,10 +82,8 @@ def run_translation(translation_method, source_lang, target_lang): final_output_path = os.path.join(output_dir, f"{os.path.splitext(os.path.basename(output_path))[0]}-temp.wav") - if translation_method == 'Local' or translation_method == 'Hybrid': + if translation_method == 'Local': merge_audio_files(Translation_chunk_files, final_output_path) - if translation_method == 'Online': - merge_online_audio_files(Translation_chunk_files, final_output_path) subprocess.run(['ffmpeg', '-i', final_output_path, '-codec:a', 'libmp3lame', output_path], check=True) os.remove(final_output_path) @@ -125,7 +102,7 @@ def run_translation(translation_method, source_lang, target_lang): print('duration less then 30') try: translation_result = translator_instance.process_audio_chunk(chunk_output_path, - target_lang, source_lang, + target_lang, chunk_idx, output_path, translation_method) except Exception as e: print(f"{e}") @@ -138,9 +115,6 @@ def run_translation(translation_method, source_lang, target_lang): return 'Translation complete' + ' file saved to: ' +str(output_path) - if input_duration <= 30 and (translation_method == 'Online' or translation_method == 'Hybrid'): - return "For online translation: you need to use an audio file longer than 30 sec!" - # Function to split audio into a chunk using ffmpeg def split_audio_chunk(input_path, output_path, start_time, end_time): ffmpeg_cmd = f'ffmpeg -i "{input_path}" -ss {start_time} -to {end_time} -c copy "{output_path}"' @@ -165,19 +139,6 @@ def merge_audio_files(input_files, output_file): print(f"Error merging audio file {input_file}: {e}") merged_audio.export(output_file, format="wav") -# Function to merge online audio files -def merge_online_audio_files(input_files, output_file): - merged_audio = AudioSegment.silent(duration=0) - for input_file in input_files: - try: - chunk_audio = AudioSegment.from_file(input_file, format="mp3") - merged_audio += chunk_audio - except FileNotFoundError as e: - print(f"Error merging audio file {input_file}: {e}") - except Exception as e: - print(f"Error merging audio file {input_file}: {e}") - merged_audio.export(output_file, format="mp3") - # Function to delete chunk files def delete_chunk_files(files): for file in files: @@ -198,9 +159,6 @@ def delete_chunk_files(files): gr.Markdown("## Select Translation Method:") translation_method = gr.Dropdown(choices=TextTranslationOption, value=TextTranslationOption[0], label="Translation Method") - gr.Markdown("## For online or hybrid translation: Select Source Audio file Language:") - source_lang = gr.Dropdown(choices=list(Src_lang.values()), value="he", label="Source Language") - gr.Markdown("## Select Audio File:") audio_file = gr.File(type="filepath", label="Upload Audio File") file_title = gr.Textbox(label="Selected File Title") @@ -214,6 +172,6 @@ def delete_chunk_files(files): with gr.Column(): translated_text = gr.Textbox(label="", lines=20, interactive=False) - translate_button.click(run_translation, inputs=[translation_method, source_lang, target_lang], outputs=translated_text) + translate_button.click(run_translation, inputs=[translation_method, target_lang], outputs=translated_text) demo.launch(server_name="127.0.0.2", server_port=7861) \ No newline at end of file diff --git a/readme.md b/readme.md index dd68c95..9ab61ac 100644 --- a/readme.md +++ b/readme.md @@ -88,7 +88,9 @@ pip install -r requirements.txt python OpenTranslator.py ``` -Or audio file translation only using a WEB UI (gradio) +Or Local mode (using a set of LLM's) for audio file translation only, using a WEB UI (Gradio) + +Please note Web UI is in beta and still in active development ``` python WebUI.py From 44ce56a788270c0f9c47d855bd2966917f3e9c08 Mon Sep 17 00:00:00 2001 From: overcrash <3681221+overcrash66@users.noreply.github.com> Date: Sun, 24 Nov 2024 16:12:20 -0400 Subject: [PATCH 5/5] complete the web UI features and design --- WebUI.py | 42 +++++++++++++++++++++++++++--------------- readme.md | 2 -- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/WebUI.py b/WebUI.py index 69dd3b3..ca31a49 100644 --- a/WebUI.py +++ b/WebUI.py @@ -36,6 +36,8 @@ "Polish": "pl" } +language_choices = [(lang, code) for lang, code in languages.items()] + # Define the translation options TextTranslationOption = ["Local"] @@ -43,7 +45,7 @@ def upload_file(file): global audio_path audio_path = file.name - return f"Selected File Title: {os.path.basename(audio_path)}" + #return f"Selected File Title: {os.path.basename(audio_path)}" # Function to run the translation process def run_translation(translation_method, target_lang): @@ -57,7 +59,8 @@ def run_translation(translation_method, target_lang): num_chunks = int(input_duration / max_chunk_duration) chunk_files = [] Translation_chunk_files = [] - + translated_text = [] + for chunk_idx in range(num_chunks): print('duration more then 30- num_chunks: '+str(num_chunks)) print('duration more then 30- chunk_idx'+str(chunk_idx)) @@ -74,10 +77,12 @@ def run_translation(translation_method, target_lang): except Exception as e: print(f"{e}") return "An Error occurred!" + + translated_text.append(translation_result) chunk_files.append(chunk_output_path) Translation_chunk_output_path = os.path.join(output_dir, f"{os.path.splitext(os.path.basename(output_path))[0]}_Translation_chunk{chunk_idx + 1}.wav") - #Translation_chunk_output_path = os.path.join(output_dir, f"w_Translation_chunk{chunk_idx + 1}.wav") + Translation_chunk_files.append(Translation_chunk_output_path) final_output_path = os.path.join(output_dir, f"{os.path.splitext(os.path.basename(output_path))[0]}-temp.wav") @@ -93,10 +98,11 @@ def run_translation(translation_method, target_lang): chunk_files = [] # List to store individual chunk files Translation_chunk_files = [] - return 'Translation complete'+ ' file saved to: ' +str(output_path) + translation_result = ', '.join(translated_text) + return translation_result, output_path if input_duration <= 30 and translation_method == 'Local': - + #translated_text = [] chunk_output_path = input_file chunk_idx = 0 print('duration less then 30') @@ -108,12 +114,13 @@ def run_translation(translation_method, target_lang): print(f"{e}") return "An Error occurred!" + #translated_text.append(translated_text) Translation_chunk_output_path = os.path.join(output_dir, f"{os.path.splitext(os.path.basename(output_path))[0]}_Translation_chunk1.wav") subprocess.run(['ffmpeg', '-i', Translation_chunk_output_path, '-codec:a', 'libmp3lame', output_path], check=True) os.remove(Translation_chunk_output_path) - - return 'Translation complete' + ' file saved to: ' +str(output_path) + + return translation_result, output_path # Function to split audio into a chunk using ffmpeg def split_audio_chunk(input_path, output_path, start_time, end_time): @@ -149,6 +156,8 @@ def delete_chunk_files(files): except Exception as e: print(f"Error deleting file {file}: {e}") +def upload_audio(audio_file): + return audio_file # Define the Gradio interface with gr.Blocks() as demo: @@ -156,22 +165,25 @@ def delete_chunk_files(files): with gr.Row(): with gr.Column(): - gr.Markdown("## Select Translation Method:") + #gr.Markdown("## Select Translation Method:") translation_method = gr.Dropdown(choices=TextTranslationOption, value=TextTranslationOption[0], label="Translation Method") gr.Markdown("## Select Audio File:") audio_file = gr.File(type="filepath", label="Upload Audio File") - file_title = gr.Textbox(label="Selected File Title") - audio_file.upload(upload_file, audio_file, file_title) + audio_player = gr.Audio(label="Audio Player", interactive=True) + + #file_title = gr.Textbox(label="Selected File Title") + audio_file.upload(upload_file, audio_file) + audio_file.change(upload_audio, audio_file, audio_player) gr.Markdown("## Select Target Language:") - target_lang = gr.Dropdown(choices=list(languages.values()), value="ar", label="Target Language") - + target_lang = gr.Dropdown(choices=language_choices, value="ar", label="Target Language") + #print(target_lang) translate_button = gr.Button("translate") with gr.Column(): - translated_text = gr.Textbox(label="", lines=20, interactive=False) - - translate_button.click(run_translation, inputs=[translation_method, target_lang], outputs=translated_text) + translated_text = gr.Textbox(label="Translated text", lines=20, interactive=False) + audio_output = gr.Audio(label="Translated Audio") + translate_button.click(run_translation, inputs=[translation_method, target_lang], outputs=[translated_text, audio_output]) demo.launch(server_name="127.0.0.2", server_port=7861) \ No newline at end of file diff --git a/readme.md b/readme.md index 9ab61ac..3f94f75 100644 --- a/readme.md +++ b/readme.md @@ -90,8 +90,6 @@ python OpenTranslator.py Or Local mode (using a set of LLM's) for audio file translation only, using a WEB UI (Gradio) -Please note Web UI is in beta and still in active development - ``` python WebUI.py