mirror of
https://github.com/barkeser2002/srtranslator-bariskeser.git
synced 2026-09-25 08:10:02 +03:00
361 lines
10 KiB
Python
361 lines
10 KiB
Python
import os
|
|
import glob
|
|
import json
|
|
import flet as ft
|
|
import traceback
|
|
import asyncio
|
|
from srtranslator.srt_file import SrtFile
|
|
from srtranslator.ass_file import AssFile
|
|
from srtranslator.translators.deepl_api import DeeplApi
|
|
from srtranslator.translators.deepl_scrap import DeeplTranslator
|
|
from srtranslator.translators.pydeeplx import PyDeepLX
|
|
from srtranslator.translators.translatepy import TranslatePy
|
|
|
|
builtin_translators = [
|
|
{
|
|
"id": "deepl-scrap",
|
|
"name": "DeepL Scraper",
|
|
"description": "Web scraper with selenium. Opens Gecodriver (firefox) to translate chunks of 1500 lines",
|
|
"handler": DeeplTranslator,
|
|
},
|
|
# {
|
|
# "id": "deepl-api",
|
|
# "name": "DeepL API",
|
|
# "description": "Uses a paid DeepL subscription to translate the files",
|
|
# "handler": DeeplApi,
|
|
# },
|
|
{
|
|
"id": "translatepy",
|
|
"name": "TranslatePy",
|
|
"description": "Uses TranslatePy library to translate from DeepL REST free api",
|
|
"handler": TranslatePy,
|
|
},
|
|
{
|
|
"id": "pydeeplx",
|
|
"name": "PyDeepLX",
|
|
"description": "Uses PyDeepLX library to translate from DeepL REST free api",
|
|
"handler": PyDeepLX,
|
|
},
|
|
]
|
|
|
|
|
|
def vertical_app(*children):
|
|
return ft.SafeArea(column(*children))
|
|
|
|
|
|
def column(*children):
|
|
return ft.Column(children)
|
|
|
|
|
|
def row(*children):
|
|
return ft.Row(children)
|
|
|
|
|
|
def pick_files_result(e: ft.FilePickerResultEvent, selected_files: ft.TextField):
|
|
if e.files:
|
|
value = ", ".join(map(lambda f: f.path, e.files))
|
|
selected_files.value = value
|
|
selected_files.update()
|
|
|
|
|
|
def get_directory_result(e: ft.FilePickerResultEvent, directory_path: ft.TextField):
|
|
if e.path:
|
|
directory_path.value = e.path
|
|
directory_path.update()
|
|
|
|
|
|
def counter(value_container: ft.Text):
|
|
def minus_click(e):
|
|
try:
|
|
new_value = str(int(txt_number.value) - 1)
|
|
update(e, new_value)
|
|
except:
|
|
update(e, value_container.value)
|
|
|
|
def plus_click(e):
|
|
try:
|
|
new_value = str(int(txt_number.value) + 1)
|
|
update(e, new_value)
|
|
except:
|
|
update(e, value_container.value)
|
|
|
|
def handle_input_change(e):
|
|
try:
|
|
txt_number.value = txt_number.value or "0"
|
|
new_value = str(int(e.control.value))
|
|
update(e, new_value)
|
|
except:
|
|
update(e, value_container.value)
|
|
|
|
def update(e, new_value: str):
|
|
txt_number.value = new_value
|
|
value_container.value = new_value
|
|
e.page.update()
|
|
|
|
txt_number = ft.TextField(
|
|
label="Wrap lines limit",
|
|
value=value_container.value,
|
|
text_align=ft.TextAlign.CENTER,
|
|
on_change=handle_input_change,
|
|
)
|
|
|
|
return ft.Row(
|
|
[
|
|
ft.IconButton(ft.icons.REMOVE, on_click=minus_click),
|
|
ft.Container(expand=1, content=txt_number),
|
|
ft.IconButton(ft.icons.ADD, on_click=plus_click),
|
|
],
|
|
)
|
|
|
|
|
|
def folder_picker_dialog(path_field: ft.TextField):
|
|
pick_folder_dialog = ft.FilePicker(
|
|
on_result=lambda e: get_directory_result(e, path_field)
|
|
)
|
|
|
|
pick_folder_trigger = ft.ElevatedButton(
|
|
"Open directory",
|
|
icon=ft.icons.FOLDER_OPEN,
|
|
on_click=lambda _: pick_folder_dialog.get_directory_path(),
|
|
)
|
|
|
|
return pick_folder_trigger, pick_folder_dialog
|
|
|
|
|
|
def file_picker_dialog(path_field: ft.TextField):
|
|
pick_files_dialog = ft.FilePicker(
|
|
on_result=lambda e: pick_files_result(e, path_field)
|
|
)
|
|
|
|
pick_files_trigger = ft.ElevatedButton(
|
|
"Pick files",
|
|
icon=ft.icons.UPLOAD_FILE,
|
|
on_click=lambda _: pick_files_dialog.pick_files(
|
|
allow_multiple=True, allowed_extensions=["srt", "ass"]
|
|
),
|
|
)
|
|
|
|
return pick_files_trigger, pick_files_dialog
|
|
|
|
|
|
def translators_dropdown():
|
|
return ft.Dropdown(
|
|
label="Translator",
|
|
value=builtin_translators[0]["id"],
|
|
options=[
|
|
ft.dropdown.Option(key=option["id"], text=option["name"])
|
|
for option in builtin_translators
|
|
],
|
|
)
|
|
|
|
|
|
def source_language_dropdown():
|
|
with open("./assets/source_languages.json") as f:
|
|
source_languages = json.load(f)
|
|
|
|
return ft.Dropdown(
|
|
label="Source language",
|
|
value=source_languages[0]["id"],
|
|
options=[
|
|
ft.dropdown.Option(key=option["id"], text=option["name"])
|
|
for option in source_languages
|
|
],
|
|
)
|
|
|
|
|
|
def destination_language_dropdown():
|
|
with open("./assets/destination_languages.json") as f:
|
|
destination_languages = json.load(f)
|
|
|
|
return ft.Dropdown(
|
|
label="Destination language",
|
|
value=destination_languages[0]["id"],
|
|
options=[
|
|
ft.dropdown.Option(key=option["id"], text=option["name"])
|
|
for option in destination_languages
|
|
],
|
|
)
|
|
|
|
|
|
def get_translator(translator_id: str):
|
|
translator = next(
|
|
(
|
|
translator
|
|
for translator in builtin_translators
|
|
if translator["id"] == translator_id
|
|
),
|
|
{},
|
|
)
|
|
|
|
handler = translator.get("handler", None)
|
|
|
|
return handler() if handler is not None else None
|
|
|
|
|
|
class TranslationProgress(ft.Container):
|
|
def __init__(
|
|
self,
|
|
filepath: str,
|
|
translator_id: str,
|
|
src_lang: str,
|
|
dest_lang: str,
|
|
wrap_limit: int,
|
|
):
|
|
super().__init__()
|
|
self.filepath = filepath
|
|
self.translator_id = translator_id
|
|
self.src_lang = src_lang
|
|
self.dest_lang = dest_lang
|
|
self.wrap_limit = int(wrap_limit)
|
|
self.cancelled = False
|
|
|
|
self.progress_bar = ft.ProgressBar()
|
|
self.close_button = ft.IconButton(
|
|
icon=ft.icons.CLOSE,
|
|
icon_size=20,
|
|
tooltip="Cancel",
|
|
on_click=self.remove,
|
|
)
|
|
self.content = row(
|
|
ft.Container(expand=2, content=ft.Text(self.filepath)),
|
|
ft.Container(
|
|
expand=1,
|
|
content=ft.Text(self.src_lang, text_align=ft.TextAlign.CENTER),
|
|
),
|
|
ft.Container(
|
|
expand=1,
|
|
content=ft.Text(self.dest_lang, text_align=ft.TextAlign.CENTER),
|
|
),
|
|
ft.Container(expand=2, content=self.progress_bar),
|
|
self.close_button,
|
|
)
|
|
|
|
def remove(self, e):
|
|
self.cancelled = True
|
|
self.page.controls.remove(self)
|
|
self.page.update()
|
|
|
|
def show_progress(self, total: float, progress: float):
|
|
self.progress_bar.value = min(1, float(progress) / float(total))
|
|
self.page.update()
|
|
|
|
def translate(self):
|
|
if self.cancelled:
|
|
return
|
|
|
|
self.close_button.disabled = True
|
|
self.page.update()
|
|
|
|
translator = get_translator(self.translator_id)
|
|
if translator is None:
|
|
raise Exception("Translator is None")
|
|
|
|
if self.filepath.endswith("ass"):
|
|
sub = AssFile(self.filepath, self.show_progress)
|
|
elif self.filepath.endswith("srt"):
|
|
sub = SrtFile(self.filepath, self.show_progress)
|
|
else:
|
|
raise Exception("File type not supported")
|
|
|
|
try:
|
|
sub.translate(translator, self.src_lang, self.dest_lang)
|
|
sub.wrap_lines(self.wrap_limit)
|
|
|
|
filename = os.path.splitext(self.filepath)
|
|
sub.save(f"{filename[0]}_{self.dest_lang}{filename[1]}")
|
|
except:
|
|
sub.save_backup()
|
|
traceback.print_exc()
|
|
raise Exception("Error while translating")
|
|
|
|
translator.quit()
|
|
self.close_button.disabled = False
|
|
self.page.update()
|
|
|
|
|
|
def handle_translation(
|
|
page: ft.Page,
|
|
path_field_input: ft.TextField,
|
|
translator: str,
|
|
src_lang: str,
|
|
dest_lang: str,
|
|
wrap_limit: str,
|
|
):
|
|
if not path_field_input.value:
|
|
return
|
|
|
|
files = []
|
|
for filepath in path_field_input.value.split(","):
|
|
if not os.path.isdir(filepath):
|
|
files.append(filepath)
|
|
continue
|
|
|
|
get_all_files = lambda ext: glob.glob(
|
|
os.path.join(glob.escape(filepath), f"**/*.{ext}"), recursive=True
|
|
)
|
|
files.extend(get_all_files("ass"))
|
|
files.extend(get_all_files("srt"))
|
|
|
|
translations = [
|
|
TranslationProgress(file, translator, src_lang, dest_lang, int(wrap_limit))
|
|
for file in files
|
|
]
|
|
|
|
path_field_input.value = ""
|
|
page.add(*translations)
|
|
|
|
for process in translations:
|
|
try:
|
|
process.translate()
|
|
except:
|
|
process.close_button.disabled = False
|
|
process.progress_bar.value = 1
|
|
process.progress_bar.color = "red"
|
|
page.update()
|
|
|
|
|
|
def main(page: ft.Page):
|
|
page.theme_mode = "dark"
|
|
|
|
wrap_limit = ft.Text(value=50)
|
|
path_field = ft.TextField(label="Path")
|
|
pick_folder_trigger, pick_folder_dialog = folder_picker_dialog(path_field)
|
|
pick_files_trigger, pick_files_dialog = file_picker_dialog(path_field)
|
|
translator_dropdown = translators_dropdown()
|
|
source_lang_dropdown = source_language_dropdown()
|
|
dest_lang_dropdown = destination_language_dropdown()
|
|
submit_button = ft.OutlinedButton(
|
|
"Translate",
|
|
on_click=lambda x: handle_translation(
|
|
page,
|
|
path_field,
|
|
translator_dropdown.value,
|
|
source_lang_dropdown.value,
|
|
dest_lang_dropdown.value,
|
|
wrap_limit.value,
|
|
),
|
|
)
|
|
|
|
page.overlay.extend([pick_files_dialog, pick_folder_dialog])
|
|
page.add(
|
|
vertical_app(
|
|
ft.Row(
|
|
[ft.Text("SRTranslatorGUI")],
|
|
alignment=ft.MainAxisAlignment.CENTER,
|
|
),
|
|
row(
|
|
ft.Container(expand=1, content=path_field),
|
|
pick_files_trigger,
|
|
pick_folder_trigger,
|
|
),
|
|
translator_dropdown,
|
|
source_lang_dropdown,
|
|
dest_lang_dropdown,
|
|
counter(wrap_limit),
|
|
row(ft.Container(expand=1, content=submit_button)),
|
|
)
|
|
)
|
|
|
|
|
|
ft.app(target=main)
|