Farit Shamardanov commited on
Commit
fde8fc4
·
1 Parent(s): ec3a891

Add application file

Browse files
Files changed (2) hide show
  1. app.py +246 -0
  2. requirements.txt +7 -0
app.py ADDED
@@ -0,0 +1,246 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from shutil import which
2
+ import gradio as gr
3
+ from transformers import pipeline
4
+ from moviepy.editor import VideoFileClip, AudioFileClip, concatenate_audioclips
5
+ from TTS.api import TTS # Coqui TTS
6
+ import librosa
7
+ import soundfile as sf
8
+ import os
9
+ import nltk
10
+ import torch
11
+ from pydub import AudioSegment
12
+
13
+ nltk.download('punkt')
14
+ nltk.download('punkt_tab')
15
+
16
+ device = 0 if torch.cuda.is_available() else -1 # Использовать GPU (0) или CPU (-1)
17
+ print("Используемый девайс:", "GPU" if device == 0 else "CPU")
18
+
19
+ # Удаление мата из текста
20
+ def detect_profanity_with_transformer(text):
21
+ profanity_detector = pipeline("text-classification", model="cardiffnlp/twitter-roberta-base-offensive", device=device)
22
+ words = text.split()
23
+ cleaned_words = []
24
+
25
+ for word in words:
26
+ result = profanity_detector(word)
27
+ if any(label["label"] == "OFFENSIVE" and label["score"] > 0.8 for label in result):
28
+ cleaned_words.append("***") # Заменяем мат на звездочки
29
+ else:
30
+ cleaned_words.append(word)
31
+
32
+ return " ".join(cleaned_words)
33
+
34
+ # Функция для извлечения аудио из видео
35
+ def extract_audio_from_video(video_path, audio_path="temp_audio.wav"):
36
+ video = VideoFileClip(video_path)
37
+ video.audio.write_audiofile(audio_path)
38
+ return audio_path
39
+
40
+ # Получение транскрипции и временных меток
41
+ def get_transcription_with_timestamps(audio_path):
42
+ asr = pipeline("automatic-speech-recognition", model="openai/whisper-large-v2", device=device)
43
+ result = asr(audio_path, return_timestamps=True)
44
+ transcription = result["text"]
45
+ timestamps = result["chunks"] # Содержит временные метки для каждого слова или фрагмента
46
+ return transcription, timestamps
47
+
48
+ # Разбиение текста на фрагменты по временным меткам
49
+ def split_text_by_timestamps(timestamps):
50
+ text_fragments = []
51
+ for chunk in timestamps:
52
+ # Проверяем наличие ключа 'timestamp' и корректности данных
53
+ if "timestamp" in chunk and "text" in chunk:
54
+ start_time, end_time = chunk["timestamp"]
55
+
56
+ # Игнорируем фрагменты с отсутствующими временными метками
57
+ if start_time is None or end_time is None:
58
+ continue
59
+
60
+ fragment_text = chunk["text"]
61
+
62
+ # Добавляем только непустые текстовые фрагменты
63
+ if fragment_text.strip():
64
+ text_fragments.append({
65
+ "start": start_time,
66
+ "end": end_time,
67
+ "text": fragment_text.strip()
68
+ })
69
+
70
+ return text_fragments
71
+
72
+ # Перевод текста
73
+ def translate_text_with_transformer(text, source_lang="ru", target_lang="en"):
74
+ translator = pipeline("translation", model="facebook/m2m100_418M", device=device)
75
+ translated_result = translator(text, src_lang=source_lang, tgt_lang=target_lang)
76
+ return translated_result[0]["translation_text"]
77
+
78
+ # Синтез аудио с учетом временных меток и синхронизация с видео
79
+ def synthesize_audio_with_timestamps(original_audio_path, text_fragments, output_audio_path):
80
+ from TTS.api import TTS
81
+ from pydub import AudioSegment
82
+ import os
83
+ import torch
84
+
85
+ tts = TTS(model_name="tts_models/multilingual/multi-dataset/xtts_v2", gpu=torch.cuda.is_available())
86
+ generated_clips = []
87
+
88
+ for fragment in text_fragments:
89
+ temp_audio_path = "temp_fragment.wav"
90
+ tts.tts_to_file(
91
+ text=fragment["text"],
92
+ file_path=temp_audio_path,
93
+ speaker_wav=original_audio_path,
94
+ language="en"
95
+ )
96
+ audio_segment = AudioSegment.from_file(temp_audio_path)
97
+
98
+ # Подгоняем длину аудио фрагмента к заданным временным рамкам
99
+ duration = fragment["end"] - fragment["start"]
100
+
101
+ # Проверка на нулевую или отрицательную длительность фрагмента
102
+ if duration <= 0:
103
+ print(f"Warning: duration is zero or negative for fragment: {fragment['text']}")
104
+ os.remove(temp_audio_path)
105
+ continue
106
+
107
+ audio_duration = len(audio_segment) / 1000 # Длительность в секундах
108
+
109
+ # Проверка на нулевую длительность аудио
110
+ if audio_duration <= 0:
111
+ print(f"Warning: audio duration is zero or negative for fragment: {fragment['text']}")
112
+ os.remove(temp_audio_path)
113
+ continue
114
+
115
+ # Корректировка длительности аудио
116
+ speed_factor = duration / audio_duration
117
+ if audio_duration < duration:
118
+ # Ускорение аудио
119
+ if speed_factor > 1e-6:
120
+ audio_segment = audio_segment.speedup(playback_speed=speed_factor)
121
+ else:
122
+ print(f"Warning: speed_factor is too small for fragment: {fragment['text']}")
123
+ os.remove(temp_audio_path)
124
+ continue
125
+ elif audio_duration > duration:
126
+ # Замедление аудио
127
+ if speed_factor > 1e-6:
128
+ audio_segment = audio_segment.speedup(playback_speed=1/speed_factor)
129
+ else:
130
+ print(f"Warning: speed_factor is too small for fragment: {fragment['text']}")
131
+ os.remove(temp_audio_path)
132
+ continue
133
+
134
+ # Проверка на слишком короткое аудио после изменения скорости
135
+ if len(audio_segment) == 0:
136
+ print(f"Warning: Audio segment became empty after speed adjustment for fragment: {fragment['text']}")
137
+ os.remove(temp_audio_path)
138
+ continue
139
+
140
+ generated_clips.append(audio_segment)
141
+ os.remove(temp_audio_path)
142
+
143
+ # Объединение всех фрагментов
144
+ if generated_clips:
145
+ final_audio = sum(generated_clips)
146
+ final_audio.export(output_audio_path, format="wav")
147
+ else:
148
+ print("No valid audio fragments to process.")
149
+
150
+ # Синтез аудио с учетом временных меток без замедления
151
+ def synthesize_audio_with_timestamps_simple(original_audio_path, text_fragments, output_audio_path):
152
+ tts = TTS(model_name="tts_models/multilingual/multi-dataset/xtts_v2", gpu=torch.cuda.is_available())
153
+ generated_clips = []
154
+
155
+ for fragment in text_fragments:
156
+ temp_audio_path = "temp_fragment.wav"
157
+ tts.tts_to_file(
158
+ text=fragment["text"],
159
+ file_path=temp_audio_path,
160
+ speaker_wav=original_audio_path,
161
+ language="en"
162
+ )
163
+ audio_segment = AudioSegment.from_file(temp_audio_path)
164
+
165
+ # Подгоняем длину аудио фрагмента к заданным временным рамкам
166
+ duration = fragment["end"] - fragment["start"]
167
+ audio_segment = audio_segment[:int(duration * 1000)] # Приводим к миллисекундам
168
+ generated_clips.append(audio_segment)
169
+ os.remove(temp_audio_path)
170
+
171
+ # Объединение всех фрагментов
172
+ final_audio = sum(generated_clips)
173
+ final_audio.export(output_audio_path, format="wav")
174
+
175
+ # Объединение видео с новым аудио
176
+ def synchronize_video_with_audio(video_path, audio_path, output_path):
177
+ video = VideoFileClip(video_path)
178
+ audio = AudioFileClip(audio_path)
179
+ video = video.set_audio(audio)
180
+ video.write_videofile(output_path, codec="libx264", audio_codec="aac")
181
+
182
+ # Основной процесс
183
+ def translate_video_with_sync(video_path, output_path, source_lang="ru", target_lang="en"):
184
+ # Извлечение аудио из видео
185
+ audio_path = extract_audio_from_video(video_path)
186
+
187
+ # Получение транскрипции и временных меток
188
+ transcription, timestamps = get_transcription_with_timestamps(audio_path)
189
+ print("Распознанный текст:", transcription)
190
+
191
+ # Удаление мата из текста
192
+ cleaned_transcription = detect_profanity_with_transformer(transcription)
193
+ print("Очищенный текст:", cleaned_transcription)
194
+
195
+ # Перевод текста
196
+ translated_text = translate_text_with_transformer(cleaned_transcription, source_lang, target_lang)
197
+ print("Переведенный текст:", translated_text)
198
+
199
+ # Разбиение текста по временным меткам
200
+ text_fragments = split_text_by_timestamps(timestamps)
201
+
202
+ # Обновляем текст фрагментов с переводом
203
+ for fragment in text_fragments:
204
+ cleaned_text = detect_profanity_with_transformer(fragment["text"])
205
+ fragment["text"] = translate_text_with_transformer(cleaned_text, source_lang, target_lang)
206
+
207
+ # Генерация синхронизированного аудио
208
+ synthesized_audio_path = "synchronized_audio.wav"
209
+ synthesize_audio_with_timestamps_simple(audio_path, text_fragments, synthesized_audio_path)
210
+
211
+ # Объединение видео с новым аудио
212
+ synchronize_video_with_audio(video_path, synthesized_audio_path, output_path)
213
+
214
+ # Удаление временных файлов
215
+ os.remove(audio_path)
216
+ os.remove(synthesized_audio_path)
217
+
218
+ print(f"Переведенное видео сохранено в {output_path}")
219
+
220
+ # Обёртка для функции `translate_video_with_sync`, чтобы она работала с Gradio
221
+ def process_video(video_file, source_lang, target_lang):
222
+ input_path = video_file.name
223
+ output_path = "translated_video.mp4"
224
+
225
+ # Вызов основной функции
226
+ translate_video_with_sync(video_path=input_path, output_path=output_path, source_lang=source_lang, target_lang=target_lang)
227
+
228
+ # Возврат результата
229
+ return output_path
230
+
231
+ # Интерфейс Gradio
232
+ interface = gr.Interface(
233
+ fn=process_video,
234
+ inputs=[
235
+ gr.File(label="Upload Video", file_types=[".mp4", ".mkv", ".avi"]), # Загрузка видео
236
+ gr.Textbox(label="Source Language (e.g., 'ru')", value="ru"), # Исходный язык
237
+ gr.Textbox(label="Target Language (e.g., 'en')", value="en"), # Целевой язык
238
+ ],
239
+ outputs=gr.File(label="Translated Video"), # Вывод обработанного видео
240
+ title="Video Translation with Audio Sync",
241
+ description="Upload a video, specify the source and target languages, and generate a translated video with synchronized audio."
242
+ )
243
+
244
+ # Запуск интерфейса
245
+ interface.launch()
246
+
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ gradio
2
+ gtts
3
+ sacremoses
4
+ TTS
5
+ kenlm
6
+ pyctcdecode
7
+ espeakng