Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -1,250 +1,179 @@
|
|
1 |
import os
|
2 |
import gradio as gr
|
3 |
-
|
4 |
-
from google.genai import types
|
5 |
import mimetypes
|
6 |
from pydub import AudioSegment
|
7 |
-
import asyncio
|
8 |
-
import logging
|
9 |
|
10 |
-
# Configure logging
|
11 |
-
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
12 |
-
logger = logging.getLogger(__name__)
|
13 |
-
|
14 |
-
# Атрыманне ключоў і мадэляў з пераменных асяроддзя
|
15 |
GEMINI_API_KEY = os.getenv("gemini")
|
16 |
-
MODEL_NAME_TH = os.getenv("modTH")
|
17 |
-
MODEL_NAME = os.getenv("mod")
|
18 |
-
PROMPT_TRANSCRIBE = os.getenv("p") # Промпт для транскрыпцыі
|
19 |
|
20 |
-
|
21 |
-
client = genai.Client(api_key=GEMINI_API_KEY)
|
22 |
|
23 |
-
def transcribe_audio(audio_file
|
24 |
-
"""Транскрыбуе аўдыяфайл з дапамогай Google GenAI."""
|
25 |
try:
|
26 |
-
if not audio_file or not os.path.exists(audio_file):
|
27 |
-
return "Памылка: Файл не знойдзены або не указаны."
|
28 |
-
|
29 |
mime_type, _ = mimetypes.guess_type(audio_file)
|
30 |
-
if
|
31 |
-
return
|
32 |
-
"Немагчыма вызначыць тып файла або файл не з'яўляецца аўдыяфайлам. "
|
33 |
-
"Падтрымліваюцца толькі аўдыяфайлы."
|
34 |
-
)
|
35 |
-
|
36 |
with open(audio_file, "rb") as f:
|
37 |
audio_data = f.read()
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
|
|
|
|
|
|
|
|
50 |
)
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
|
|
|
|
|
|
|
|
|
|
55 |
except FileNotFoundError:
|
56 |
-
logger.error(f"Файл не знойдзены: {audio_file}")
|
57 |
return "Памылка: Файл не знойдзены."
|
|
|
|
|
58 |
except Exception as e:
|
59 |
-
logger.error(f"Памылка пры транскрыпцыі: {str(e)}", exc_info=True)
|
60 |
return f"Нечаканая памылка: {str(e)}"
|
61 |
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
return ""
|
67 |
-
|
68 |
-
prompt_fix = (
|
69 |
-
"Не змяняй тэксты, выправі толькі часовы фармат у субцітрах на правільны, "
|
70 |
-
"вось прыклад 00:00:01,589. \nУ адказ напішы толькі субцітры:\n"
|
71 |
-
f"{transcript}"
|
72 |
-
)
|
73 |
try:
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
config={"temperature": 0.2}
|
78 |
)
|
79 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
80 |
except Exception as e:
|
81 |
-
logger.error(f"Памылка пры выпраўленні субцітраў: {str(e)}", exc_info=True)
|
82 |
return transcript
|
83 |
|
84 |
-
|
85 |
-
def create_srt(transcript: str, filename: str = "subtitles.srt") -> tuple[str, str]:
|
86 |
-
"""Стварае SRT-файл з транскрыпцыі."""
|
87 |
-
if not transcript:
|
88 |
-
return "", ""
|
89 |
-
|
90 |
try:
|
91 |
with open(filename, "w", encoding="utf-8") as f:
|
92 |
f.write(transcript)
|
93 |
return transcript, filename
|
94 |
except Exception as e:
|
95 |
-
|
96 |
-
return f"Памылка пры запісе SRT-файла: {str(e)}", ""
|
97 |
-
|
98 |
-
|
99 |
-
def process_audio(audio_path: str) -> tuple[str, str]:
|
100 |
-
"""Апрацоўвае аўдыёфайл: транскрыбуе і стварае SRT."""
|
101 |
-
if not audio_path:
|
102 |
-
return "Не указаны шлях да аўдыёфайла.", ""
|
103 |
-
|
104 |
-
transcript = transcribe_audio(audio_path)
|
105 |
-
if transcript.startswith("Памылка") or transcript.startswith("Немагчыма") or transcript.startswith("Нечаканая"):
|
106 |
-
return transcript, ""
|
107 |
|
|
|
|
|
|
|
|
|
|
|
108 |
fixed_transcript = fix_subtitles_format(transcript)
|
109 |
text, srt_file = create_srt(fixed_transcript)
|
110 |
return text, srt_file
|
111 |
|
112 |
-
|
113 |
-
def extract_audio_from_video(video_file: str) -> tuple[str, str]:
|
114 |
-
"""Выдзяляе аўдыёдарожку з відэафайла."""
|
115 |
-
if not video_file or not os.path.exists(video_file):
|
116 |
-
return "", "Памылка: Відэафайл не знойдзены або не указаны."
|
117 |
-
|
118 |
try:
|
119 |
audio = AudioSegment.from_file(video_file)
|
120 |
audio_path = "extracted_audio.mp3"
|
121 |
audio.export(audio_path, format="mp3")
|
122 |
-
return audio_path,
|
123 |
except Exception as e:
|
124 |
-
|
125 |
-
return "", f"Памылка пры выдзяленні аўдыё з відэафайла: {str(e)}"
|
126 |
-
|
127 |
|
128 |
-
def process_video(
|
129 |
-
|
130 |
-
if not video_path:
|
131 |
-
return "Не указаны шлях да відэафайла.", ""
|
132 |
-
|
133 |
-
audio_path, error = extract_audio_from_video(video_path)
|
134 |
if error:
|
135 |
-
return error,
|
136 |
return process_audio(audio_path)
|
137 |
|
138 |
-
|
139 |
-
|
140 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
141 |
try:
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
return process_video(video_path)
|
146 |
-
else:
|
147 |
-
return "Няма файла для апрацоўкі.", ""
|
148 |
-
except Exception as e:
|
149 |
-
logger.error(f"Памылка пры апрацоўцы файла: {str(e)}", exc_info=True)
|
150 |
-
return f"Памылка пры апрацоўцы файла: {str(e)}", ""
|
151 |
-
|
152 |
-
|
153 |
-
def update_on_audio_change(audio_path: str | None) -> gr.update:
|
154 |
-
"""Абнаўляе інтэрфейс пры змене аўдыёфайла."""
|
155 |
-
try:
|
156 |
-
return gr.update(value=None, interactive=not bool(audio_path))
|
157 |
-
except Exception as e:
|
158 |
-
logger.error(f"Памылка пры абнаўленні інтэрфейса: {str(e)}", exc_info=True)
|
159 |
-
return gr.update(value=None, interactive=True)
|
160 |
-
|
161 |
-
|
162 |
-
def update_on_video_change(video_path: str | None) -> gr.update:
|
163 |
-
"""Абнаўляе інтэрфейс пры змене відэафайла."""
|
164 |
-
try:
|
165 |
-
return gr.update(value=None, interactive=not bool(video_path))
|
166 |
-
except Exception as e:
|
167 |
-
logger.error(f"Памылка пры абнаўленні інтэрфейса: {str(e)}", exc_info=True)
|
168 |
-
return gr.update(value=None, interactive=True)
|
169 |
-
|
170 |
-
|
171 |
-
def translate_transcript(transcript: str, target_language: str) -> tuple[str, str]:
|
172 |
-
"""Перакладае транскрыпцыю на іншую мову і стварае SRT."""
|
173 |
-
if not transcript:
|
174 |
-
return "Няма тэксту для перакладу.", ""
|
175 |
-
|
176 |
-
prompt_text = (
|
177 |
-
f"Перакладзі толькі тэксты субцітраў на {target_language} мову. "
|
178 |
-
"Астатняе пакінь як ёсць.\nТэкст:\n{transcript}"
|
179 |
-
)
|
180 |
-
try:
|
181 |
-
response = client.models.generate_content(
|
182 |
-
model=MODEL_NAME,
|
183 |
-
contents=[{"text": prompt_text}],
|
184 |
-
config={"temperature": 0.2}
|
185 |
)
|
186 |
-
|
|
|
|
|
|
|
|
|
|
|
187 |
translated_srt_filename = "translated_subtitles.srt"
|
188 |
-
|
|
|
|
|
189 |
except Exception as e:
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
)
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
file_output = gr.File(label="SRT-файл")
|
219 |
-
btn.click(
|
220 |
-
fn=process_file, inputs=[audio_input, video_input], outputs=[transcript_output, file_output]
|
221 |
-
)
|
222 |
-
|
223 |
-
gr.Markdown("## Пераклад субцітраў")
|
224 |
-
with gr.Row():
|
225 |
-
language_dropdown = gr.Dropdown(
|
226 |
-
choices=["English", "Руcкая", "Польская", "Літоўская", "Нямецкая"],
|
227 |
-
label="Выберы мову перакладу",
|
228 |
-
value="English",
|
229 |
-
)
|
230 |
-
translate_btn = gr.Button("Пераклад")
|
231 |
-
translation_output = gr.Textbox(label="Пераклад", lines=10)
|
232 |
-
translation_file_output = gr.File(label="Translated SRT-файл")
|
233 |
-
translate_btn.click(
|
234 |
-
fn=translate_transcript,
|
235 |
-
inputs=[transcript_output, language_dropdown],
|
236 |
-
outputs=[translation_output, translation_file_output],
|
237 |
)
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
show_error=True,
|
246 |
-
prevent_thread_lock=True
|
247 |
)
|
248 |
-
|
249 |
-
|
250 |
-
print(f"Крытычная памылка: {str(e)}")
|
|
|
1 |
import os
|
2 |
import gradio as gr
|
3 |
+
import google.generativeai as genai
|
|
|
4 |
import mimetypes
|
5 |
from pydub import AudioSegment
|
|
|
|
|
6 |
|
|
|
|
|
|
|
|
|
|
|
7 |
GEMINI_API_KEY = os.getenv("gemini")
|
8 |
+
MODEL_NAME_TH = os.getenv("modTH")
|
9 |
+
MODEL_NAME = os.getenv("mod")
|
|
|
10 |
|
11 |
+
genai.configure(api_key=GEMINI_API_KEY)
|
|
|
12 |
|
13 |
+
def transcribe_audio(audio_file):
|
|
|
14 |
try:
|
|
|
|
|
|
|
15 |
mime_type, _ = mimetypes.guess_type(audio_file)
|
16 |
+
if mime_type is None:
|
17 |
+
return "Немагчыма вызначыць тып файла. Падтрымліваюцца толькі аўдыяфайлы."
|
|
|
|
|
|
|
|
|
18 |
with open(audio_file, "rb") as f:
|
19 |
audio_data = f.read()
|
20 |
+
prompt_text = (
|
21 |
+
"""The user wants me to transcribe the audio into subtitles in SRT format, with a maximum of three words per one subtitle.
|
22 |
+
I need to listen to the audio and create subtitles with timestamps. Check time format hours:minutes:seconds,milliseconds (00:00:00,000) after creating all. Example SRT format:
|
23 |
+
1
|
24 |
+
00:00:01,670 --> 00:00:02,030
|
25 |
+
За мяжою, за мяжою,
|
26 |
+
2
|
27 |
+
00:00:02,270 --> 00:00:03,850
|
28 |
+
ні сваё і не чужое.
|
29 |
+
3
|
30 |
+
00:00:04,240 --> 00:00:05,760
|
31 |
+
Хоць спявай ты, хоць ты грай,
|
32 |
+
4
|
33 |
+
00:00:05,770 --> 00:00:06,250
|
34 |
+
а навокал іншы край.
|
35 |
+
"""
|
36 |
)
|
37 |
+
model = genai.GenerativeModel(MODEL_NAME_TH)
|
38 |
+
response = model.generate_content(
|
39 |
+
[prompt_text, {"mime_type": mime_type, "data": audio_data}]
|
40 |
+
)
|
41 |
+
if response.text:
|
42 |
+
transcript = response.text.strip()
|
43 |
+
else:
|
44 |
+
transcript = "Не атрымалася транскрыбаваць аўдыя. Магчыма, памылка з API."
|
45 |
+
return transcript
|
46 |
except FileNotFoundError:
|
|
|
47 |
return "Памылка: Файл не знойдзены."
|
48 |
+
except genai.APIError as e:
|
49 |
+
return f"Памылка API: {str(e)}"
|
50 |
except Exception as e:
|
|
|
51 |
return f"Нечаканая памылка: {str(e)}"
|
52 |
|
53 |
+
def fix_subtitles_format(transcript):
|
54 |
+
"""
|
55 |
+
Дадатковы запыт да мадэлі, які выпраўляе фармат часоў у субцітрах.
|
56 |
+
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
57 |
try:
|
58 |
+
prompt_fix = (
|
59 |
+
f"Не змяняй тэксты, выправі толькі часовы фармат у субцітрах на правільны, вось прыклад 00:00:01,589 \n"
|
60 |
+
f" �� адказ напішы толькі субцітры: {transcript}"
|
|
|
61 |
)
|
62 |
+
model = genai.GenerativeModel(MODEL_NAME)
|
63 |
+
response_fix = model.generate_content(prompt_fix)
|
64 |
+
if response_fix.text:
|
65 |
+
fixed_transcript = response_fix.text.strip()
|
66 |
+
else:
|
67 |
+
fixed_transcript = transcript
|
68 |
+
return fixed_transcript
|
69 |
except Exception as e:
|
|
|
70 |
return transcript
|
71 |
|
72 |
+
def create_srt(transcript, filename="subtitles.srt"):
|
|
|
|
|
|
|
|
|
|
|
73 |
try:
|
74 |
with open(filename, "w", encoding="utf-8") as f:
|
75 |
f.write(transcript)
|
76 |
return transcript, filename
|
77 |
except Exception as e:
|
78 |
+
return f"Памылка пры запісе SRT-файла: {str(e)}", None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
79 |
|
80 |
+
def process_audio(audio):
|
81 |
+
transcript = transcribe_audio(audio)
|
82 |
+
if transcript.startswith("Памылка"):
|
83 |
+
return transcript, None
|
84 |
+
# Дадаем другі запыт для выпраўлення фармату часоў у субцітрах
|
85 |
fixed_transcript = fix_subtitles_format(transcript)
|
86 |
text, srt_file = create_srt(fixed_transcript)
|
87 |
return text, srt_file
|
88 |
|
89 |
+
def extract_audio_from_video(video_file):
|
|
|
|
|
|
|
|
|
|
|
90 |
try:
|
91 |
audio = AudioSegment.from_file(video_file)
|
92 |
audio_path = "extracted_audio.mp3"
|
93 |
audio.export(audio_path, format="mp3")
|
94 |
+
return audio_path, None
|
95 |
except Exception as e:
|
96 |
+
return None, f"Памылка пры выдзяленні аўдыі з відэафайла: {str(e)}"
|
|
|
|
|
97 |
|
98 |
+
def process_video(video):
|
99 |
+
audio_path, error = extract_audio_from_video(video)
|
|
|
|
|
|
|
|
|
100 |
if error:
|
101 |
+
return error, None
|
102 |
return process_audio(audio_path)
|
103 |
|
104 |
+
def process_file(audio, video):
|
105 |
+
if audio is not None:
|
106 |
+
return process_audio(audio)
|
107 |
+
elif video is not None:
|
108 |
+
return process_video(video)
|
109 |
+
else:
|
110 |
+
return "Няма файла для апрацоўкі.", None
|
111 |
+
|
112 |
+
def update_on_audio_change(audio):
|
113 |
+
if audio is not None:
|
114 |
+
return gr.update(value=None, interactive=False)
|
115 |
+
else:
|
116 |
+
return gr.update(interactive=True)
|
117 |
+
|
118 |
+
def update_on_video_change(video):
|
119 |
+
if video is not None:
|
120 |
+
return gr.update(value=None, interactive=False)
|
121 |
+
else:
|
122 |
+
return gr.update(interactive=True)
|
123 |
+
|
124 |
+
def translate_transcript(transcript, target_language):
|
125 |
try:
|
126 |
+
prompt_text = (
|
127 |
+
f"перакладзі толькі тэксты субцітраў на {target_language} мову. Астатняя пакінь як ёсць."
|
128 |
+
f"Тэкст:\n{transcript}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
129 |
)
|
130 |
+
model = genai.GenerativeModel(MODEL_NAME)
|
131 |
+
response = model.generate_content(prompt_text)
|
132 |
+
if response.text:
|
133 |
+
translated = response.text.strip()
|
134 |
+
else:
|
135 |
+
translated = "Не атрымалася перакласці тэкст. Магчыма, памылка з API."
|
136 |
translated_srt_filename = "translated_subtitles.srt"
|
137 |
+
with open(translated_srt_filename, "w", encoding="utf-8") as f:
|
138 |
+
f.write(translated)
|
139 |
+
return translated, translated_srt_filename
|
140 |
except Exception as e:
|
141 |
+
return f"Памылка пры перакладзе: {str(e)}", None
|
142 |
+
|
143 |
+
with gr.Blocks() as demo:
|
144 |
+
gr.Markdown("# Транскрыпцыя аўдыя для беларускай мовы")
|
145 |
+
gr.Markdown(
|
146 |
+
"""
|
147 |
+
## Загрузіце аўдыёфайл або відэафайл да 15 хвілін. Калі загружаны аўдыёфайл, відэа неактыўна, і наадварот.
|
148 |
+
Субцітры будуць аўтаматычна згенераваны разам з файлам субцітраў.
|
149 |
+
[Далучайцеся да беларускаймоўнай суполкі ў ТГ](https://t.me/belarusai)
|
150 |
+
**Падтрымаць праект:** [Buy me a coffee](https://buymeacoffee.com/tuteishygpt)
|
151 |
+
"""
|
152 |
+
)
|
153 |
+
with gr.Row():
|
154 |
+
audio_input = gr.Audio(type="filepath", label="Аўдыёфайл")
|
155 |
+
video_input = gr.Video(label="Відэафайл")
|
156 |
+
audio_input.change(fn=update_on_audio_change, inputs=audio_input, outputs=video_input)
|
157 |
+
video_input.change(fn=update_on_video_change, inputs=video_input, outputs=audio_input)
|
158 |
+
|
159 |
+
btn = gr.Button("Апрацаваць")
|
160 |
+
transcript_output = gr.Textbox(label="Транскрыпцыя", lines=10)
|
161 |
+
file_output = gr.File(label="SRT-файл")
|
162 |
+
btn.click(fn=process_file, inputs=[audio_input, video_input], outputs=[transcript_output, file_output])
|
163 |
+
|
164 |
+
gr.Markdown("## Пераклад субцітраў")
|
165 |
+
with gr.Row():
|
166 |
+
language_dropdown = gr.Dropdown(
|
167 |
+
choices=["English", "Руcкая", "Польская", "Літоўская", "Нямецкая"],
|
168 |
+
label="Выберы мову перакладу", value="English"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
169 |
)
|
170 |
+
translate_btn = gr.Button("Пераклад")
|
171 |
+
translation_output = gr.Textbox(label="Пераклад", lines=10)
|
172 |
+
translation_file_output = gr.File(label="Translated SRT-файл")
|
173 |
+
translate_btn.click(
|
174 |
+
fn=translate_transcript,
|
175 |
+
inputs=[transcript_output, language_dropdown],
|
176 |
+
outputs=[translation_output, translation_file_output]
|
|
|
|
|
177 |
)
|
178 |
+
|
179 |
+
demo.launch()
|
|