File size: 14,222 Bytes
1cfce90
 
 
 
 
5da4b94
b595555
 
16867b4
0cebf66
d338f93
1cfce90
c8e49b9
1cfce90
 
 
 
 
54f9951
 
 
 
 
1cfce90
54f9951
 
 
 
1cfce90
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5da4b94
b595555
 
 
5da4b94
 
 
b595555
5da4b94
c8e49b9
1cfce90
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b595555
1cfce90
 
 
 
 
 
 
 
5da4b94
 
1cfce90
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b595555
1cfce90
 
 
 
 
 
 
 
c5a928e
1cfce90
 
c8e49b9
 
1cfce90
 
 
e03a20e
b595555
 
 
da033a0
1cfce90
 
 
c8e49b9
 
153d604
 
b595555
c8e49b9
153d604
1cfce90
e03a20e
ef692d0
 
 
 
e03a20e
1cfce90
 
ef692d0
c8e49b9
da033a0
b595555
e03a20e
 
da033a0
b595555
c8e49b9
 
 
 
 
 
2c02b43
b595555
ef692d0
b595555
 
 
1cfce90
c8e49b9
1cfce90
 
 
e03a20e
 
 
c8e49b9
e03a20e
c8e49b9
 
 
e03a20e
c8e49b9
 
e03a20e
c8e49b9
 
 
e03a20e
c8e49b9
5da4b94
b595555
5da4b94
e03a20e
1cfce90
 
 
c8e49b9
 
1cfce90
 
 
c8e49b9
 
 
1cfce90
 
 
c8e49b9
 
1cfce90
 
 
 
 
 
 
 
 
c8e49b9
1cfce90
 
 
b595555
0cebf66
1cfce90
 
0cebf66
1cfce90
 
c8e49b9
3723c75
 
0cebf66
50f4d12
 
0cebf66
50f4d12
 
 
 
 
1cfce90
 
 
b595555
 
5da4b94
1cfce90
 
 
 
 
5da4b94
1cfce90
5da4b94
1cfce90
 
 
 
 
5da4b94
 
 
 
b595555
5da4b94
 
b595555
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
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
import gradio as gr
from argparse import Namespace
import os
import shutil
import sys
import yt_dlp  # для працы з YouTube
import glob  # Import glob for file searching

GEMINI_API_KEY = os.getenv("gem2")


from sub_tools.media.converter import hls_to_media, media_to_signature, video_to_audio
from sub_tools.media.segmenter import segment_audio
from sub_tools.subtitles.combiner import combine_subtitles
from sub_tools.system.directory import change_directory
from sub_tools.system.console import header, success, error
from sub_tools.transcribe import transcribe

# Вызначэнне функцый для дэактывацыі процілеглага поля
def update_on_audio_change(audio):
    if audio is not None:
        return gr.update(interactive=False)
    return gr.update(interactive=True)

def update_on_video_change(video):
    if video is not None:
        return gr.update(interactive=False)
    return gr.update(interactive=True)

def main_logic(args: Namespace) -> tuple:
    """
    Асноўная логіка прыкладання: ад загрузкі відэа/аўдыё да зліцця субтытраў.
    Пасля зліцця субтытраў вяртае (тэкст субтытраў, шлях да SRT‑файла для спампоўкі).
    """
    output_str = ""
    subtitles_text = ""
    srt_file_path = None

    try:
        change_directory(args.output_path)
        step = 1

        if "video" in args.tasks:
            if not args.hls_url:
                output_str += f"{step}. Download Video: No video file uploaded\n"
                raise Exception("No video file uploaded")
            header(f"{step}. Download Video")
            output_str += f"{step}. Download Video: Started\n"
            hls_to_media(args.hls_url, args.video_file, False, args.overwrite)
            success("Done!")
            output_str += "Done!\n"
            step += 1

        if "audio" in args.tasks:
            header(f"{step}. Video to Audio")
            output_str += f"{step}. Video to Audio: Started\n"
            video_to_audio(args.video_file, args.audio_file, args.overwrite)
            success("Done!")
            output_str += "Done!\n"
            step += 1

        if "signature" in args.tasks:
            header(f"{step}. Audio to Signature")
            output_str += f"{step}. Audio to Signature: Started\n"
            media_to_signature(args.audio_file, args.signature_file, args.overwrite)
            success("Done!")
            output_str += "Done!\n"
            step += 1

        if "segment" in args.tasks:
            header(f"{step}. Segment Audio")
            output_str += f"{step}. Segment Audio: Started\n"

            print(f"Current working directory before segment_audio: {os.getcwd()}")  # Debug CWD
            print(f"Audio file path for segmentation: {args.audio_file}")  # Debug audio file path
            if not os.path.exists(args.audio_file):  # Critical file existence check
                error_msg = f"Error: Audio file not found before segmentation: {args.audio_file}"
                error(error_msg)
                return (error_msg, None)
            print(f"Audio file exists before segmentation: {args.audio_file}")  # Debug file existence

            segment_audio(args.audio_file, args.audio_segment_prefix, args.audio_segment_format, args.audio_segment_length, args.overwrite)
            success("Done!")
            output_str += "Done!\n"
            step += 1

        if "transcribe" in args.tasks:
            if not (args.gemini_api_key and args.gemini_api_key.strip()):
                output_str += f"{step}. Transcribe Audio: No Gemini API Key provided\n"
                raise Exception("No Gemini API Key provided")
            header(f"{step}. Transcribe Audio")
            output_str += f"{step}. Transcribe Audio: Started\n"
            transcribe(args)
            success("Done!")
            output_str += "Done!\n"
            step += 1

        if "combine" in args.tasks:
            header(f"{step}. Combine Subtitles")
            output_str += f"{step}. Combine Subtitles: Started\n"
            combine_subtitles(args.languages, args.audio_segment_prefix, args.audio_segment_format)
            success("Done!")
            output_str += "Done!\n"
            if args.languages:
                language = args.languages[0]
                srt_file_path = os.path.join(os.getcwd(), f"{language}.srt")
                try:
                    with open(srt_file_path, "r", encoding="utf-8") as f:
                        subtitles_text = f.read()
                except Exception as e:
                    subtitles_text = f"Error reading subtitles file: {str(e)}"
            else:
                subtitles_text = "No language specified"
            step += 1

        return (subtitles_text, srt_file_path)

    except Exception as e:
        error_msg = f"Error: {str(e)}"
        error(error_msg)
        return (error_msg, None)

def run_subtools(
    tasks,
    hls_url,
    video_file,
    audio_file,
    signature_file,
    output_path,
    languages,
    overwrite,
    retry,
    gemini_api_key,
    debug,
    audio_segment_prefix,
    audio_segment_format,
    audio_segment_length
):
    """
    Падрыхтоўка каталога вываду і запуск асноўнай логікі.
    """
    if os.path.exists(output_path):
        shutil.rmtree(output_path)
    os.makedirs(output_path, exist_ok=True)

    if isinstance(languages, str):
        languages = [lang.strip() for lang in languages.split(",") if lang.strip()]

    args = Namespace(
        tasks=tasks,
        hls_url=hls_url,
        video_file=video_file,
        audio_file=audio_file,
        signature_file=signature_file,
        output_path=output_path,
        languages=languages,
        overwrite=overwrite,
        retry=retry,
        gemini_api_key=gemini_api_key,
        debug=debug,
        audio_segment_prefix=audio_segment_prefix,
        audio_segment_format=audio_segment_format,
        audio_segment_length=audio_segment_length,
    )

    return main_logic(args)


def transcribe_youtube(youtube_url: str) -> tuple:
    """
    Спампоўвае аўдыё з відэа YouTube праз yt_dlp і вяртае паведамленне і шлях да часовага аўдыёфайла.
    Выкарыстоўвае chromewebstore.google.com_cookies.txt, калі ён ёсць.
    """
    if not youtube_url:
        return "Не ўведзена спасылка", None

    output_dir = "output_youtube_downloads"  # Define a specific output directory
    os.makedirs(output_dir, exist_ok=True)  # Create the directory if it doesn't exist
    outtmpl = os.path.join(output_dir, "temp_youtube_audio.%(ext)s")  # Path within the output dir

    try:
        ydl_opts = {
            'format': 'bestaudio/best',
            'outtmpl': outtmpl,
            #'quiet': True,  # Прыбіраем quiet для адладкі
            'postprocessors': [{
                'key': 'FFmpegExtractAudio',
                'preferredcodec': 'mp3',  # Download as MP3
                'preferredquality': '0',  # лепшая якасць
            }],
        }

        default_cookies_file = "chromewebstore.google.com_cookies.txt"
        if os.path.exists(default_cookies_file):
            ydl_opts['cookiefile'] = default_cookies_file
            print(f"Выкарыстоўваецца файл cookie па змаўчанні: {default_cookies_file}")

        with yt_dlp.YoutubeDL(ydl_opts) as ydl:
            ydl.download([youtube_url])

        # Шукаем створаны аўдыёфайл у output_dir
        audio_files = glob.glob(os.path.join(output_dir, "temp_youtube_audio.*"))
        print(f"Знойдзеныя аўдыёфайлы ў {output_dir}: {audio_files}")  # Дадаем вывад для адладкі
        if not audio_files:
            return ("Памылка: файл аўдыё не створаны", None)

        audio_file_rel = None  # Relative path
        for f in audio_files:
            if f.endswith(".mp3"):
                audio_file_rel = f
                break
        if audio_file_rel is None:
            audio_file_rel = audio_files[0]

        audio_file_abs = os.path.abspath(audio_file_rel)  # Get absolute path here!

        print(f"Абраны аўдыёфайл (relative): {audio_file_rel}")  # Дадаем вывад для адладкі
        print(f"Абраны аўдыёфайл (absolute): {audio_file_abs}")  # Дадаем вывад для адладкі
        return ("Спампоўка YouTube аўдыё завершана", audio_file_abs)  # Return absolute path!
    except Exception as e:
        err_msg = f"Памылка пры апрацоўцы YouTube (yt_dlp): {e}. Калі ласка, пераканайцеся, што спасылка сапраўдная, і праверце, ці даступныя відэа."
        print(err_msg)
        return (err_msg, None)


def process_youtube_url(youtube_url):
    """
    Апрацоўвае YouTube спасылку: спампоўвае аўдыё, адлюстроўвае аўдыёфайл і запускае ланцуг апрацоўкі для стварэння субтытраў.
    """
    download_msg, audio_file = transcribe_youtube(youtube_url)
    if not audio_file:
        return download_msg, None  # Return None for other outputs if download fails

    full_audio_file_path = audio_file
    print(f"Поўны шлях да аўдыёфайла: {full_audio_file_path}")

    if not os.path.exists(full_audio_file_path):
        return f"Памылка: Аўдыёфайл не знойдзены па шляху: {full_audio_file_path}", None
    print(f"Файл існуе: {full_audio_file_path}")

    output_text_file, output_file_file = process_uploaded_file(audio=full_audio_file_path, video=None)

    return output_text_file, output_file_file  # Correct return order: text, file_path


def process_uploaded_file(audio, video):
    """
    Выбірае, які файл загружаны, і фармуе параметры для апрацоўкі.
    Калі загружаны аўдыёфайл – запускае апрацоўку для аўдыё,
    калі відэафайл – запускае поўны ланцуг апрацоўкі.
    """
    if audio is not None and video is None:
        tasks = ["signature", "segment", "transcribe", "combine"]
        video_file = ""  # відэа не выкарыстоўваецца
        audio_file = audio
        hls_url = ""  # не патрабуецца
    elif video is not None and audio is None:
        tasks = ["video", "audio", "signature", "segment", "transcribe", "combine"]
        video_file = video
        audio_file = "audio.mp3"  # прызначаем імя для аўдыёфайла
        hls_url = "dummy"  # задаём няпустое значэнне для праверкі
    else:
        return "Error: Загрузіце толькі АЎДЫЁ або ВІДЭАфайл, а не абодва.", None

    return run_subtools(
        tasks=tasks,
        hls_url=hls_url,
        video_file=video_file,
        audio_file=audio_file,
        signature_file="message.shazamsignature",
        output_path="output",
        languages="be",
        overwrite=False,
        retry=50,
        gemini_api_key=GEMINI_API_KEY,
        debug=True,
        audio_segment_prefix="audio_segment",
        audio_segment_format="mp3",
        audio_segment_length=300000
    )

# --------------------- Gradio UI ---------------------

with gr.Blocks() as demo:
    gr.Markdown("# пакуль не працуе! Транскрыпцыя аўдыя для беларускай мовы. Перарабляем на больш якаснае разпазнаванне для доўгіх відэа")
    gr.Markdown(
        """
## [Можна выкарыстаць сэрвіс для кароткіх відэа тут] (https://huggingface.co/spaces/archivartaunik/SubtitlesBE)
[Ёсць пытанні ці прапановы? Далучайцеся да беларускаймоўнай суполкі штучнага інтэлекту](https://t.me/belarusai)
**Хочаце каб сэрвіс працаваў? Налівайце каву! :** [Buy me a coffee](https://buymeacoffee.com/tuteishygpt)
**Агучце беларускую мову тут :** [Беларуская мадэль маўлення](https://huggingface.co/spaces/archivartaunik/Bextts)
        """
    )
    with gr.Tabs():
        with gr.Tab("Файл"):
            with gr.Row():
                audio_input = gr.Audio(type="filepath", label="Аўдыёфайл")  # Keep type="filepath"
                video_input = gr.Video(label="Відэафайл", interactive=True)  # Reset to interactive True initially

            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)
            submit_btn_file = gr.Button("Submit")
            output_text_file = gr.Textbox(label="Тэкст субтытраў")
            output_file_file = gr.File(label="Спампаваць SRT файл")

            submit_btn_file.click(fn=process_uploaded_file, inputs=[audio_input, video_input], outputs=[output_text_file, output_file_file])

        with gr.Tab("YouTube"):
            youtube_url_input = gr.Textbox(label="YouTube URL", placeholder="Устаўце спасылку на відэа YouTube")
            submit_btn_youtube = gr.Button("Submit")
            output_text_youtube = gr.Textbox(label="Тэкст субтытраў")
            output_file_youtube = gr.File(label="Спампаваць SRT файл")

            submit_btn_youtube.click(
                fn=process_youtube_url,
                inputs=youtube_url_input,
                outputs=[output_text_youtube, output_file_youtube]  # Correct output order
            )

demo.launch(allowed_paths=["output_youtube_downloads"])