File size: 10,874 Bytes
6160629
 
4ba47fd
 
d9d75ab
06555da
04c273e
 
 
 
 
 
6160629
c3bc5d7
e64464f
89be3e8
 
 
a9913d9
4ba47fd
 
265d4cc
 
89be3e8
6160629
04c273e
 
 
d9d75ab
265d4cc
89be3e8
 
 
 
265d4cc
6160629
 
3ab57cd
4ba47fd
 
89be3e8
 
 
 
 
4ba47fd
 
 
82ff83b
4ba47fd
89be3e8
 
a9913d9
265d4cc
89be3e8
04c273e
89be3e8
6160629
04c273e
 
a9913d9
265d4cc
 
89be3e8
04c273e
 
 
265d4cc
89be3e8
 
265d4cc
 
631160f
4ba47fd
 
 
82ff83b
4ba47fd
a9913d9
631160f
04c273e
265d4cc
3ab57cd
a9913d9
265d4cc
89be3e8
04c273e
 
 
d9d75ab
f2c750c
d9d75ab
f2c750c
d9d75ab
04c273e
 
a9913d9
265d4cc
 
89be3e8
04c273e
 
 
265d4cc
04c273e
a9913d9
89be3e8
631160f
89be3e8
 
a9913d9
6160629
265d4cc
89be3e8
04c273e
 
 
9dab23b
06555da
6c7418c
 
265d4cc
9dab23b
04c273e
 
a9913d9
265d4cc
 
89be3e8
04c273e
 
 
265d4cc
9dab23b
265d4cc
9dab23b
 
a9913d9
265d4cc
89be3e8
04c273e
 
 
 
 
 
 
 
 
 
89be3e8
 
 
 
04c273e
 
 
 
 
89be3e8
 
 
 
04c273e
 
 
 
 
9dab23b
265d4cc
a9913d9
89be3e8
04c273e
 
 
265d4cc
89be3e8
 
265d4cc
f2c750c
4ba47fd
 
 
82ff83b
4ba47fd
a9913d9
89be3e8
 
f2c750c
04c273e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
265d4cc
04c273e
 
 
 
 
 
89be3e8
04c273e
 
 
 
 
 
3ab57cd
04c273e
 
 
 
 
 
 
 
 
 
 
 
 
 
f2c750c
d9d75ab
04c273e
 
 
 
 
 
 
 
 
 
 
82ff83b
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
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
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
import os
import gradio as gr
from google import genai
from google.genai import types
import mimetypes
from pydub import AudioSegment
import asyncio
import logging

# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

# Атрыманне ключоў і мадэляў з пераменных асяроддзя
GEMINI_API_KEY = os.getenv("gemini")
MODEL_NAME_TH = os.getenv("modTH")  # Мадэль для транскрыпцыі
MODEL_NAME = os.getenv("mod")  # Мадэль для выпраўлення фармату і перакладу
PROMPT_TRANSCRIBE = os.getenv("p")  # Промпт для транскрыпцыі

# Стварэнне кліента
client = genai.Client(api_key=GEMINI_API_KEY)

def transcribe_audio(audio_file: str) -> str:
    """Транскрыбуе аўдыяфайл з дапамогай Google GenAI."""
    try:
        if not audio_file or not os.path.exists(audio_file):
            return "Памылка: Файл не знойдзены або не указаны."
            
        mime_type, _ = mimetypes.guess_type(audio_file)
        if not mime_type or not mime_type.startswith("audio"):
            return (
                "Немагчыма вызначыць тып файла або файл не з'яўляецца аўдыяфайлам. "
                "Падтрымліваюцца толькі аўдыяфайлы."
            )

        with open(audio_file, "rb") as f:
            audio_data = f.read()

        # Стварэнне contents для перадачы ў API
        contents = [
            {"text": PROMPT_TRANSCRIBE},
            {"mime_type": mime_type, "data": audio_data},
        ]

        # Выклік API
        response = client.models.generate_content(
            model=MODEL_NAME_TH,
            contents=contents,
            config={"temperature": 0.2}
        )
        
        # Атрыманне адказу
        return response.text.strip()

    except FileNotFoundError:
        logger.error(f"Файл не знойдзены: {audio_file}")
        return "Памылка: Файл не знойдзены."
    except Exception as e:
        logger.error(f"Памылка пры транскрыпцыі: {str(e)}", exc_info=True)
        return f"Нечаканая памылка: {str(e)}"


def fix_subtitles_format(transcript: str) -> str:
    """Выпраўляе фармат часу ў субцітрах."""
    if not transcript:
        return ""
        
    prompt_fix = (
        "Не змяняй тэксты, выправі толькі часовы фармат у субцітрах на правільны, "
        "вось прыклад 00:00:01,589. \nУ адказ напішы толькі субцітры:\n"
        f"{transcript}"
    )
    try:
        response_fix = client.models.generate_content(
            model=MODEL_NAME,
            contents=[{"text": prompt_fix}],
            config={"temperature": 0.2}
        )
        return response_fix.text.strip()
    except Exception as e:
        logger.error(f"Памылка пры выпраўленні субцітраў: {str(e)}", exc_info=True)
        return transcript


def create_srt(transcript: str, filename: str = "subtitles.srt") -> tuple[str, str]:
    """Стварае SRT-файл з транскрыпцыі."""
    if not transcript:
        return "", ""
        
    try:
        with open(filename, "w", encoding="utf-8") as f:
            f.write(transcript)
        return transcript, filename
    except Exception as e:
        logger.error(f"Памылка пры запісе SRT-файла: {str(e)}", exc_info=True)
        return f"Памылка пры запісе SRT-файла: {str(e)}", ""


def process_audio(audio_path: str) -> tuple[str, str]:
    """Апрацоўвае аўдыёфайл: транскрыбуе і стварае SRT."""
    if not audio_path:
        return "Не указаны шлях да аўдыёфайла.", ""
        
    transcript = transcribe_audio(audio_path)
    if transcript.startswith("Памылка") or transcript.startswith("Немагчыма") or transcript.startswith("Нечаканая"):
        return transcript, ""

    fixed_transcript = fix_subtitles_format(transcript)
    text, srt_file = create_srt(fixed_transcript)
    return text, srt_file


def extract_audio_from_video(video_file: str) -> tuple[str, str]:
    """Выдзяляе аўдыёдарожку з відэафайла."""
    if not video_file or not os.path.exists(video_file):
        return "", "Памылка: Відэафайл не знойдзены або не указаны."
        
    try:
        audio = AudioSegment.from_file(video_file)
        audio_path = "extracted_audio.mp3"
        audio.export(audio_path, format="mp3")
        return audio_path, ""
    except Exception as e:
        logger.error(f"Памылка пры выдзяленні аўдыё з відэафайла: {str(e)}", exc_info=True)
        return "", f"Памылка пры выдзяленні аўдыё з відэафайла: {str(e)}"


def process_video(video_path: str) -> tuple[str, str]:
    """Апрацоўвае відэафайл: выдзяляе аўдыё, транскрыбуе і стварае SRT."""
    if not video_path:
        return "Не указаны шлях да відэафайла.", ""
        
    audio_path, error = extract_audio_from_video(video_path)
    if error:
        return error, ""
    return process_audio(audio_path)


def process_file(audio_path: str | None, video_path: str | None) -> tuple[str, str]:
    """Апрацоўвае файл (аўдыё або відэа)."""
    try:
        if audio_path:
            return process_audio(audio_path)
        elif video_path:
            return process_video(video_path)
        else:
            return "Няма файла для апрацоўкі.", ""
    except Exception as e:
        logger.error(f"Памылка пры апрацоўцы файла: {str(e)}", exc_info=True)
        return f"Памылка пры апрацоўцы файла: {str(e)}", ""


def update_on_audio_change(audio_path: str | None) -> gr.update:
    """Абнаўляе інтэрфейс пры змене аўдыёфайла."""
    try:
        return gr.update(value=None, interactive=not bool(audio_path))
    except Exception as e:
        logger.error(f"Памылка пры абнаўленні інтэрфейса: {str(e)}", exc_info=True)
        return gr.update(value=None, interactive=True)


def update_on_video_change(video_path: str | None) -> gr.update:
    """Абнаўляе інтэрфейс пры змене відэафайла."""
    try:
        return gr.update(value=None, interactive=not bool(video_path))
    except Exception as e:
        logger.error(f"Памылка пры абнаўленні інтэрфейса: {str(e)}", exc_info=True)
        return gr.update(value=None, interactive=True)


def translate_transcript(transcript: str, target_language: str) -> tuple[str, str]:
    """Перакладае транскрыпцыю на іншую мову і стварае SRT."""
    if not transcript:
        return "Няма тэксту для перакладу.", ""
        
    prompt_text = (
        f"Перакладзі толькі тэксты субцітраў на {target_language} мову. "
        "Астатняе пакінь як ёсць.\nТэкст:\n{transcript}"
    )
    try:
        response = client.models.generate_content(
            model=MODEL_NAME,
            contents=[{"text": prompt_text}],
            config={"temperature": 0.2}
        )
        translated = response.text.strip()
        translated_srt_filename = "translated_subtitles.srt"
        return create_srt(translated, translated_srt_filename)
    except Exception as e:
        logger.error(f"Памылка пры перакладзе: {str(e)}", exc_info=True)
        return f"Памылка пры перакладзе: {str(e)}", ""


try:
    with gr.Blocks() as demo:
        gr.Markdown("# Транскрыпцыя аўдыя для беларускай мовы")
        gr.Markdown(
            """
    ## Загрузіце аўдыёфайл або відэафайл да 15 хвілін. Калі загружаны аўдыёфайл, відэа неактыўна, і наадварот.
    Субцітры будуць аўтаматычна згенераваны разам з SRT-файлам.
    [Далучайцеся да беларускаймоўнай суполкі ў ТГ](https://t.me/belarusai)
    **Падтрымаць праект:** [Buy me a coffee](https://buymeacoffee.com/tuteishygpt)
            """
        )
        with gr.Row():
            audio_input = gr.Audio(type="filepath", label="Аўдыёфайл")
            video_input = gr.Video(label="Відэафайл")

        audio_input.change(
            fn=update_on_audio_change, inputs=audio_input, outputs=video_input
        )
        video_input.change(
            fn=update_on_video_change, inputs=video_input, outputs=audio_input
        )

        btn = gr.Button("Апрацаваць")
        transcript_output = gr.Textbox(label="Транскрыпцыя", lines=10)
        file_output = gr.File(label="SRT-файл")
        btn.click(
            fn=process_file, inputs=[audio_input, video_input], outputs=[transcript_output, file_output]
        )

        gr.Markdown("## Пераклад субцітраў")
        with gr.Row():
            language_dropdown = gr.Dropdown(
                choices=["English", "Руcкая", "Польская", "Літоўская", "Нямецкая"],
                label="Выберы мову перакладу",
                value="English",
            )
            translate_btn = gr.Button("Пераклад")
        translation_output = gr.Textbox(label="Пераклад", lines=10)
        translation_file_output = gr.File(label="Translated SRT-файл")
        translate_btn.click(
            fn=translate_transcript,
            inputs=[transcript_output, language_dropdown],
            outputs=[translation_output, translation_file_output],
        )

    # Запуск з дадатковымі параметрамі для стабільнасці
    demo.launch(
        server_name="0.0.0.0",
        server_port=7860,
        share=False,
        debug=False,
        show_error=True,
        prevent_thread_lock=True
    )
except Exception as e:
    logger.critical(f"Крытычная памылка пры запуску праграмы: {str(e)}", exc_info=True)
    print(f"Крытычная памылка: {str(e)}")