├── .github ├── FUNDING.yml ├── pull_request_template.md └── workflows │ └── build.yml ├── CONTRIBUTING.md ├── GICutscenesUI ├── main.py ├── subtitles.py └── web │ ├── images │ └── icon.ico │ ├── locales │ ├── en.json │ ├── fr.json │ ├── id.json │ ├── jp.json │ ├── kr.json │ ├── ru.json │ ├── subtitles_preview.json │ ├── uk.json │ ├── vi.json │ ├── zh-hans.json │ └── zh-hant.json │ ├── main.html │ ├── scripts │ ├── about.js │ ├── console.js │ ├── language.js │ ├── main.js │ └── settings.js │ └── styles │ ├── dark.css │ ├── fontawesome │ ├── css │ │ └── all.min.css │ └── webfonts │ │ ├── fa-regular-400.woff2 │ │ └── fa-solid-900.woff2 │ └── main.css ├── README.md ├── github ├── ffmpeg.exe ├── images │ ├── animation_low.gif │ ├── icons │ │ ├── CLI │ │ │ ├── cli-1.png │ │ │ └── cli-2.png │ │ └── UI │ │ │ ├── ui-1.png │ │ │ └── ui-2.png │ ├── main.png │ ├── new_main_page.png │ ├── settings_dark.png │ ├── settings_light.png │ ├── settings_old.png │ └── subtitles.png ├── requirements.txt └── ver ├── translations.md └── useful.md /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 2 | # patreon: # Replace with a single Patreon username 3 | # ko_fi: # Replace with a single Ko-fi username 4 | custom: ['https://donatello.to/super_zombi'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 5 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ### Please create all pull requests to the `dev` branch! 2 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build GICutscenesUI 2 | 3 | on: 4 | release: 5 | types: [published] 6 | workflow_dispatch: 7 | 8 | jobs: 9 | build: 10 | runs-on: windows-latest 11 | defaults: 12 | run: 13 | shell: pwsh 14 | working-directory: ./GICutscenesUI 15 | steps: 16 | - name: Checkout the repo 17 | uses: actions/checkout@v4 18 | - name: Set up Python 3.12 19 | uses: actions/setup-python@v5 20 | with: 21 | python-version: "3.12" 22 | - name: Install Python dependencies 23 | run: | 24 | python -m pip install --upgrade pip 25 | pip install -r ../github/requirements.txt 26 | - name: Set up GICutscenes 27 | run: | 28 | Invoke-WebRequest -Uri "https://github.com/ToaHartor/GI-cutscenes/releases/download/v0.5.0/GICutscenes-0.5.0-win-x64-standalone.zip" -OutFile ./gicutscenes.zip 29 | 7z e gicutscenes.zip appsettings.json GICutscenes.exe 30 | Invoke-WebRequest -Uri "https://raw.githubusercontent.com/ToaHartor/GI-cutscenes/refs/heads/main/versions.json" -OutFile ./versions.json 31 | - name: Use PyInstaller to build GICutscenesUI 32 | run: pyinstaller --noconfirm --onefile --noconsole --icon="./web/images/icon.ico" --name="GICutscenesUI" --version-file="../github/ver" --add-data="web\\;.\\web" --add-data="GICutscenes.exe;." --add-data="appsettings.json;." --add-data="versions.json;." --add-data="../github/ffmpeg.exe;." main.py 33 | - name: Upload to Release 34 | uses: xresloader/upload-to-github-release@v1 35 | with: 36 | file: ./GICutscenesUI/dist/GICutscenesUI.exe 37 | overwrite: true 38 | update_latest_release: true 39 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ### Please create all pull requests to the `dev` branch! 2 | -------------------------------------------------------------------------------- /GICutscenesUI/main.py: -------------------------------------------------------------------------------- 1 | from tkinter import Tk, font 2 | from tkinter.filedialog import askdirectory, askopenfilename, askopenfilenames 3 | import eel 4 | import sys, os 5 | import shutil 6 | import subprocess 7 | import json 8 | from json_minify import json_minify 9 | import re 10 | import base64 11 | import requests 12 | from subtitles import * 13 | 14 | CONSOLE_DEBUG_MODE = False 15 | __version__ = '0.9.1' 16 | 17 | # ---- Required Functions ---- 18 | 19 | def resource_path(relative_path=""): 20 | """ Get absolute path to resource, works for dev and for PyInstaller """ 21 | base_path = getattr(sys, '_MEIPASS', os.path.dirname(os.path.abspath(__file__))) 22 | return os.path.join(base_path, relative_path) 23 | 24 | @eel.expose 25 | def get_version(): 26 | return __version__ 27 | 28 | # ---- Locales ---- 29 | 30 | remove_coma_regex = r'''(?<=[}\]"']),(?!\s*[{["'])''' 31 | 32 | @eel.expose 33 | def get_translation(code): 34 | tr_file = os.path.join(resource_path("web"), "locales", code + ".json") 35 | if os.path.exists(tr_file): 36 | with open(tr_file, 'r', encoding="utf-8-sig") as file: 37 | string = json_minify(file.read()) # remove comments 38 | string = re.sub(remove_coma_regex, "", string, 0) # remove coma at the end 39 | output = json.loads(string) 40 | return output 41 | 42 | def load_subs_preview_text(): 43 | file = os.path.join(resource_path("web"), "locales", "subtitles_preview.json") 44 | with open(file, 'r', encoding="utf-8-sig") as f: 45 | string = re.sub(remove_coma_regex, "", f.read(), 0) 46 | data = json.loads(string) 47 | def wraper(lang): return data.get(lang, data.get('en')) 48 | return wraper 49 | 50 | SUBTITLES_PREVIEW_TEXT = load_subs_preview_text() 51 | 52 | 53 | # ---- EXE Functions ---- 54 | 55 | def find_script(script_name): 56 | def find_in(folder): 57 | files = [f for f in os.listdir(folder) if os.path.isfile(os.path.join(folder, f))] 58 | if script_name in files: 59 | return os.path.join(folder, script_name) 60 | 61 | result = find_in(os.getcwd()) 62 | if result: return result 63 | 64 | result = find_in(resource_path()) 65 | if result: return result 66 | 67 | def file_in_temp(file): 68 | return os.path.dirname(file) == os.path.dirname(resource_path(os.path.basename(file))) 69 | 70 | 71 | # ---- Settings Functions ---- 72 | 73 | def load_settings_inline(): 74 | global SCRIPT_FILE, OUTPUT_F, FFMPEG, SUBTITLES_F 75 | set_file = os.path.join(os.getcwd(), "UI-settings.json") 76 | settings = {} 77 | if os.path.exists(set_file): 78 | with open(set_file, 'r', encoding='utf-8') as file: 79 | settings = json.loads(file.read()) 80 | if "script_file" in settings.keys(): 81 | SCRIPT_FILE = settings["script_file"] 82 | if "output_folder" in settings.keys(): 83 | OUTPUT_F = settings["output_folder"] 84 | if "FFMPEG" in settings.keys(): 85 | FFMPEG = settings["FFMPEG"] 86 | if "subtitles_folder" in settings.keys(): 87 | SUBTITLES_F = settings["subtitles_folder"] 88 | 89 | SCRIPT_FILE = settings.get("script_file") or find_script("GICutscenes.exe") 90 | OUTPUT_F = settings.get("output_folder") or os.path.join(os.getcwd(), "output") 91 | FFMPEG = settings.get("FFMPEG") or find_script("ffmpeg.exe") or "ffmpeg" 92 | SUBTITLES_F = settings.get("subtitles_folder") or "" 93 | 94 | if os.path.exists(os.path.join(os.getcwd(), "versions.json")) and file_in_temp(SCRIPT_FILE): 95 | local_ver_file = os.path.join(os.getcwd(), "versions.json") 96 | temp_ver_file = os.path.join(os.path.dirname(SCRIPT_FILE), "versions.json") 97 | with open(local_ver_file, 'r') as orig_file: 98 | data = orig_file.read() 99 | with open(temp_ver_file, 'w') as temp_file: 100 | temp_file.write(data) 101 | 102 | load_settings_inline() 103 | 104 | @eel.expose 105 | def load_settings(): 106 | set_file = os.path.join(os.getcwd(), "UI-settings.json") 107 | if os.path.exists(set_file): 108 | with open(set_file, 'r', encoding='utf-8') as file: 109 | settings = json.loads(file.read()) 110 | return settings 111 | return {} 112 | 113 | @eel.expose 114 | def save_settings(settings): 115 | settings['output_folder'] = OUTPUT_F 116 | settings['subtitles_folder'] = SUBTITLES_F 117 | if not file_in_temp(SCRIPT_FILE): 118 | settings['script_file'] = SCRIPT_FILE 119 | if not file_in_temp(FFMPEG): 120 | settings['FFMPEG'] = FFMPEG 121 | 122 | with open(os.path.join(os.getcwd(), "UI-settings.json"), 'w', encoding='utf-8') as file: 123 | file.write(json.dumps(settings, indent=4, ensure_ascii=False)) 124 | return True 125 | 126 | @eel.expose 127 | def delete_settings(): 128 | global SCRIPT_FILE, OUTPUT_F, FFMPEG, SUBTITLES_F 129 | SCRIPT_FILE = find_script("GICutscenes.exe") 130 | OUTPUT_F = os.path.join(os.getcwd(), "output") 131 | FFMPEG = find_script("ffmpeg.exe") or "ffmpeg" 132 | SUBTITLES_F = "" 133 | 134 | set_file = os.path.join(os.getcwd(), "UI-settings.json") 135 | if os.path.exists(set_file): 136 | os.remove(set_file) 137 | return True 138 | return False 139 | 140 | 141 | 142 | # ---- About Tab Functions ---- 143 | 144 | @eel.expose 145 | def get_GICutscenes_ver(): 146 | if SCRIPT_FILE: 147 | process = subprocess.Popen([SCRIPT_FILE, "--version"], stdout=subprocess.PIPE, creationflags=subprocess.CREATE_NO_WINDOW) 148 | answer = process.communicate()[0] 149 | try: 150 | text = answer.decode('utf-8') 151 | except UnicodeDecodeError: 152 | text = answer.decode(os.device_encoding(0)) 153 | return text.strip() 154 | 155 | def get_ffmpeg_output(cmd): 156 | process = subprocess.Popen([ 157 | FFMPEG, "-hide_banner"] + cmd, 158 | stdout=subprocess.PIPE, creationflags=subprocess.CREATE_NO_WINDOW 159 | ) 160 | answer = process.communicate()[0] 161 | try: 162 | text = answer.decode('utf-8') 163 | except UnicodeDecodeError: 164 | text = answer.decode(os.device_encoding(0)) 165 | return text 166 | 167 | @eel.expose 168 | def get_ffmpeg_ver(): 169 | def find_ver(text): 170 | return text.splitlines()[0].split("ffmpeg version")[-1].strip().split()[0] 171 | def find_year(text): 172 | match = re.findall(r'\b([1-3][0-9]{3})\b', text) 173 | if match is not None: 174 | return match 175 | try: 176 | text = get_ffmpeg_output(['-version']) 177 | final = {'ver': find_ver(text.strip()), 'year': find_year(text.strip())} 178 | return final 179 | except: return {} 180 | 181 | 182 | def parse_releases(relative_path): 183 | r = requests.get(f'https://api.github.com/repos/{relative_path}/tags') 184 | if r.status_code == 200: 185 | answer = r.json() 186 | latest = answer[0]["name"] 187 | return latest 188 | else: 189 | r = requests.get(f'https://github.com/{relative_path}/tags') 190 | links = [] 191 | for line in r.text.split("\n"): 192 | match = re.search(r"href=\"(.+)\">", line) 193 | if match: 194 | links.append(match.group(1)) 195 | 196 | pat = re.compile(r'releases\/tag') 197 | content = [ s for s in links if pat.findall(s) ] 198 | latest = sorted(content)[-1].split("/")[-1] 199 | return latest 200 | 201 | @eel.expose 202 | def get_latest_ui_version(): 203 | return parse_releases('SuperZombi/GICutscenesUI') 204 | 205 | @eel.expose 206 | def get_latest_script_version(): 207 | return parse_releases('ToaHartor/GI-cutscenes') 208 | 209 | @eel.expose 210 | def compare_version_files(): 211 | if SCRIPT_FILE: 212 | local_ver_file = os.path.join( 213 | os.path.dirname(SCRIPT_FILE), 214 | "versions.json" 215 | ) 216 | if os.path.exists(local_ver_file): 217 | with open(local_ver_file, 'r') as file: 218 | LOCAL = file.read() 219 | 220 | try: 221 | r = requests.get("https://raw.githubusercontent.com/ToaHartor/GI-cutscenes/main/versions.json") 222 | GLOBAL = r.text 223 | 224 | if json.loads(LOCAL) == json.loads(GLOBAL): 225 | return {"success": True, "status": True} 226 | else: 227 | return {"success": True, "status": False} 228 | except: None 229 | return {"success": False} 230 | 231 | @eel.expose 232 | def download_latest_version_file(): 233 | r = requests.get("https://raw.githubusercontent.com/ToaHartor/GI-cutscenes/main/versions.json") 234 | local_ver_file = os.path.join(os.path.dirname(SCRIPT_FILE), "versions.json") 235 | with open(local_ver_file, 'w') as file: 236 | file.write(r.text) 237 | 238 | if file_in_temp(SCRIPT_FILE): 239 | local_ver_file = os.path.join(os.getcwd(), "versions.json") 240 | with open(local_ver_file, 'w') as file: 241 | file.write(r.text) 242 | 243 | 244 | # ---- Logger Functions ---- 245 | 246 | def send_message_to_ui_output(type_, message): 247 | eel.putMessageInOutput(type_, message)() 248 | 249 | def log_subprocess_output(pipe, process=None): 250 | for line in pipe: 251 | if process: 252 | if STOPED_BY_USER: 253 | process.kill() 254 | send_message_to_ui_output("console", line.strip()) 255 | 256 | 257 | # ---- Explorer Functions ---- 258 | 259 | def get_disks(): 260 | import win32api 261 | return [d for d in win32api.GetLogicalDriveStrings()[0]] 262 | 263 | @eel.expose 264 | def get_all_fonts(): 265 | root = Tk() 266 | root.withdraw() 267 | return sorted(set(font.families())) 268 | 269 | @eel.expose 270 | def ask_files(): 271 | root = Tk() 272 | root.withdraw() 273 | root.wm_attributes('-topmost', 1) 274 | files = askopenfilenames(initialdir=GENSHIN_FOLDER, parent=root, 275 | filetypes=[("Genshin Impact Cutscene", "*.usm"), ("All files", "*.*")] 276 | ) 277 | return files 278 | 279 | def ask_folder(): 280 | root = Tk() 281 | root.withdraw() 282 | root.wm_attributes('-topmost', 1) 283 | return askdirectory(parent=root) 284 | 285 | @eel.expose 286 | def get_output_folder(): 287 | return OUTPUT_F 288 | 289 | @eel.expose 290 | def get_subtitles_folder(): 291 | return SUBTITLES_F 292 | 293 | @eel.expose 294 | def ask_output_folder(): 295 | global OUTPUT_F 296 | folder = ask_folder() 297 | if folder: OUTPUT_F = folder 298 | return OUTPUT_F 299 | 300 | @eel.expose 301 | def ask_subtitles_folder(): 302 | global SUBTITLES_F 303 | folder = ask_folder() 304 | if folder: SUBTITLES_F = folder 305 | return SUBTITLES_F 306 | 307 | @eel.expose 308 | def open_output_folder(): 309 | if os.name == "nt": 310 | subprocess.run(['explorer', OUTPUT_F], creationflags=subprocess.CREATE_NO_WINDOW) 311 | elif os.name == "posix": 312 | subprocess.run(['xdg-open', OUTPUT_F], creationflags=subprocess.CREATE_NO_WINDOW) 313 | 314 | def find_genshin_folder(): 315 | if os.name == "nt": 316 | templates = ( 317 | ('Games', 'Genshin Impact'), 318 | ('Program Files', 'Genshin Impact'), 319 | ('Program Files', 'HoYoPlay', 'games') 320 | ) 321 | for _disk in get_disks(): 322 | disk = f'{_disk}:'+os.sep 323 | for template in templates: 324 | path = os.path.join(disk, *template) 325 | if os.path.exists(path): 326 | assets = os.path.join(path, "Genshin Impact game", "GenshinImpact_Data", "StreamingAssets", "VideoAssets", "StandaloneWindows64") 327 | if os.path.exists(assets): return assets 328 | print("[WARN] Assets not found!") 329 | GENSHIN_FOLDER = find_genshin_folder() 330 | 331 | 332 | # ---- Subtitles Functions ---- 333 | @eel.expose 334 | def make_subs_preview(args): 335 | params = { 336 | "tempfile": "temp.ass", 337 | "width": 1920, "height": 1080, 338 | **args 339 | } 340 | width, height, tempfile = params.pop("width"), params.pop("height"), params.pop("tempfile") 341 | make_subs_template(text=SUBTITLES_PREVIEW_TEXT(params.pop("lang")), file=tempfile, **params) 342 | cmd = [ 343 | FFMPEG, '-f', 'lavfi', '-i', 344 | f'color=color=black@0.0:size={width}x{height},format=rgba,subtitles={tempfile}:alpha=1', 345 | '-vframes', '1', '-f', 'image2pipe', '-vcodec', 'png', '-' 346 | ] 347 | process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, creationflags=subprocess.CREATE_NO_WINDOW) 348 | output, error = process.communicate() 349 | os.remove(tempfile) 350 | if process.returncode == 0: 351 | img_base64 = base64.b64encode(output).decode('utf-8') 352 | data_url = f"data:image/png;base64,{img_base64}" 353 | return data_url 354 | 355 | 356 | # ---- GPU Functions ---- 357 | GPU_args = { 358 | "nvidia": { 359 | "decode": 'cuda', 360 | "encode": 'h264_nvenc' 361 | }, 362 | "intel": { 363 | "decode": 'qsv', 364 | "encode": 'h264_qsv' 365 | }, 366 | "amd": { 367 | "encode": "h264_amf" 368 | } 369 | } 370 | def test_encoder(encoder, decoder=None): 371 | try: 372 | cmd = [ 373 | FFMPEG, '-f', 'lavfi', '-i', 374 | 'color=black:size=360x360', 375 | '-vframes', '1', '-f', "null", 376 | '-vcodec', encoder, '-' 377 | ] 378 | process = subprocess.Popen(cmd, creationflags=subprocess.CREATE_NO_WINDOW) 379 | returncode = process.wait() 380 | if returncode == 0: return True 381 | except: None 382 | 383 | @eel.expose 384 | def get_ffmpeg_supports(): 385 | suported = [] 386 | for gpu, args in GPU_args.items(): 387 | if test_encoder(args.get("encode")): 388 | suported.append(gpu) 389 | return suported 390 | 391 | 392 | # ---- MAIN Functions ---- 393 | STOPED_BY_USER = None 394 | @eel.expose 395 | def stop_work(): 396 | global STOPED_BY_USER 397 | STOPED_BY_USER = True 398 | 399 | @eel.expose 400 | def start_work(files, args): 401 | global STOPED_BY_USER 402 | STOPED_BY_USER = False 403 | send_message_to_ui_output("event", "start") 404 | file_lenth = len(files) 405 | send_message_to_ui_output("file_count", [0, file_lenth]) 406 | # Make folders 407 | temp_folder = resource_path("Temp") 408 | if not os.path.exists(temp_folder): 409 | os.mkdir(temp_folder) 410 | if not os.path.exists(OUTPUT_F): 411 | os.mkdir(OUTPUT_F) 412 | 413 | OLD_DIR = os.getcwd() 414 | os.chdir(os.path.dirname(SCRIPT_FILE)) 415 | 416 | for i, file in enumerate(files): 417 | if STOPED_BY_USER: break 418 | else: 419 | send_message_to_ui_output("file_count", [i, file_lenth]) 420 | send_message_to_ui_output("event", "copy_files") 421 | send_message_to_ui_output("work_file", file) 422 | send_message_to_ui_output("console", f"----- {os.path.basename(file)} -----") 423 | send_message_to_ui_output("event", "run_demux") 424 | # MAIN CALL 425 | p_status = 0 426 | if CONSOLE_DEBUG_MODE: 427 | subprocess.call([SCRIPT_FILE, 'demuxUsm', file, '--output', OUTPUT_F]) 428 | else: 429 | process = subprocess.Popen([SCRIPT_FILE, 'demuxUsm', file, '--output', OUTPUT_F], encoding=os.device_encoding(0), universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, creationflags=subprocess.CREATE_NO_WINDOW) 430 | with process.stdout: 431 | log_subprocess_output(process.stdout) 432 | p_status = process.wait() 433 | 434 | if p_status != 0: 435 | send_message_to_ui_output("event", "error") 436 | send_message_to_ui_output("sub_work", {"name": "keys", "status": False}) 437 | else: 438 | send_message_to_ui_output("event", "rename_files") 439 | 440 | # Rename to m2v 441 | old_file_name = os.path.splitext(os.path.basename(file))[0] 442 | file_name = str(old_file_name) + ".ivf" 443 | new_file_name = str(old_file_name) + ".m2v" 444 | file_name = os.path.join(OUTPUT_F, file_name) 445 | new_file_name = os.path.join(OUTPUT_F, new_file_name) 446 | if os.path.exists(file_name): 447 | if os.path.exists(new_file_name): os.remove(new_file_name) 448 | os.rename(file_name, new_file_name) 449 | else: 450 | send_message_to_ui_output("console", "\n") 451 | send_message_to_ui_output("event", "error") 452 | send_message_to_ui_output("sub_work", {"name": "keys", "status": False}) 453 | continue 454 | 455 | # Delete hca encoded Audio (cuz wav files decoded) 456 | for index in [0, 1, 2, 3]: 457 | f = str(old_file_name) + "_" + str(index) + ".hca" 458 | f = os.path.join(OUTPUT_F, f) 459 | if os.path.exists(f): 460 | os.remove(f) 461 | 462 | # Merge Video and Audio 463 | if args['merge']: 464 | if STOPED_BY_USER: break 465 | else: 466 | audio_index = int(args['audio_index']) 467 | audio_file = os.path.join(OUTPUT_F , str(old_file_name) + "_" + str(audio_index) + ".wav") 468 | output_file = os.path.join(OUTPUT_F, str(old_file_name) + ".mp4") 469 | 470 | # Subtitles 471 | subtitles_file = None 472 | if args['subtitles']: 473 | send_message_to_ui_output("console", "\nSearching for subtitles") 474 | 475 | if args.get('subtitles_provider') == "local": 476 | if args.get('subtitles_folder'): 477 | subtitles = find_subtitles( 478 | old_file_name, 479 | provider=args.get('subtitles_folder'), 480 | lang=args.get('subtitles_lang') 481 | ) 482 | elif args.get('subtitles_provider') == "url": 483 | if args.get('subtitles_url'): 484 | subtitles = find_subtitles( 485 | old_file_name, 486 | provider=args.get('subtitles_url'), 487 | lang=args.get('subtitles_lang') 488 | ) 489 | else: 490 | subtitles = find_subtitles( 491 | old_file_name, 492 | provider=args.get('subtitles_provider'), 493 | lang=args.get('subtitles_lang') 494 | ) 495 | 496 | if not subtitles: 497 | send_message_to_ui_output("console", "Subtitles not found!") 498 | send_message_to_ui_output("sub_work", {"name": "subtitles", "status": False}) 499 | else: 500 | send_message_to_ui_output("console", "Converting subtitles") 501 | send_message_to_ui_output("sub_work", {"name": "subtitles", "status": True}) 502 | subtitles_file = os.path.join(OUTPUT_F, str(old_file_name) + ".ass") 503 | srt_to_ass( 504 | subtitles, 505 | subtitles_file, 506 | font_name=args.get('subtitles_font'), 507 | font_size=args.get('subtitles_fontsize'), 508 | text_color=args.get('subtitles_text_color'), 509 | outline_color=args.get('subtitles_outline_color'), 510 | outline_width=args.get('subtitles_outline_width'), 511 | letter_spacing=args.get('subtitles_letter_spacing'), 512 | bold=args.get('subtitles_bold'), 513 | italic=args.get('subtitles_italic') 514 | ) 515 | 516 | # Merging 517 | send_message_to_ui_output("event", "run_merge") 518 | send_message_to_ui_output("console", "\nStarting ffmpeg") 519 | if os.path.exists(output_file): 520 | send_message_to_ui_output("console", f'File {output_file} already exists.') 521 | os.remove(output_file) 522 | 523 | send_message_to_ui_output("console", "Working ffmpeg...") 524 | p_status = 0 525 | bitrate = int(args['video_quality']) * 1000 526 | gp_args = GPU_args[args.get('gpu')] if args.get('gpu') and args.get('gpu') in GPU_args else {} 527 | command = [FFMPEG, '-hide_banner'] 528 | if "decode" in gp_args: 529 | if gp_args['decode'] == "qsv" and subtitles_file: pass 530 | else: 531 | command += ['-hwaccel', gp_args['decode']] 532 | command += [ 533 | '-i', new_file_name, 534 | '-i', audio_file 535 | ] 536 | if subtitles_file: 537 | subs_file = subtitles_file.replace("\\", "/").replace(":", "\\\\\\:") 538 | command += ["-vf", f'subtitles={subs_file}'] 539 | if "encode" in gp_args: 540 | command += ['-c:v', gp_args['encode']] 541 | command += [ 542 | '-b:v', str(bitrate), 543 | '-b:a', '192K', 544 | output_file 545 | ] 546 | 547 | if CONSOLE_DEBUG_MODE: 548 | subprocess.call(command) 549 | else: 550 | process = subprocess.Popen(command, encoding='utf-8', universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, creationflags=subprocess.CREATE_NO_WINDOW) 551 | with process.stderr: 552 | log_subprocess_output(process.stderr, process) 553 | p_status = process.wait() 554 | 555 | if p_status != 0: 556 | if os.path.exists(output_file): os.remove(output_file) 557 | send_message_to_ui_output("event", "error") 558 | continue 559 | else: 560 | send_message_to_ui_output("console", "Merging complete!") 561 | if args['delete_after_merge']: 562 | send_message_to_ui_output("console", "Removing trash...") 563 | if subtitles_file: os.remove(subtitles_file) 564 | files_to_remove = [ 565 | old_file_name + ".m2v", 566 | *[f"{old_file_name}_{i}.wav" for i in [0, 1, 2, 3]] 567 | ] 568 | files_to_remove = list(map(lambda x: os.path.join(OUTPUT_F, x),files_to_remove)) 569 | for f in files_to_remove: 570 | os.remove(f) 571 | send_message_to_ui_output("console", "OK") 572 | 573 | if i != file_lenth - 1: 574 | send_message_to_ui_output("console", "\n") 575 | 576 | send_message_to_ui_output("event", "ok") 577 | 578 | send_message_to_ui_output("event", "finish") 579 | if STOPED_BY_USER: 580 | send_message_to_ui_output("console", "\nStoped by user") 581 | send_message_to_ui_output("event", "stoped") 582 | else: 583 | send_message_to_ui_output("file_count", [file_lenth, file_lenth]) 584 | shutil.rmtree(temp_folder) 585 | os.chdir(OLD_DIR) 586 | 587 | 588 | eel.init(resource_path("web")) 589 | 590 | browsers = ['chrome', 'default'] 591 | for browser in browsers: 592 | try: 593 | eel.start("main.html", size=(600, 800), mode=browser, port=0) 594 | break 595 | except Exception: 596 | print(f"Failed to launch the app using {browser.title()} browser") 597 | -------------------------------------------------------------------------------- /GICutscenesUI/subtitles.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | import requests 4 | from io import StringIO 5 | import pysubs2 6 | 7 | 8 | def hex_to_rgb(hex): return tuple(int(hex.lstrip('#')[i:i+2], 16) for i in (0, 2, 4)) 9 | 10 | def find_subtitles(name, lang, provider): 11 | filename = f"{name}_{lang}.srt" 12 | if provider.startswith('http'): 13 | pattern = r'https://(github|gitlab)\.com/([^/]+)/([^/]+)' 14 | match = re.search(pattern, provider) 15 | if match: 16 | site = match.group(1) 17 | author = match.group(2) 18 | repository = match.group(3) 19 | 20 | if site == "gitlab": 21 | url = f"https://gitlab.com/{author}/{repository}/raw/master/Subtitle/{lang}/{filename}" 22 | elif site == "github": 23 | url = f"https://raw.githubusercontent.com/{author}/{repository}/main/Subtitle/{lang}/{filename}" 24 | else: 25 | url = f"{provider}/{lang}/{filename}" 26 | r = requests.get(url) 27 | if r.ok: 28 | return StringIO(r.content.decode(r.encoding)) 29 | else: 30 | path = os.path.join(provider, lang, filename) 31 | if os.path.exists(path): 32 | with open(path, 'r', encoding='utf-8') as f: 33 | data = f.read() 34 | return StringIO(data) 35 | 36 | def apply_styles( 37 | font_name="Arial", font_size=14, 38 | text_color='#ffffff', 39 | outline_color='#000000', 40 | outline_width=1, letter_spacing=0, 41 | bold=False, italic=False 42 | ): 43 | style = pysubs2.SSAStyle() 44 | style.fontname = font_name 45 | style.fontsize = font_size 46 | style.primarycolor = pysubs2.Color(*hex_to_rgb(text_color)) 47 | style.outlinecolor = pysubs2.Color(*hex_to_rgb(outline_color)) 48 | style.outline = outline_width 49 | style.shadow = 0 50 | style.spacing = letter_spacing 51 | style.bold = bold 52 | style.italic = italic 53 | return style 54 | 55 | def srt_to_ass(srt_file, ass_file, **kwargs): 56 | subs = pysubs2.SSAFile.from_string(srt_file.read(), format_="srt") 57 | subs.styles["Default"] = apply_styles(**kwargs) 58 | subs.save(ass_file, format_="ass") 59 | 60 | def make_subs_template(text, file, **kwargs): 61 | subs = pysubs2.SSAFile() 62 | subs.styles["Default"] = apply_styles(**kwargs) 63 | subs.events.append( 64 | pysubs2.SSAEvent(start=0, end=1000, text=text) 65 | ) 66 | subs.save(file) 67 | -------------------------------------------------------------------------------- /GICutscenesUI/web/images/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SuperZombi/GICutscenesUI/c49bda9bc53b9899e9bbdf86b6be3e91c403ab9c/GICutscenesUI/web/images/icon.ico -------------------------------------------------------------------------------- /GICutscenesUI/web/locales/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "home": "Home", 3 | "settings": "Settings", 4 | "select_files": "Add Files", 5 | "select_files_dots": "Select Files:", 6 | "clear_files": "Clear", 7 | "version": "version:", 8 | "about": "About", 9 | "translations": "Translations:", 10 | 11 | "change": "Change", 12 | "reset": "Reset", 13 | "reset_title": "Delete Settings File", 14 | "remove": "Remove", 15 | "save": "Save", 16 | "save_title": "Save Settings to File", 17 | "language": "Language:", 18 | "output_folder": "Output Folder:", 19 | "console_button": "Console", 20 | "clear": "Clear", 21 | "start": "START", 22 | "stop": "STOP", 23 | "appearance": "Appearance", 24 | "theme": "Theme:", 25 | "light": "Light", 26 | "dark": "Dark", 27 | "gpu": "GPU:", 28 | "gpu_none": "None", 29 | 30 | "merging_area": "Merging", 31 | "merge": "Merge Video and Audio", 32 | "using_ffmpeg_tooltip": "With using ffmpeg", 33 | "audio_track": "Audio track:", 34 | "quality": "Quality:", 35 | "delete_after_merge": "Delete unnecessary files after merging", 36 | 37 | "subtitles_area": "Subtitles", 38 | "subtitles_checkbox": "Add subtitles", 39 | "subtitles_tooltip": "Works only if merging is enabled", 40 | "subtitles_provider": "Subtitles provider:", 41 | "subtitles_provider_local": "Local", 42 | "subtitles_provider_url": "URL", 43 | "subtitles_language": "Language:", 44 | "subtitles_font": "Font:", 45 | "subtitles_font_size": "Font size:", 46 | "subtitles_font_default": "Default", 47 | "subtitles_customization_button": "Customization", 48 | "subtitles_customization_header": "Customization of Subtitles", 49 | "subtitles_text_color": "Text color:", 50 | "subtitles_outline_color": "Outline color:", 51 | "subtitles_outline_width": "Outline width:", 52 | "subtitles_letter_spacing": "Letter spacing:", 53 | "subtitles_bold": "Bold", 54 | "subtitles_italic": "Italic", 55 | 56 | "no_files": "No Files!", 57 | "saved": "Saved!", 58 | "confirm_delete_settings": "Are you sure you want to Delete all Settings?", 59 | 60 | "copy_files": "Copying Files...", 61 | "run_demux": "Converting and Decrypting...", 62 | "rename_files": "Renaming Files...", 63 | "run_merge": "Merging Video and Audio...", 64 | "open_output_dir": "Open Output Folder", 65 | "stopped": "Stopped", 66 | 67 | "new_update": "New update!", 68 | "update": "Update", 69 | 70 | "donate_title": "Enjoying this free program?", 71 | "donate_description": "Consider supporting development with a small donation!", 72 | "donate_thanks": "Thank you for your support!", 73 | "donate_button": "Donate" 74 | } 75 | -------------------------------------------------------------------------------- /GICutscenesUI/web/locales/fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "__root__": { 3 | "inherit": "en" 4 | }, 5 | "home": "Menu principal", 6 | "settings": "Paramètres", 7 | "select_files": "Choix des fichiers", 8 | "select_files_dots": "Choix des fichiers:", 9 | "version": "version:", 10 | "about": "À propos", 11 | "translations": "Traductions:", 12 | 13 | "change": "Modifier", 14 | "reset": "Réinitialiser", 15 | "reset_title": "Supprimer le fichier de paramètres", 16 | "save": "Enregistrer", 17 | "save_title": "Enregistrer le fichier de paramètres", 18 | "language": "Langue:", 19 | "output_folder": "Dossier de sortie:", 20 | "merge": "Fusionner la video et l'audio", 21 | "clear": "Effacer", 22 | "start": "DÉMARRER", 23 | "appearance": "Apparence", 24 | "theme": "Thème:", 25 | "light": "Clair", 26 | "dark": "Sombre", 27 | 28 | "no_files": "Aucun fichier choisi!", 29 | "select_script_file": "Choisissez le script!", 30 | "saved": "Enregistré!", 31 | "confirm_delete_settings": "Êtes-vous sûr de vouloir supprimer vos paramètres?", 32 | 33 | "using_ffmpeg_tooltip": "Utilise ffmpeg", 34 | "track_index_tooltip": "Indice de piste", 35 | 36 | "copy_files": "Copie des fichiers...", 37 | "run_demux": "Conversion et Déchiffrement...", 38 | "rename_files": "Renommage des fichiers...", 39 | "run_merge": "Fusion des pistes video et audio...", 40 | "open_output_dir": "Ouvrir le dossier de sortie", 41 | 42 | "new_update": "Mise à jour disponible!" 43 | } 44 | -------------------------------------------------------------------------------- /GICutscenesUI/web/locales/id.json: -------------------------------------------------------------------------------- 1 | { 2 | "__root__": { 3 | "inherit": "en" 4 | }, 5 | "home": "Beranda", 6 | "settings": "Pengaturan", 7 | "select_files": "Tambahkan Berkas", 8 | "select_files_dots": "Pilih Berkas:", 9 | "clear_files": "Bersihkan", 10 | "version": "Versi:", 11 | "about": "Tentang", 12 | "translations": "Terjemahan:", 13 | 14 | "change": "Ubah", 15 | "reset": "Setel Ulang", 16 | "reset_title": "Hapus Berkas Pengaturan", 17 | "remove": "Hapus", 18 | "save": "Simpan", 19 | "save_title": "Simpan Pengaturan ke Berkas", 20 | "language": "Bahasa:", 21 | "output_folder": "Folder Output:", 22 | "console_button": "konsol", 23 | "delete_after_merge": "Hapus berkas tidak diperlukan setelah penggabungan", 24 | "clear": "Bersihkan", 25 | "start": "Mulai", 26 | "stop": "BERHENTI", 27 | "appearance": "Tampilan", 28 | "theme": "Tema:", 29 | "light": "Terang", 30 | "dark": "Gelap", 31 | "gpu": "GPU:", 32 | "gpu_none": "Nonaktif", 33 | 34 | "merging_area": "Menggabungkan", 35 | "merge": "Gabungkan Video dan Audio", 36 | "using_ffmpeg_tooltip": "Memakai ffmpeg", 37 | "audio_track": "Trek audio:", 38 | "quality": "Kualitas:", 39 | 40 | "subtitles_area": "Subtitle", 41 | "subtitles_checkbox": "Tambahkan subtitle", 42 | "subtitles_tooltip": "Hanya bekerja jika penggabungan diaktifkan", 43 | "subtitles_provider": "Penyedia subtitle:", 44 | "subtitles_provider_local": "Lokal", 45 | "subtitles_provider_url": "URL", 46 | "subtitles_language": "Bahasa:", 47 | "subtitles_font": "Huruf:", 48 | "subtitles_font_size": "Ukuran huruf:", 49 | "subtitles_font_default": "Default", 50 | "subtitles_customization_button": "Personalisasi", 51 | "subtitles_customization_header": "Personalisasi Subtitle", 52 | "subtitles_text_color": "Warna teks:", 53 | "subtitles_outline_color": "Warna outline:", 54 | "subtitles_outline_width": "Lebar outline:", 55 | "subtitles_letter_spacing": "Jarak antar huruf:", 56 | "subtitles_bold": "Tebal", 57 | "subtitles_italic": "Miring", 58 | 59 | "no_files": "Berkas Kosong!", 60 | "select_script_file": "Pilih Berkas Script!", 61 | "saved": "Tersimpan!", 62 | "confirm_delete_settings": "Apakah Anda Yakin Ingin Menghapus Semua Pengaturan?", 63 | 64 | "copy_files": "Menyalin Berkas...", 65 | "run_demux": "Mengonversi dan Mendekripsi...", 66 | "rename_files": "Ubah Nama Berkas...", 67 | "run_merge": "Menggabungkan Video dan Audio...", 68 | "open_output_dir": "Buka Folder Output", 69 | "stopped": "Terhenti", 70 | 71 | "new_update": "Pembaruan Baru!", 72 | "update": "Perbarui", 73 | 74 | "donate_title": "Suka Program Gratis Ini?", 75 | "donate_description": "Pertimbangkan untuk mendukung pengembangan dengan donasi kecil!", 76 | "donate_thanks": "Terima kasih atas dukungan anda!", 77 | "donate_button": "Donasi" 78 | } 79 | -------------------------------------------------------------------------------- /GICutscenesUI/web/locales/jp.json: -------------------------------------------------------------------------------- 1 | { 2 | "__root__": { 3 | "inherit": "en" 4 | }, 5 | "home": "ホーム", 6 | "settings": "設定", 7 | "select_files": "ファイルを追加", 8 | "select_files_dots": "ファイルを選択:", 9 | "clear_files": "削除", 10 | "version": "バージョン:", 11 | "about": "詳細", 12 | "translations": "翻訳:", 13 | 14 | "change": "変更", 15 | "reset": "設定の初期化", 16 | "reset_title": "設定ファイルの削除", 17 | "save": "保存", 18 | "save_title": "設定ファイルを保存", 19 | "language": "言語:", 20 | "output_folder": "出力先フォルダ:", 21 | "merge": "映像と音声の統合", 22 | "delete_after_merge": "融合後に不要なファイルを削除", 23 | "clear": "ログを削除", 24 | "start": "開始", 25 | "stop": "停止", 26 | "appearance": "外観", 27 | "theme": "テーマ:", 28 | "light": "ライト", 29 | "dark": "ダーク", 30 | 31 | "no_files": "ファイルが選択されていません", 32 | "select_script_file": "スクリプトファイルを選択してください", 33 | "saved": "保存完了", 34 | "confirm_delete_settings": "本当に全ての設定を初期化してもよろしいですか?", 35 | 36 | "using_ffmpeg_tooltip": "ffmpegを使う", 37 | "track_index_tooltip": "トラックインデックス", 38 | 39 | "copy_files": "ファイルコピー中...", 40 | "run_demux": "変換と復号中...", 41 | "rename_files": "ファイル名置換中...", 42 | "run_merge": "映像と音声を融合中...", 43 | "open_output_dir": "出力フォルダを開く", 44 | "stopped": "停止しました", 45 | 46 | "new_update": "新しいアップデートがあります", 47 | "update": "アップデート" 48 | } 49 | -------------------------------------------------------------------------------- /GICutscenesUI/web/locales/kr.json: -------------------------------------------------------------------------------- 1 | { 2 | "__root__": { 3 | "inherit": "en" 4 | }, 5 | "home": "홈 화면", 6 | "settings": "설정", 7 | "select_files": "파일 선택", 8 | "select_files_dots": "파일 선택:", 9 | "version": "버전:", 10 | "about": "정보", 11 | "translations": "번역:", 12 | 13 | "change": "선택", 14 | "reset": "리셋", 15 | "reset_title": "설정 파일을 삭제", 16 | "save": "저장", 17 | "save_title": "설정 파일을 저장", 18 | "language": "언어:", 19 | "output_folder": "출력 폴더 지정:", 20 | "merge": "비디오와 오디오를 통합", 21 | "clear": "삭제", 22 | "start": "시작", 23 | "appearance": "언어 및 색상", 24 | "theme": "색상:", 25 | "light": "화이트", 26 | "dark": "블랙", 27 | 28 | "no_files": "파일이 존재하지않음!", 29 | "select_script_file": "스크립트 파일을 선택!", 30 | "saved": "저장 완료!", 31 | "confirm_delete_settings": "정말로 이 설정을 삭제하시겠습니까?", 32 | 33 | "using_ffmpeg_tooltip": "ffmpeg 파일을 사용", 34 | "track_index_tooltip": "오디오 언어 목록", 35 | 36 | "copy_files": "파일 복사중...", 37 | "run_demux": "전환 및 해독중...", 38 | "rename_files": "파일 제목을 변경하는중...", 39 | "run_merge": "비디오와 오디오를 통합하는중...", 40 | "open_output_dir": "폴더 열기", 41 | 42 | "new_update": "새로운 업데이트!" 43 | } 44 | -------------------------------------------------------------------------------- /GICutscenesUI/web/locales/ru.json: -------------------------------------------------------------------------------- 1 | { 2 | "__root__": { 3 | "inherit": "en" 4 | }, 5 | "home": "Главная", 6 | "settings": "Настройки", 7 | "select_files": "Добавить Файлы", 8 | "select_files_dots": "Выберите Файлы:", 9 | "clear_files": "Очистить", 10 | "version": "версия:", 11 | "about": "О программе", 12 | "translations": "Переводы:", 13 | 14 | "change": "Изменить", 15 | "reset": "Сбросить", 16 | "reset_title": "Удалить Файл с Настройками", 17 | "remove": "Убрать", 18 | "save": "Сохранить", 19 | "save_title": "Сохранить Настройки в Файл", 20 | "language": "Язык:", 21 | "output_folder": "Папка Вывода:", 22 | "console_button": "Консоль", 23 | "clear": "Очистить", 24 | "start": "СТАРТ", 25 | "stop": "СТОП", 26 | "appearance": "Внешний вид", 27 | "theme": "Тема:", 28 | "light": "Светлая", 29 | "dark": "Тёмная", 30 | "gpu": "Видеокарта:", 31 | "gpu_none": "Нет", 32 | 33 | "merging_area": "Рендеринг", 34 | "merge": "Объединить Видео и Аудио", 35 | "using_ffmpeg_tooltip": "С помощью ffmpeg", 36 | "audio_track": "Аудио дорожка:", 37 | "quality": "Качество:", 38 | "delete_after_merge": "Удалить ненужные файлы после объединения", 39 | 40 | "subtitles_area": "Субтитры", 41 | "subtitles_checkbox": "Добавить субтитры", 42 | "subtitles_tooltip": "Работает только если включен рендеринг", 43 | "subtitles_provider": "Расположение субтитров:", 44 | "subtitles_provider_local": "Выбрать папку", 45 | "subtitles_language": "Язык:", 46 | "subtitles_font": "Шрифт:", 47 | "subtitles_font_size": "Размер шрифта:", 48 | "subtitles_font_default": "По умолчанию", 49 | "subtitles_customization_button": "Внешний вид", 50 | "subtitles_customization_header": "Настройка субтитров", 51 | "subtitles_text_color": "Цвет текста:", 52 | "subtitles_outline_color": "Цвет обводки:", 53 | "subtitles_outline_width": "Ширина обводки:", 54 | "subtitles_letter_spacing": "Межбуквенный интервал:", 55 | "subtitles_bold": "Жирный", 56 | "subtitles_italic": "Курсивный", 57 | 58 | "no_files": "Нет Файлов!", 59 | "saved": "Сохранено!", 60 | "confirm_delete_settings": "Вы уверены, что хотите Удалить все Настройки?", 61 | 62 | "copy_files": "Копирование файлов...", 63 | "run_demux": "Конвертация и Расшифровка...", 64 | "rename_files": "Переименование файлов...", 65 | "run_merge": "Объединение Видео и Аудио...", 66 | "open_output_dir": "Открыть Папку Вывода", 67 | "stopped": "Остановлено", 68 | 69 | "new_update": "Новое обновление!", 70 | "update": "Обновить", 71 | 72 | "donate_title": "Нравится эта бесплатная программа?", 73 | "donate_description": "Поддержите развитие небольшим пожертвованием!", 74 | "donate_thanks": "Спасибо за Вашу поддержку!", 75 | "donate_button": "Пожертвовать" 76 | } 77 | -------------------------------------------------------------------------------- /GICutscenesUI/web/locales/subtitles_preview.json: -------------------------------------------------------------------------------- 1 | { 2 | "en": "Subtitles, will look like this!", 3 | "ru": "Субтитры, будут выглядеть так!", 4 | "id": "Subtitle, akan terlihat seperti ini!", 5 | } 6 | -------------------------------------------------------------------------------- /GICutscenesUI/web/locales/uk.json: -------------------------------------------------------------------------------- 1 | { 2 | "__root__": { 3 | "inherit": "ru" 4 | }, 5 | "home": "Головна", 6 | "settings": "Налаштування", 7 | "select_files": "Додати Файли", 8 | "select_files_dots": "Виберіть Файли:", 9 | "clear_files": "Очистити", 10 | "version": "версія:", 11 | "about": "Про програму", 12 | "translations": "Переклади:", 13 | 14 | "change": "Змінити", 15 | "reset": "Скинути", 16 | "reset_title": "Видалити Файл з Налаштуваннями", 17 | "remove": "Прибрати", 18 | "save": "Зберегти", 19 | "save_title": "Зберегти Налаштування у Файл", 20 | "language": "Мова:", 21 | "output_folder": "Папка Виводу:", 22 | "console_button": "Консоль", 23 | "clear": "Очистити", 24 | "start": "СТАРТ", 25 | "stop": "СТОП", 26 | "appearance": "Зовнішній вигляд", 27 | "theme": "Тема:", 28 | "light": "Світла", 29 | "dark": "Темна", 30 | "gpu": "Відеокарта:", 31 | "gpu_none": "Ні", 32 | 33 | "merging_area": "Рендеринг", 34 | "merge": "Об'єднати Відео та Аудіо", 35 | "using_ffmpeg_tooltip": "За допомогою ffmpeg", 36 | "audio_track": "Аудіо доріжка:", 37 | "quality": "Якість:", 38 | "delete_after_merge": "Видалити непотрібні файли після об’єднання", 39 | 40 | "subtitles_area": "Субтитри", 41 | "subtitles_checkbox": "Додати субтитри", 42 | "subtitles_tooltip": "Працює тільки якщо увімкнено рендеринг", 43 | "subtitles_provider": "Розташування субтитрів:", 44 | "subtitles_provider_local": "Вибрати папку", 45 | "subtitles_language": "Мова:", 46 | "subtitles_font": "Шрифт:", 47 | "subtitles_font_size": "Розмір шрифту:", 48 | "subtitles_font_default": "Стандартний", 49 | "subtitles_customization_button": "Зовнішній вигляд", 50 | "subtitles_customization_header": "Налаштування субтитрів", 51 | "subtitles_text_color": "Колір тексту:", 52 | "subtitles_outline_color": "Колір контуру:", 53 | "subtitles_outline_width": "Ширина контуру:", 54 | "subtitles_letter_spacing": "Міжлітерний інтервал:", 55 | "subtitles_bold": "Жирний", 56 | "subtitles_italic": "Курсивний", 57 | 58 | "no_files": "Немає Файлів!", 59 | "saved": "Збережено!", 60 | "confirm_delete_settings": "Ви впевнені, що хочете Видалити всі Налаштування?", 61 | 62 | "copy_files": "Копіювання файлів...", 63 | "run_demux": "Конвертація та Розшифрування...", 64 | "rename_files": "Перейменування файлів...", 65 | "run_merge": "Об'єднання Відео та Аудіо...", 66 | "open_output_dir": "Відкрити Вихідну Папку", 67 | "stopped": "Зупинено", 68 | 69 | "new_update": "Нове оновлення!", 70 | "update": "Оновити", 71 | 72 | "donate_title": "Подобається ця безкоштовна програма?", 73 | "donate_description": "Підтримайте розвиток невеликим внеском!", 74 | "donate_thanks": "Дякуємо за Вашу підтримку!", 75 | "donate_button": "Пожертвувати" 76 | } 77 | -------------------------------------------------------------------------------- /GICutscenesUI/web/locales/vi.json: -------------------------------------------------------------------------------- 1 | { 2 | "__root__": { 3 | "inherit": "en" 4 | }, 5 | "home": "Trang Chủ", 6 | "settings": "Cài Đặt", 7 | "select_files": "Chọn File", 8 | "select_files_dots": "Chọn File:", 9 | "version": "phiên bản:", 10 | "about": "Về ứng dụng", 11 | "translations": "Phiên dịch:", 12 | 13 | "change": "Thay đổi", 14 | "reset": "Đặt lại", 15 | "reset_title": "Xoá file cài đặt", 16 | "save": "Lưu", 17 | "save_title": "Lưu cài đặt vào file", 18 | "language": "Ngôn ngữ:", 19 | "output_folder": "Thư mục đầu ra:", 20 | "merge": "Ghép video và âm thanh", 21 | "clear": "Xoá", 22 | "start": "BẮT ĐẦU", 23 | "appearance": "Giao diện", 24 | "theme": "Chủ đề:", 25 | "light": "Sáng", 26 | "dark": "Tối", 27 | 28 | "no_files": "Không có file!", 29 | "select_script_file": "Chọn file script!", 30 | "saved": "Đã lưu!", 31 | "confirm_delete_settings": "Bạn có muốn xoá hết tất cả cài đặt?", 32 | 33 | "using_ffmpeg_tooltip": "Sử dụng FFMPEG", 34 | "track_index_tooltip": "Track index", 35 | 36 | "copy_files": "Sao chép file...", 37 | "run_demux": "Đang giải mã và chuyển đổi...", 38 | "rename_files": "Đang đổi tên file...", 39 | "run_merge": "Ghép video và âm thanh...", 40 | "open_output_dir": "Mở thư mục đầu ra", 41 | 42 | "new_update": "Cập nhật mới!" 43 | } 44 | -------------------------------------------------------------------------------- /GICutscenesUI/web/locales/zh-hans.json: -------------------------------------------------------------------------------- 1 | { 2 | "__root__": { 3 | "inherit": "en" 4 | }, 5 | "home": "主页", 6 | "settings": "设置", 7 | "select_files": "选择文件", 8 | "select_files_dots": "选择文件:", 9 | "version": "版本:", 10 | "about": "关于", 11 | "translations": "翻译人员:", 12 | 13 | "change": "更改", 14 | "reset": "重置", 15 | "reset_title": "清空设置文件", 16 | "save": "保存", 17 | "save_title": "导出设置到文件", 18 | "language": "语言:", 19 | "output_folder": "输出文件夹:", 20 | "clear": "清空", 21 | "start": "开始", 22 | "stop": "停止", 23 | "appearance": "外观", 24 | "theme": "主题:", 25 | "light": "浅色", 26 | "dark": "深色", 27 | 28 | "merging_area": "合并", 29 | "merge": "合并视频和音频", 30 | "using_ffmpeg_tooltip": "使用 ffmpeg", 31 | "audio_track": "音轨语言:", 32 | "quality": "品质:", 33 | "delete_after_merge": "合并后删除不必要的文件", 34 | 35 | "subtitles_area": "字幕", 36 | "subtitles_checkbox": "添加字幕", 37 | "subtitles_tooltip": "仅在启用合并功能时可用", 38 | "subtitles_provider": "字幕来源:", 39 | "subtitles_provider_local": "本地", 40 | "subtitles_provider_url": "URL", 41 | "subtitles_language": "语言:", 42 | "subtitles_font": "字体:", 43 | "subtitles_font_size": "字号:", 44 | "subtitles_font_default": "默认", 45 | "subtitles_customization_button": "自定义", 46 | "subtitles_customization_header": "自定义字幕", 47 | "subtitles_text_color": "文本颜色:", 48 | "subtitles_outline_color": "轮廓颜色:", 49 | "subtitles_outline_width": "轮廓宽度:", 50 | "subtitles_letter_spacing": "字符间距:", 51 | "subtitles_bold": "粗体", 52 | "subtitles_italic": "斜体", 53 | 54 | "no_files": "无文件!", 55 | "select_script_file": "选择脚本文件!", 56 | "saved": "已保存!", 57 | "confirm_delete_settings": "确定删除所有设置吗?", 58 | 59 | "copy_files": "正在复制文件...", 60 | "run_demux": "正在转换和解密...", 61 | "rename_files": "正在重命名文件...", 62 | "run_merge": "正在合并视频和音频...", 63 | "open_output_dir": "打开输出文件夹", 64 | "stopped": "停止", 65 | 66 | "new_update": "新版本可用!", 67 | "update": "升级", 68 | 69 | "donate_title": "喜欢这个免费程序吗?", 70 | "donate_description": "你可以考虑通过小额捐款来支持开发工作!", 71 | "donate_thanks": "感谢你的支持!", 72 | "donate_button": "捐赠" 73 | } 74 | -------------------------------------------------------------------------------- /GICutscenesUI/web/locales/zh-hant.json: -------------------------------------------------------------------------------- 1 | { 2 | "__root__": { 3 | "inherit": "zh-hans" 4 | }, 5 | "home": "主頁", 6 | "settings": "設置", 7 | "select_files": "選擇文件", 8 | "select_files_dots": "選擇文件:", 9 | "version": "版本:", 10 | "about": "關於", 11 | "translations": "翻譯人員:", 12 | 13 | "change": "更改", 14 | "reset": "重置", 15 | "reset_title": "清空設置文件", 16 | "save": "保存", 17 | "save_title": "導出設置到文件", 18 | "language": "語言:", 19 | "output_folder": "輸出文件夾:", 20 | "merge": "合並視頻和音頻", 21 | "clear": "清空", 22 | "start": "開始", 23 | "appearance": "外觀", 24 | "theme": "主題:", 25 | "light": "淺色", 26 | "dark": "深色", 27 | 28 | "no_files": "無文件!", 29 | "select_script_file": "選擇腳本文件!", 30 | "saved": "已保存!", 31 | "confirm_delete_settings": "確定刪除所有設置嗎?", 32 | 33 | "using_ffmpeg_tooltip": "使用 ffmpeg", 34 | "track_index_tooltip": "音軌列表", 35 | 36 | "copy_files": "正在復製文件...", 37 | "run_demux": "正在轉換和解密...", 38 | "rename_files": "正在重命名文件...", 39 | "run_merge": "正在合並視頻和音頻...", 40 | "open_output_dir": "打開輸出文件夾", 41 | 42 | "new_update": "新的版本可用!" 43 | } 44 | -------------------------------------------------------------------------------- /GICutscenesUI/web/main.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |233 | 234 |
235 |
243 | 246 | 247 | 248 | GitHub 249 | 250 |
251 |GI-cutscenes: | 257 |
|
258 |
Ffmpeg: | 266 |
|
267 |
4 |
5 |
7 | User Interface for Genshin Cutscenes Demuxer 8 |
9 | 13 | 14 | ## Screenshots: 15 |
63 | |
65 | 66 | Donatello 67 | | 68 |
71 | |
73 | 74 | Donation Alerts 75 | | 76 |