vnrecode: add cli parameters for configuration

This commit is contained in:
OleSTEEP 2024-08-29 04:01:41 +03:00
parent e5fa49ad53
commit f240fdca5f
6 changed files with 109 additions and 33 deletions

View file

@ -2,17 +2,19 @@
from .application import Application from .application import Application
from .compress import Compress from .compress import Compress
from .printer import Printer from .printer import Printer
from .params import Params
from .config import Config from .config import Config
from .utils import Utils from .utils import Utils
def init(): def init():
config = Config.setup_config() config = Config.setup_config()
params = Params.setup(config.config, config.args)
printer = Printer(config.args.source) printer = Printer(config.args.source)
utils = Utils(config.config, printer) utils = Utils(params, printer)
compress = Compress(config.config, printer, utils) compress = Compress(params, printer, utils)
Application(config, compress, printer, utils).run() Application(params, config.args, compress, printer, utils).run()
if __name__ == "__main__": if __name__ == "__main__":

View file

@ -7,9 +7,9 @@ import os
class Application: class Application:
def __init__(self, config, compress, printer, utils): def __init__(self, params, args, compress, printer, utils):
self.config = config.config self.params = params
self.args = config.args self.args = args
self.compress = compress.compress self.compress = compress.compress
self.printer = printer self.printer = printer
self.utils = utils self.utils = utils
@ -35,7 +35,7 @@ class Application:
self.printer.info(f'Compressing "{folder.replace(source, os.path.split(source)[-1])}" folder...') self.printer.info(f'Compressing "{folder.replace(source, os.path.split(source)[-1])}" folder...')
output = folder.replace(source, f"{source}_compressed") output = folder.replace(source, f"{source}_compressed")
with ThreadPoolExecutor(max_workers=self.config["FFMPEG"]["Workers"]) as executor: with ThreadPoolExecutor(max_workers=self.params.workers) as executor:
futures = [ futures = [
executor.submit(self.compress, folder, file, source, output) executor.submit(self.compress, folder, file, source, output)
for file in files if os.path.isfile(f'{folder}/{file}') for file in files if os.path.isfile(f'{folder}/{file}')

View file

@ -40,13 +40,13 @@ class File:
class Compress: class Compress:
def __init__(self, config, printer, utils): def __init__(self, params, printer, utils):
self.config = config self.params = params
self.printer = printer self.printer = printer
self.utils = utils self.utils = utils
def audio(self, folder, file, target_folder, extension): def audio(self, folder, file, target_folder, extension):
bitrate = self.config['AUDIO']['BitRate'] bitrate = self.params.audio_bitrate
try: try:
(FFmpeg() (FFmpeg()
.input(f'{folder}/{file}') .input(f'{folder}/{file}')
@ -58,16 +58,16 @@ class Compress:
except FFmpegError as e: except FFmpegError as e:
self.utils.add_unprocessed_file(f'{folder}/{file}', f'{target_folder}/{file}') self.utils.add_unprocessed_file(f'{folder}/{file}', f'{target_folder}/{file}')
self.utils.errors += 1 self.utils.errors += 1
if not self.config['FFMPEG']['HideErrors']: if not self.params.hide_errors:
self.printer.error(f"File {file} can't be processed! Error: {e}") self.printer.error(f"File {file} can't be processed! Error: {e}")
self.printer.files(file, os.path.splitext(file)[0], extension, f"{bitrate}") self.printer.files(file, os.path.splitext(file)[0], extension, f"{bitrate}")
return f'{target_folder}/{os.path.splitext(file)[0]}.{extension}' return f'{target_folder}/{os.path.splitext(file)[0]}.{extension}'
def video(self, folder, file, target_folder, extension): def video(self, folder, file, target_folder, extension):
if not self.config['VIDEO']['SkipVideo']: if not self.params.video_skip:
codec = self.config['VIDEO']['Codec'] codec = self.params.video_codec
crf = self.config['VIDEO']['CRF'] crf = self.params.video_crf
try: try:
(FFmpeg() (FFmpeg()
@ -82,7 +82,7 @@ class Compress:
except FFmpegError as e: except FFmpegError as e:
self.utils.add_unprocessed_file(f'{folder}/{file}', f'{target_folder}/{file}') self.utils.add_unprocessed_file(f'{folder}/{file}', f'{target_folder}/{file}')
self.utils.errors += 1 self.utils.errors += 1
if not self.config['FFMPEG']['HideErrors']: if not self.params.hide_errors:
self.printer.error(f"File {file} can't be processed! Error: {e}") self.printer.error(f"File {file} can't be processed! Error: {e}")
else: else:
self.utils.add_unprocessed_file(f'{folder}/{file}', f'{target_folder}/{file}') self.utils.add_unprocessed_file(f'{folder}/{file}', f'{target_folder}/{file}')
@ -90,20 +90,20 @@ class Compress:
def image(self, folder, file, target_folder, extension): def image(self, folder, file, target_folder, extension):
quality = self.config['IMAGE']['Quality'] quality = self.params.image_quality
try: try:
image = Image.open(f'{folder}/{file}') image = Image.open(f'{folder}/{file}')
if (extension == "jpg" or extension == "jpeg" or if (extension == "jpg" or extension == "jpeg" or
(extension == "webp" and not self.config['FFMPEG']['WebpRGBA'])): (extension == "webp" and not self.params.webp_rgba)):
if File.has_transparency(image): if File.has_transparency(image):
self.printer.warning(f"{file} has transparency. Changing to fallback...") self.printer.warning(f"{file} has transparency. Changing to fallback...")
extension = self.config['IMAGE']['FallBackExtension'] extension = self.params.image_fall_ext
if File.has_transparency(image): if File.has_transparency(image):
image.convert('RGBA') image.convert('RGBA')
res_downscale = self.config['IMAGE']['ResDownScale'] res_downscale = self.params.image_downscale
if res_downscale != 1: if res_downscale != 1:
width, height = image.size width, height = image.size
new_size = (int(width / res_downscale), int(height / res_downscale)) new_size = (int(width / res_downscale), int(height / res_downscale))
@ -111,20 +111,20 @@ class Compress:
image.save(self.utils.check_duplicates(f"{target_folder}/{os.path.splitext(file)[0]}.{extension}"), image.save(self.utils.check_duplicates(f"{target_folder}/{os.path.splitext(file)[0]}.{extension}"),
optimize=True, optimize=True,
lossless=self.config['IMAGE']['Lossless'], lossless=self.params.image_lossless,
quality=quality, quality=quality,
minimize_size=True) minimize_size=True)
self.printer.files(file, os.path.splitext(file)[0], extension, f"{quality}%") self.printer.files(file, os.path.splitext(file)[0], extension, f"{quality}%")
except Exception as e: except Exception as e:
self.utils.add_unprocessed_file(f'{folder}/{file}', f'{target_folder}/{file}') self.utils.add_unprocessed_file(f'{folder}/{file}', f'{target_folder}/{file}')
self.utils.errors += 1 self.utils.errors += 1
if not self.config['FFMPEG']['HideErrors']: if not self.params.hide_errors:
self.printer.error(f"File {file} can't be processed! Error: {e}") self.printer.error(f"File {file} can't be processed! Error: {e}")
return f'{target_folder}/{os.path.splitext(file)[0]}.{extension}' return f'{target_folder}/{os.path.splitext(file)[0]}.{extension}'
def unknown(self, folder, file, target_folder): def unknown(self, folder, file, target_folder):
if self.config["FFMPEG"]["ForceCompress"]: if self.params.force_compress:
self.printer.unknown_file(file) self.printer.unknown_file(file)
try: try:
(FFmpeg() (FFmpeg()
@ -135,7 +135,7 @@ class Compress:
except FFmpegError as e: except FFmpegError as e:
self.utils.add_unprocessed_file(f'{folder}/{file}', f'{target_folder}/{file}') self.utils.add_unprocessed_file(f'{folder}/{file}', f'{target_folder}/{file}')
self.utils.errors += 1 self.utils.errors += 1
if not self.config['FFMPEG']['HideErrors']: if not self.params.hide_errors:
self.printer.error(f"File {file} can't be processed! Error: {e}") self.printer.error(f"File {file} can't be processed! Error: {e}")
else: else:
self.utils.add_unprocessed_file(f'{folder}/{file}', f'{target_folder}/{file}') self.utils.add_unprocessed_file(f'{folder}/{file}', f'{target_folder}/{file}')
@ -145,15 +145,15 @@ class Compress:
def compress(self, _dir, filename, source, output): def compress(self, _dir, filename, source, output):
match File.get_type(filename): match File.get_type(filename):
case "audio": case "audio":
out_file = self.audio(_dir, filename, output, self.config['AUDIO']['Extension']) out_file = self.audio(_dir, filename, output, self.params.audio_ext)
case "image": case "image":
out_file = self.image(_dir, filename, output, self.config['IMAGE']['Extension']) out_file = self.image(_dir, filename, output, self.params.image_ext)
case "video": case "video":
out_file = self.video(_dir, filename, output, self.config['VIDEO']['Extension']) out_file = self.video(_dir, filename, output, self.params.video_ext)
case "unknown": case "unknown":
out_file = self.unknown(_dir, filename, output) out_file = self.unknown(_dir, filename, output)
if self.config['FFMPEG']['MimicMode']: if self.params.mimic_mode:
try: try:
os.rename(out_file, f'{_dir}/{filename}'.replace(source, f"{source}_compressed")) os.rename(out_file, f'{_dir}/{filename}'.replace(source, f"{source}_compressed"))
except FileNotFoundError: except FileNotFoundError:

View file

@ -1,8 +1,9 @@
import os.path
from argparse import Namespace, ArgumentParser from argparse import Namespace, ArgumentParser
from dataclasses import dataclass from dataclasses import dataclass
from sysconfig import get_path
from typing import Any from typing import Any
import tomllib import tomllib
import os
@dataclass @dataclass
@ -13,11 +14,30 @@ class Config:
@classmethod @classmethod
def setup_config(cls): def setup_config(cls):
default_config = os.path.join(get_path('purelib'), "vnrecode", "vnrecode.toml")
parser = ArgumentParser(prog="vnrecode", parser = ArgumentParser(prog="vnrecode",
description="Python utility to compress Visual Novel Resources" description="Python utility to compress Visual Novel Resources"
) )
parser.add_argument("source") parser.add_argument("source", help="SourceDir")
parser.add_argument("-c", "--config", default="vnrecode.toml") parser.add_argument("-c", "--config", default=default_config, help="ConfigFile")
parser.add_argument("-u", action="store_true", help="CopyUnprocessed")
parser.add_argument("-f", "--force", action="store_true", help="ForceCompress")
parser.add_argument("-m", "--mimic", action="store_true", help="MimicMode")
parser.add_argument("-s", "--silent", action="store_true", help="HideErrors")
parser.add_argument("--webprgba", action="store_true", help="WebpRGBA")
parser.add_argument("-j", "--jobs", type=int, help="Workers")
parser.add_argument("-ae", "--aext", help="Audio Extension")
parser.add_argument("-ab", "--abit", help="Audio Bitrate")
parser.add_argument("-id", "--idown", type=int, help="Image Downscale")
parser.add_argument("-ie", "--iext", help="Image Extension")
parser.add_argument("-ife", "--ifallext", help="Image Fallback Extension")
parser.add_argument("-il", "--ilossless", action="store_true", help="Image Lossless")
parser.add_argument("-iq", "--iquality", help="Image Quality")
parser.add_argument("--vcrf", help="Video CRF")
parser.add_argument("-vs", "--vskip", help="Video Skip")
parser.add_argument("-ve", "--vext", help="Video Extension")
parser.add_argument("-vc", "--vcodec", help="Video Codec")
args = parser.parse_args() args = parser.parse_args()
if os.path.isfile(args.config): if os.path.isfile(args.config):
with open(args.config, "rb") as cfile: with open(args.config, "rb") as cfile:

54
vnrecode/params.py Normal file
View file

@ -0,0 +1,54 @@
from dataclasses import dataclass
from typing import Self
@dataclass
class Params:
copy_unprocessed: bool
force_compress: bool
mimic_mode: bool
hide_errors: bool
webp_rgba: bool
workers: int
audio_ext: str
audio_bitrate: str
image_downscale: int
image_ext: str
image_fall_ext: str
image_lossless: str
image_quality: int
video_crf: int
video_skip: bool
video_ext: str
video_codec: str
@classmethod
def setup(cls, config, args) -> Self:
copy_unprocessed = config["FFMPEG"]["CopyUnprocessed"] if not args.u else args.u
force_compress = config["FFMPEG"]["ForceCompress"] if not args.force else args.force
mimic_mode = config["FFMPEG"]["MimicMode"] if not args.mimic else args.mimic
hide_errors = config["FFMPEG"]["HideErrors"] if not args.silent else args.silent
workers = config["FFMPEG"]["Workers"] if args.jobs is None else args.jobs
webp_rgba = config["FFMPEG"]["WebpRGBA"] if not args.webprgba else args.webprgba
audio_ext = config["AUDIO"]["Extension"] if args.aext is None else args.aext
audio_bitrate = config["AUDIO"]["BitRate"] if args.abit is None else args.abit
image_downscale = config["IMAGE"]["ResDownScale"] if args.idown is None else args.idown
image_ext = config["IMAGE"]["Extension"] if args.iext is None else args.iext
image_fall_ext = config["IMAGE"]["FallBackExtension"] if args.ifallext is None else args.ifallext
image_lossless = config["IMAGE"]["Lossless"] if not args.ilossless else args.ilossless
image_quality = config["IMAGE"]["Quality"] if args.iquality is None else args.iquality
video_crf = config["VIDEO"]["CRF"] if args.vcrf is None else args.vcrf
video_skip = config["VIDEO"]["SkipVideo"] if args.vskip is None else args.vskip
video_ext = config["VIDEO"]["Extension"] if args.vext is None else args.vext
video_codec = config["VIDEO"]["Codec"] if args.vcodec is None else args.vcodec
return cls(
copy_unprocessed, force_compress, mimic_mode, hide_errors, webp_rgba, workers,
audio_ext, audio_bitrate,
image_downscale, image_ext, image_fall_ext, image_lossless, image_quality,
video_crf, video_skip, video_ext, video_codec
)

View file

@ -4,9 +4,9 @@ import os
class Utils: class Utils:
def __init__(self, config, printer): def __init__(self, params, printer):
self.errors = 0 self.errors = 0
self.config = config self.params = params
self.printer = printer self.printer = printer
@staticmethod @staticmethod
@ -56,7 +56,7 @@ class Utils:
self.get_compression(orig_folder, f"{orig_folder}_compressed") self.get_compression(orig_folder, f"{orig_folder}_compressed")
def add_unprocessed_file(self, orig_folder, new_folder): def add_unprocessed_file(self, orig_folder, new_folder):
if self.config['FFMPEG']['CopyUnprocessed']: if self.params.copy_unprocessed:
filename = orig_folder.split("/").pop() filename = orig_folder.split("/").pop()
copyfile(orig_folder, new_folder) copyfile(orig_folder, new_folder)
self.printer.info(f"File {filename} copied to compressed folder.") self.printer.info(f"File {filename} copied to compressed folder.")