diff --git a/FFMpeg-Compressor/README.md b/FFMpeg-Compressor/README.md index f30ceed..2704f0b 100644 --- a/FFMpeg-Compressor/README.md +++ b/FFMpeg-Compressor/README.md @@ -2,7 +2,6 @@ Python utility uses ffmpeg to compress Visual Novel Resources ### How to use -* Download `ffmpeg-comp.toml` and put in next to binary or in to `/etc` folder -* Change the configuration of the utility in `ffmpeg-comp.toml` for yourself -* `ffmpeg-comp {folder}` +* Configure utitlity in `config.toml` +* `python main.py {folder}` * In result you get `{folder-compressed}` near with original `{folder}` diff --git a/FFMpeg-Compressor/config.toml b/FFMpeg-Compressor/config.toml new file mode 100644 index 0000000..2600227 --- /dev/null +++ b/FFMpeg-Compressor/config.toml @@ -0,0 +1,9 @@ +[FFMPEG] +AudioBitRate = "320k" +AudioExt = "mp3" +CompLevel = 20 +ImageExt = "png" +JpegComp = 3 +FFmpegParams = "-hide_banner -loglevel error" +VideoCodec = "libvpx-vp9" +VideoExt = "webm" \ No newline at end of file diff --git a/FFMpeg-Compressor/ffmpeg-comp.toml b/FFMpeg-Compressor/ffmpeg-comp.toml deleted file mode 100644 index 9f117bb..0000000 --- a/FFMpeg-Compressor/ffmpeg-comp.toml +++ /dev/null @@ -1,15 +0,0 @@ -[FFMPEG] -FFmpegParams = "-hide_banner -loglevel error" - -[AUDIO] -Extension = "mp3" -BitRate = "320k" - -[IMAGE] -Extension = "jpg" -CompLevel = 100 -JpegComp = 3 - -[VIDEO] -Extension = "webm" -Codec = "libvpx-vp9" diff --git a/FFMpeg-Compressor/main.py b/FFMpeg-Compressor/main.py index 3a14022..c789839 100755 --- a/FFMpeg-Compressor/main.py +++ b/FFMpeg-Compressor/main.py @@ -3,31 +3,22 @@ from modules import compressor from modules import printer from modules import utils -import shutil import sys import os try: - if sys.argv[1][len(sys.argv[1])-1] == "/": - arg_path = sys.argv[1][:len(sys.argv[1])-1] - else: - arg_path = sys.argv[1] + orig_folder = sys.argv[1] except IndexError: print(utils.help_message()) exit() -orig_folder = arg_path -printer.orig_folder = arg_path +try: + os.mkdir(f"{orig_folder}_compressed") + printer.info(f"Created {orig_folder}_compressed folder") +except OSError: + printer.warning(f"{orig_folder}_compressed already exist!") + pass -printer.bar_init(orig_folder) - -if os.path.exists(f"{orig_folder}_compressed"): - shutil.rmtree(f"{orig_folder}_compressed") -printer.info("Creating folders...") -for folder, folders, files in os.walk(orig_folder): - if not os.path.exists(folder.replace(orig_folder, f"{orig_folder}_compressed")): - os.mkdir(folder.replace(orig_folder, f"{orig_folder}_compressed")) - - printer.info(f"Compressing \"{folder.replace(orig_folder, orig_folder.split('/').pop())}\" folder...") - compressor.compress(orig_folder, folder) +printer.info("Compression started!") +compressor.compress(orig_folder) utils.get_compression_status(orig_folder) diff --git a/FFMpeg-Compressor/modules/compressor.py b/FFMpeg-Compressor/modules/compressor.py index 5f3d959..0e83fee 100644 --- a/FFMpeg-Compressor/modules/compressor.py +++ b/FFMpeg-Compressor/modules/compressor.py @@ -7,19 +7,13 @@ audio_exts = ['.aac', '.flac', '.m4a', '.mp3', '.ogg', '.opus', '.raw', '.wav', image_exts = ['.apng', '.avif', '.jfif', '.pjpeg', '.pjp', '.svg', '.webp', '.jpg', '.jpeg', '.png', '.raw'] video_exts = ['.3gp' '.amv', '.avi', '.gif', '.m4v', '.mkv', '.mov', '.mp4', '.m4v', '.mpeg', '.mpv', '.webm', '.ogv'] -try: - config = tomllib.load(open("ffmpeg-comp.toml", "rb")) -except FileNotFoundError: - try: - config = tomllib.load(open("/etc/ffmpeg-comp.toml", "rb")) - except FileNotFoundError: - printer.error("Config file not found. Please put it next to binary or in to /etc folder.") - exit() +with open("config.toml", "rb") as f: + config = tomllib.load(f) ffmpeg_params = config['FFMPEG']['FFmpegParams'] -req_audio_ext = config['AUDIO']['Extension'] -req_image_ext = config['IMAGE']['Extension'] -req_video_ext = config['VIDEO']['Extension'] +req_audio_ext = config['FFMPEG']['AudioExt'] +req_image_ext = config['FFMPEG']['ImageExt'] +req_video_ext = config['FFMPEG']['VideoExt'] def has_transparency(img): @@ -38,43 +32,42 @@ def has_transparency(img): return False -def compress(root_folder, folder): - target_folder = folder.replace(root_folder, f"{root_folder}_compressed") +def compress(folder): + files = len(os.listdir(folder)) + progress = 0 for file in os.listdir(folder): if os.path.isfile(f'{folder}/{file}'): if os.path.splitext(file)[1] in audio_exts: - bitrate = config['AUDIO']['BitRate'] - printer.files(file, os.path.splitext(file)[0], req_audio_ext, f"{bitrate}bit/s") - os.system(f"ffmpeg -i '{folder}/{file}' {ffmpeg_params} " - f"'{target_folder}/{os.path.splitext(file)[0]}.{req_audio_ext}'") + bitrate = config['FFMPEG']['AudioBitRate'] + printer.files(int((progress / files) * 100), file, os.path.splitext(file)[0], req_audio_ext, f"{bitrate}bit/s") + os.system(f"ffmpeg -i '{folder}/{file}' {ffmpeg_params} '{folder}_compressed/{os.path.splitext(file)[0]}.{req_audio_ext}'") elif os.path.splitext(file)[1] in image_exts: if req_image_ext == "jpg" or req_image_ext == "jpeg": if not has_transparency(Image.open(f'{folder}/{file}')): - jpg_comp = config['IMAGE']['JpegComp'] - printer.files(file, os.path.splitext(file)[0], req_image_ext, f"level {jpg_comp}") - os.system(f"ffmpeg -i '{folder}/{file}' {ffmpeg_params} -q {jpg_comp} " - f"'{target_folder}/{os.path.splitext(file)[0]}.{req_image_ext}'") + jpg_comp = config['FFMPEG']['JpegComp'] + printer.files(int((progress / files) * 100), file, os.path.splitext(file)[0], req_image_ext, f"level {jpg_comp}") + os.system(f"ffmpeg -i '{folder}/{file}' {ffmpeg_params} -q {jpg_comp} '{folder}_compressed/{os.path.splitext(file)[0]}.{req_image_ext}'") else: printer.warning(f"{file} has transparency (.jpg not support it). Skipping...") else: - comp_level = config['IMAGE']['CompLevel'] - printer.files(file, os.path.splitext(file)[0], req_image_ext, f"{comp_level}%") - os.system(f"ffmpeg -i '{folder}/{file}' {ffmpeg_params} -compression_level {comp_level} " - f"'{target_folder}/{os.path.splitext(file)[0]}.{req_image_ext}'") + comp_level = config['FFMPEG']['CompLevel'] + printer.files(int((progress / files) * 100), file, os.path.splitext(file)[0], req_image_ext, f"{comp_level}%") + os.system(f"ffmpeg -i '{folder}/{file}' {ffmpeg_params} -compression_level {comp_level} '{folder}_compressed/{os.path.splitext(file)[0]}.{req_image_ext}'") elif os.path.splitext(file)[1] in video_exts: - codec = config['VIDEO']['Codec'] - printer.files(file, os.path.splitext(file)[0], req_video_ext, codec) - os.system(f"ffmpeg -i '{folder}/{file}' {ffmpeg_params} -vcodec {codec} " - f"'{target_folder}/{os.path.splitext(file)[0]}.{req_video_ext}'") + codec = config['FFMPEG']['VideoCodec'] + printer.files(int((progress / files) * 100), file, os.path.splitext(file)[0], req_video_ext, codec) + os.system(f"ffmpeg -i '{folder}/{file}' {ffmpeg_params} -vcodec {codec} '{folder}_compressed/{os.path.splitext(file)[0]}.{req_video_ext}'") else: printer.warning("File extension not recognized. This may affect the quality of the compression.") - printer.unknown_file(file) - os.system(f"ffmpeg -i '{folder}/{file}' {ffmpeg_params} '{target_folder}/{file}'") + print(f"\r[{int((progress/files) * 100)}%] \033[0;33m{file}\033[0m") + os.system(f"ffmpeg -i '{folder}/{file}' {ffmpeg_params} '{folder}_compressed/{file}'") + + progress += 1 diff --git a/FFMpeg-Compressor/modules/printer.py b/FFMpeg-Compressor/modules/printer.py index f735b01..209f813 100644 --- a/FFMpeg-Compressor/modules/printer.py +++ b/FFMpeg-Compressor/modules/printer.py @@ -1,42 +1,21 @@ import os -from progress.bar import IncrementalBar - - -# Fill whole string with spaces for cleaning progress bar -def clean_str(string): - return string + " " * (os.get_terminal_size().columns - len(string)) def info(string): - print(clean_str(f"\r\033[0;32m[INFO]\033[0m {string}")) + print(f"[INFO] \033[0;32m{string}\033[0m") + + +def files(progress, source, dest, dest_ext, comment): + source_ext = os.path.splitext(source)[1] + source_name= os.path.splitext(source)[0] + + if progress < 10: + progress = f" {progress}" + elif progress < 100: + progress = f" {progress}" + + print(f"[{progress}%] \033[0;32m{source_name}\033[0m{source_ext}\033[0;32m -> {dest}\033[0m.{dest_ext}\033[0;32m ({comment})\033[0m") def warning(string): - print(clean_str(f"\r\033[0;33m[WARNING]\033[0m {string}")) - - -def error(string): - print(clean_str(f"\r\033[0;31m[ERROR]\033[0m {string}")) - - -def bar_init(folder): - file_count = 0 - for folder, folders, file in os.walk(folder): - file_count += len(file) - global bar - bar = IncrementalBar('Compressing', max=file_count, suffix='[%(index)d/%(max)d] (%(percent).1f%%) - ETA: %(eta)ds') - - -def files(source, dest, dest_ext, comment): - - source_ext = os.path.splitext(source)[1] - source_name = os.path.splitext(source)[0] - - print(clean_str(f"\r[COMP] \033[0;32m{source_name}\033[0m{source_ext}\033[0;32m -> {dest}\033[0m.{dest_ext}\033[0;32m ({comment})\033[0m")) - bar.next() - - -def unknown_file(file): - - print(clean_str(f"\r[COMP] \033[0;33m{file}\033[0m")) - bar.next() + print(f"\033[0;33m[WARNING] {string}\033[0m") \ No newline at end of file diff --git a/FFMpeg-Compressor/modules/utils.py b/FFMpeg-Compressor/modules/utils.py index 66f3b80..8cc41e2 100644 --- a/FFMpeg-Compressor/modules/utils.py +++ b/FFMpeg-Compressor/modules/utils.py @@ -1,21 +1,21 @@ from modules import printer +import glob import os def get_dir_size(directory, files): total_size = 0 - for folder, folders, files in os.walk(directory): - for file in files: - if not os.path.islink(f"{folder}/{file}"): - total_size += os.path.getsize(f"{folder}/{file}") + for f in files: + fp = glob.glob(f'{directory}/{f}*')[0] + if not os.path.islink(fp): + total_size += os.path.getsize(fp) return total_size def get_compression(orig, comp): processed_files = [] - for folder, folders, files in os.walk(comp): - for file in files: - processed_files.append(file) + for file in os.listdir(comp): + processed_files.append(os.path.splitext(file)[0]) try: comp = 100 - int((get_dir_size(comp, processed_files) / get_dir_size(orig, processed_files)) * 100) @@ -32,11 +32,13 @@ def get_compression_status(orig_folder): orig_folder_len = 0 comp_folder_len = 0 - for folder, folders, file in os.walk(orig_folder): - orig_folder_len += len(file) + for file in os.listdir(orig_folder): + if os.path.isfile(f'{orig_folder}/{file}'): + orig_folder_len += 1 - for folder, folders, file in os.walk(f'{orig_folder}_compressed'): - comp_folder_len += len(file) + for file in os.listdir(f'{orig_folder}_compressed'): + if os.path.isfile(f'{orig_folder}_compressed/{file}'): + comp_folder_len += 1 if orig_folder_len == comp_folder_len: printer.info("Success!") @@ -47,5 +49,5 @@ def get_compression_status(orig_folder): def help_message(): - text = "Usage: ffmpeg-comp {folder}" + text = "Usage: main.py {folder}" return text diff --git a/FFMpeg-Compressor/requirements.txt b/FFMpeg-Compressor/requirements.txt deleted file mode 100644 index 290af37..0000000 --- a/FFMpeg-Compressor/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -Pillow==9.5.0 -progress==1.6 diff --git a/README.md b/README.md index 127caae..5982f00 100644 --- a/README.md +++ b/README.md @@ -3,5 +3,4 @@ Collection of tools used by administrators from VN Telegram Channel ### Tools * `FFMpeg-Compressor` - Python utility uses ffmpeg to compress Visual Novel Resources -* `RenPy-Android-Unpack` - Simple Python script for unpacking Ren'Py based .apk files for later rebuilding in the Ren'Py SDK -* `RenPy-Unpacker` - Simple .rpy script that will make any RenPy game unpack itself +* `A simple Python script` for unpacking Ren'Py based .apk files for later rebuilding in the Ren'Py SDK diff --git a/RenPy-Android-Unpack/unpack.py b/RenPy-Android-Unpack/unpack.py index 07cc074..6d826a8 100755 --- a/RenPy-Android-Unpack/unpack.py +++ b/RenPy-Android-Unpack/unpack.py @@ -9,15 +9,10 @@ def extract_assets(file): for content in zip_ref.namelist(): if content.split('/')[0] == 'assets': zip_ref.extract(content) - if os.path.splitext(file)[1] == '.apk': - try: - zip_ref.extract('res/mipmap-xxxhdpi-v4/icon_background.png', 'assets') - zip_ref.extract('res/mipmap-xxxhdpi-v4/icon_foreground.png', 'assets') - os.rename('assets/res/mipmap-xxxhdpi-v4/icon_background.png', 'assets/android-icon_background.png') - os.rename('assets/res/mipmap-xxxhdpi-v4/icon_foreground.png', 'assets/android-icon_foreground.png') - except KeyError: - zip_ref.extract('res/drawable/icon.png', 'assets') - os.rename('assets/res/drawable/icon.png', 'assets/icon.png') + zip_ref.extract('res/mipmap-xxxhdpi-v4/icon_background.png', 'assets') + zip_ref.extract('res/mipmap-xxxhdpi-v4/icon_foreground.png', 'assets') + os.rename('assets/res/mipmap-xxxhdpi-v4/icon_background.png', 'assets/android-icon_background.png') + os.rename('assets/res/mipmap-xxxhdpi-v4/icon_foreground.png', 'assets/android-icon_foreground.png') def rename_files(directory): @@ -43,8 +38,7 @@ def rename_dirs(directory): if __name__ == '__main__': for filename in os.listdir(os.getcwd()): - renpy_warn = 0 - if os.path.splitext(filename)[1] == '.apk' or os.path.splitext(filename)[1] == '.obb': + if os.path.splitext(filename)[1] == '.apk': print(f'[INFO] Extracting assets from {filename}... ', end='') extract_assets(filename) print('Done') @@ -53,15 +47,9 @@ if __name__ == '__main__': rename_dirs('assets') print('Done') print('[INFO] Removing unneeded files... ', end='') - try: - shutil.rmtree('assets/renpy') - except FileNotFoundError: - renpy_warn = 1 - if os.path.splitext(filename)[1] == '.apk': - shutil.rmtree('assets/res') + shutil.rmtree('assets/renpy') + shutil.rmtree('assets/res') print('Done') - if renpy_warn: - print("[WARN] File does not contain renpy folder!") print('[INFO] Renaming directory... ', end='') os.rename('assets', f'{os.path.splitext(filename)[0]}') print('Done') diff --git a/RenPy-Unpacker/README.md b/RenPy-Unpacker/README.md deleted file mode 100644 index 41a3c75..0000000 --- a/RenPy-Unpacker/README.md +++ /dev/null @@ -1,8 +0,0 @@ -## RenPy-Unpacker -Simple .rpy script that will make any RenPy game unpack itself - -### How to use -* Put .rpyc from releases page to game's `game` folder -* Open your game and wait until it not be launched -* Unpacked assets will be in `unpack` folder near with game's executable -* Enjoy! diff --git a/RenPy-Unpacker/unpack.rpy b/RenPy-Unpacker/unpack.rpy deleted file mode 100644 index 005e855..0000000 --- a/RenPy-Unpacker/unpack.rpy +++ /dev/null @@ -1,12 +0,0 @@ -init 4 python: - import os - - for asset in renpy.list_files(): - if os.path.splitext(asset)[1] != ".rpa" and asset != "unpack.rpyc": - output = "unpack/game/" + asset - if not os.path.exists(os.path.dirname(output)): - os.makedirs(os.path.dirname(output)) - - out_bytes = open(output, "wb") - out_bytes.write(renpy.file(asset).read()) - out_bytes.close() diff --git a/build.sh b/build.sh deleted file mode 100755 index 3eec12d..0000000 --- a/build.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -mkdir output -mkdir output/bin -nuitka3 --jobs=$(nproc) --output-dir=output --follow-imports --output-filename=output/bin/ffmpeg-comp FFMpeg-Compressor/main.py -cp FFMpeg-Compressor/ffmpeg-comp.toml output/bin/ -nuitka3 --jobs=$(nproc) --output-dir=output --follow-imports --output-filename=output/bin/rendroid-unpack RenPy-Android-Unpack/unpack.py \ No newline at end of file